Linux Serial subsystem development
 help / color / mirror / Atom feed
* [patch v3 3/5] DT nodes for AST2500 DMA UART driver
From: sudheer.v @ 2019-06-25 10:44 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561459476-14268-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

DT node for DMA controller(ast_uart_sdma) doesn't bind to any DMA controller driver.
This is because Software for DMA controller is not based on DMA framework,but is dedicated
and serves only UARTs in AST2500. ast_uart_sdma node is searched by compatible string in the 
driver software.basic use of this node is to provide register base address of DMA controller and DMA irq number(<50>).
IRQ of DMA controller is of crucial importance, which does RX and TX of UART data. 

uart nodes dma_uart1,2...etc binds to the platform driver.
irq numbers <9>,<32>,<33>,<34> in dma_uart nodes install ISRs which are of not much interest in uart data TX/RX .


Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---

changes in v3:
- change logs added

 arch/arm/boot/dts/aspeed-ast2500-evb.dts | 21 +++++++
 arch/arm/boot/dts/aspeed-g5.dtsi         | 71 ++++++++++++++++++++++--
 2 files changed, 88 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/aspeed-ast2500-evb.dts b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
index 5dbb33c10c4f..4da09fbe94df 100644
--- a/arch/arm/boot/dts/aspeed-ast2500-evb.dts
+++ b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
@@ -64,6 +64,27 @@
 	status = "okay";
 };
 
+&ast_uart_sdma {
+	status = "okay";
+};
+
+&dma_uart1 {
+	status = "okay";
+};
+
+&dma_uart2 {
+	status = "okay";
+};
+
+&dma_uart3 {
+	status = "okay";
+};
+
+&dma_uart4 {
+	status = "okay";
+};
+
+
 &mac0 {
 	status = "okay";
 
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 674746513031..fb7b3ed463de 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -23,10 +23,10 @@
 		i2c11 = &i2c11;
 		i2c12 = &i2c12;
 		i2c13 = &i2c13;
-		serial0 = &uart1;
-		serial1 = &uart2;
-		serial2 = &uart3;
-		serial3 = &uart4;
+		serial0 = &dma_uart1;
+		serial1 = &dma_uart2;
+		serial2 = &dma_uart3;
+		serial3 = &dma_uart4;
 		serial4 = &uart5;
 		serial5 = &vuart;
 		peci0 = &peci0;
@@ -497,6 +497,69 @@
 				status = "disabled";
 			};
 
+			ast_uart_sdma: uart_sdma@1e79e000 {
+				compatible = "aspeed,ast-uart-sdma";
+				reg = <0x1e79e000 0x400>;
+				interrupts = <50>;
+				status = "disabled";
+			};
+
+			dma_uart1: dma_uart1@1e783000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e783000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <9>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART1CLK>;
+				dma-channel = <0>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd1_default
+							 &pinctrl_rxd1_default>;
+				status = "disabled";
+			};
+
+			dma_uart2: dma_uart2@1e78d000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e78d000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <32>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART2CLK>;
+				dma-channel = <1>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd2_default
+							 &pinctrl_rxd2_default>;
+				status = "disabled";
+			};
+
+			dma_uart3: dma_uart3@1e78e000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e78e000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <33>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART3CLK>;
+				dma-channel = <2>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd3_default
+							 &pinctrl_rxd3_default>;
+				status = "disabled";
+			};
+
+			dma_uart4: dma_uart4@1e78f000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e78f000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <34>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART4CLK>;
+				dma-channel = <3>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd4_default
+							 &pinctrl_rxd4_default>;
+				status = "disabled";
+			};
+
 			i2c: bus@1e78a000 {
 				compatible = "simple-bus";
 				#address-cells = <1>;
-- 
2.17.1

^ permalink raw reply related

* [patch v3 2/5] build configuration for AST2500 DMA UART driver
From: sudheer.v @ 2019-06-25 10:44 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561459476-14268-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

build config for DMA based UART driver in AST2500.
Total Available  UARTs in AST2500 are 4

Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---

Changes in v3:
- change logs added

drivers/tty/serial/8250/Kconfig  | 35 +++++++++++++++++++++++++++++++-
 drivers/tty/serial/8250/Makefile |  1 +
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 15c2c5463835..c793466a1c47 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -45,7 +45,7 @@ config SERIAL_8250_DEPRECATED_OPTIONS
 	  keep the 8250_core.* options around until they revert the changes
 	  they already did.
 
-	  If 8250 is built as a module, this adds 8250_core alias instead. 
+	  If 8250 is built as a module, this adds 8250_core alias instead.
 
 	  If you did not notice yet and/or you have userspace from pre-3.7, it
 	  is safe (and recommended) to say N here.
@@ -189,6 +189,39 @@ config SERIAL_8250_RUNTIME_UARTS
 	  with the module parameter "nr_uarts", or boot-time parameter
 	  8250.nr_uarts
 
+config  AST_SERIAL_DMA_UART
+        tristate "AST UART driver with DMA"
+        select SERIAL_CORE
+        help
+          UART driver with DMA support for Aspeed BMC AST25XX.
+          this driver supports UARTs in AST2500,AST2600. It uses
+          DMA channel of DMA engines present in these chips.
+          since this dma engine is used only by UARTs it is not
+          added as a separate DMA driver instead added as a layer
+          within UART driver.
+
+
+config AST_NR_DMA_UARTS
+        int "Maximum number of uart dma serial ports"
+        depends on AST_SERIAL_DMA_UART
+        default "4"
+        help
+          Set this to the number of serial ports you want the driver
+          to support.  This includes any ports discovered via ACPI or
+          PCI enumeration and any ports that may be added at run-time
+          via hot-plug, or any ISA multi-port serial cards.
+
+config AST_RUNTIME_DMA_UARTS
+        int "Number of uart dma serial ports to register at runtime"
+        depends on AST_SERIAL_DMA_UART
+        range 0 AST_NR_DMA_UARTS
+        default "4"
+        help
+          Set this to the maximum number of serial ports you want
+          the kernel to register at boot time.  This can be overridden
+          with the module parameter "nr_uarts", or boot-time parameter
+          8250.nr_uarts
+
 config SERIAL_8250_EXTENDED
 	bool "Extended 8250/16550 serial driver options"
 	depends on SERIAL_8250
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 18751bc63a84..54d40e5c6e2a 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS)		+= 8250_lpss.o
 obj-$(CONFIG_SERIAL_8250_MID)		+= 8250_mid.o
 obj-$(CONFIG_SERIAL_8250_MOXA)		+= 8250_moxa.o
 obj-$(CONFIG_SERIAL_8250_PXA)		+= 8250_pxa.o
+obj-$(CONFIG_AST_SERIAL_DMA_UART)	+= 8250_ast2500_uart_dma.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM)	+= 8250_of.o
 
 CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt
-- 
2.17.1

^ permalink raw reply related

* [patch v3 1/5] AST2500 DMA UART driver
From: sudheer.v @ 2019-06-25 10:44 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561459476-14268-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

UART driver for Aspeed's bmc chip AST2500

Design approch:
AST2500 has dedicated Uart DMA controller which has 12 sets of Tx and RX channels
connected to UART controller directly.
Since the DMA controller have dedicated buffers and registers,
there would be little benifit in adding DMA framework overhead.
So the software for DMA controller is included within the UART driver itself.

implementation details:
'struct uart_8250_port' serial port  is populated and registered with 8250_core.
Rx and Tx dma channels are requested from DMA controller software Layer, which
is part of uart driver itself.
Interrupt service routine for DMA controller is the crucial one for Handling all
the tx and rx data. ISRs installed for individual uarts are just dummy,and are helpful 
only to report any spurious interrupts in hardware.


Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---

Changes in v3:
-custom debug replaced by in kerenl dynamic debug: pr_debug 
-change-logs added 

.../tty/serial/8250/8250_ast2500_uart_dma.c   | 1879 +++++++++++++++++
 1 file changed, 1879 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c

diff --git a/drivers/tty/serial/8250/8250_ast2500_uart_dma.c b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
new file mode 100644
index 000000000000..13911a0a745a
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
@@ -0,0 +1,1879 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  DMA UART Driver for ASPEED BMC chip: AST2500
+ *
+ *  Copyright (C) 2019 sudheer Kumar veliseti, Aspeed technology Inc.
+ *  <open.sudheer@gmail.com>
+ *
+ */
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/nmi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "8250.h"
+
+#define DMA_BUFF_SIZE 0x1000      // 4096
+#define SDMA_RX_BUFF_SIZE 0x10000 // 65536
+
+#define SDDMA_RX_FIX 1
+/* enum ast_uart_chan_op
+ * operation codes passed to the DMA code by the user, and also used
+ * to inform the current channel owner of any changes to the system state
+ */
+
+enum ast_uart_chan_op {
+	AST_UART_DMAOP_TRIGGER,
+	AST_UART_DMAOP_STOP,
+	AST_UART_DMAOP_PAUSE,
+};
+
+/* ast_uart_dma_cbfn_t *  * buffer callback routine type */
+typedef void (*ast_uart_dma_cbfn_t)(void *dev_id, u16 len);
+
+struct ast_sdma_info {
+	u8 ch_no;
+	u8 direction;
+	u8 enable;
+	void *priv;
+	char *sdma_virt_addr;
+	dma_addr_t dma_phy_addr;
+	/* cdriver callbacks */
+	ast_uart_dma_cbfn_t callback_fn; /* buffer done callback */
+};
+
+#define AST_UART_SDMA_CH 12
+
+struct ast_sdma_ch {
+	struct ast_sdma_info tx_dma_info[AST_UART_SDMA_CH];
+	struct ast_sdma_info rx_dma_info[AST_UART_SDMA_CH];
+};
+
+struct ast_sdma {
+	void __iomem *reg_base;
+	int dma_irq;
+	struct ast_sdma_ch *dma_ch;
+	struct regmap *map;
+};
+
+
+
+#define UART_TX_SDMA_EN 0x00
+#define UART_RX_SDMA_EN 0x04
+#define UART_SDMA_CONF 0x08
+#define UART_SDMA_TIMER 0x0C
+#define UART_TX_SDMA_REST 0x20
+#define UART_RX_SDMA_REST 0x24
+#define UART_TX_SDMA_IER 0x30
+#define UART_TX_SDMA_ISR 0x34
+#define UART_RX_SDMA_IER 0x38
+#define UART_RX_SDMA_ISR 0x3C
+#define UART_TX_R_POINT(x) (0x40 + (x * 0x20))
+#define UART_TX_W_POINT(x) (0x44 + (x * 0x20))
+#define UART_TX_SDMA_ADDR(x) (0x48 + (x * 0x20))
+#define UART_RX_R_POINT(x) (0x50 + (x * 0x20))
+#define UART_RX_W_POINT(x) (0x54 + (x * 0x20))
+#define UART_RX_SDMA_ADDR(x) (0x58 + (x * 0x20))
+
+/* UART_TX_SDMA_EN-0x00 : UART TX DMA Enable */
+/* UART_RX_SDMA_EN-0x04 : UART RX DMA Enable */
+#define SDMA_CH_EN(x) (0x1 << (x))
+
+/* UART_SDMA_CONF - 0x08 : Misc, Buffer size  */
+#define SDMA_TX_BUFF_SIZE_MASK (0x3)
+#define SDMA_SET_TX_BUFF_SIZE(x) (x)
+#define SDMA_BUFF_SIZE_1KB (0x0)
+#define SDMA_BUFF_SIZE_4KB (0x1)
+#define SDMA_BUFF_SIZE_16KB (0x2)
+#define SDMA_BUFF_SIZE_64KB (0x3)
+#define SDMA_RX_BUFF_SIZE_MASK (0x3 << 2)
+#define SDMA_SET_RX_BUFF_SIZE(x) (x << 2)
+#define SDMA_TIMEOUT_DIS (0x1 << 4)
+
+/* UART_SDMA_TIMER-0x0C :  UART DMA time out timer */
+
+/* UART_TX_SDMA_IER			0x30	*/
+/* UART_TX_SDMA_ISR			0x34	*/
+
+#define UART_SDMA11_INT (1 << 11)
+#define UART_SDMA10_INT (1 << 10)
+#define UART_SDMA9_INT (1 << 9)
+#define UART_SDMA8_INT (1 << 8)
+#define UART_SDMA7_INT (1 << 7)
+#define UART_SDMA6_INT (1 << 6)
+#define UART_SDMA5_INT (1 << 5)
+#define UART_SDMA4_INT (1 << 4)
+#define UART_SDMA3_INT (1 << 3)
+#define UART_SDMA2_INT (1 << 2)
+#define UART_SDMA1_INT (1 << 1)
+#define UART_SDMA0_INT (1 << 0)
+
+
+/*
+ * Configuration:
+ *   share_irqs - whether we pass IRQF_SHARED to request_irq().  This option
+ *                is unsafe when used on edge-triggered interrupts.
+ */
+static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+static unsigned int nr_uarts = CONFIG_AST_RUNTIME_DMA_UARTS;
+
+
+#define PASS_LIMIT 256
+
+#include <asm/serial.h>
+
+#define UART_DMA_NR CONFIG_AST_NR_DMA_UARTS
+
+
+struct ast_uart_priv_data {
+
+	unsigned short line; //index of uart port
+	struct uart_8250_port *up;
+	u8 dma_ch; // dma channel number
+	struct circ_buf rx_dma_buf;
+	struct circ_buf tx_dma_buf;
+	dma_addr_t dma_rx_addr; /* Mapped ADMA descr. table */
+	dma_addr_t dma_tx_addr; /* Mapped ADMA descr. table */
+#ifdef SDDMA_RX_FIX
+	struct tasklet_struct rx_tasklet;
+#else
+	struct timer_list rx_timer;
+#endif
+	struct tasklet_struct tx_tasklet;
+	spinlock_t lock;
+	int tx_done;
+	int tx_count;
+};
+
+
+static inline struct uart_8250_port *
+to_uart_8250_port(struct uart_port *uart) {
+	return container_of(uart, struct uart_8250_port, port);
+}
+
+struct irq_info {
+	spinlock_t lock;
+	struct uart_8250_port *up;
+};
+
+static struct irq_info ast_uart_irq[1];
+static DEFINE_MUTEX(ast_uart_mutex);
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial8250_config uart_config[] = {
+	[PORT_UNKNOWN] = {
+		.name		= "unknown",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_8250] = {
+		.name		= "8250",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16450] = {
+		.name		= "16450",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16550] = {
+		.name		= "16550",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16550A] = {
+		.name		= "16550A",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10
+							| UART_FCR_DMA_SELECT,
+		.flags		= UART_CAP_FIFO,
+	},
+};
+
+/* sane hardware needs no mapping */
+#define map_8250_in_reg(up, offset) (offset)
+#define map_8250_out_reg(up, offset) (offset)
+
+// SDMA - software Layer : (previously was in ast-uart-sdma.c)
+static inline void ast_uart_sdma_write(struct ast_sdma *sdma,
+						u32 val, u32 reg)
+{
+	pr_debug("uart dma write:val:%x,reg:%x\n", val, reg);
+	writel(val, sdma->reg_base + reg);
+}
+
+static inline u32 ast_uart_sdma_read(struct ast_sdma *sdma, u32 reg)
+{
+	return readl(sdma->reg_base + reg);
+}
+
+struct ast_sdma ast_uart_sdma;
+
+int ast_uart_rx_sdma_enqueue(u8 ch, dma_addr_t rx_buff)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("ch = %d, rx buff = %x\n", ch, rx_buff);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, rx_buff, UART_RX_SDMA_ADDR(ch));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_tx_sdma_enqueue(u8 ch, dma_addr_t tx_buff)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("ch = %d, tx buff = %x\n", ch, tx_buff);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, tx_buff, UART_TX_SDMA_ADDR(ch));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_rx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	pr_debug("RX DMA CTRL [ch %d]\n", ch);
+
+	local_irq_save(flags);
+
+	switch (op) {
+	case AST_UART_DMAOP_TRIGGER:
+	pr_debug("Trigger\n");
+	dma_ch->enable = 1;
+#ifdef SDDMA_RX_FIX
+#else
+	ast_uart_set_sdma_time_out(0xffff);
+#endif
+	// set enable
+	ast_uart_sdma_write(sdma,
+			ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) | (0x1 << ch),
+			UART_RX_SDMA_EN);
+	break;
+	case AST_UART_DMAOP_STOP:
+	// disable engine
+	pr_debug("STOP\n");
+	dma_ch->enable = 0;
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_EN);
+	// set reset
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) |
+				  (0x1 << ch),
+			UART_RX_SDMA_REST);
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(ch));
+	ast_uart_sdma_write(sdma, dma_ch->dma_phy_addr, UART_RX_SDMA_ADDR(ch));
+	break;
+	case AST_UART_DMAOP_PAUSE:
+	// disable engine
+	dma_ch->enable = 0;
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_EN);
+	break;
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+int ast_uart_tx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+	pr_debug("TX DMA CTRL [ch %d]\n", ch);
+
+	local_irq_save(flags);
+
+	switch (op) {
+	case AST_UART_DMAOP_TRIGGER:
+	pr_debug("TRIGGER : Enable\n");
+	dma_ch->enable = 1;
+	// set enable
+	ast_uart_sdma_write(sdma,
+			ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) | (0x1 << ch),
+			UART_TX_SDMA_EN);
+	break;
+	case AST_UART_DMAOP_STOP:
+	pr_debug("STOP : DISABLE & RESET\n");
+	dma_ch->enable = 0;
+	// disable engine
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_EN);
+	// set reset
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) |
+				  (0x1 << ch),
+			UART_TX_SDMA_REST);
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(ch));
+	break;
+	case AST_UART_DMAOP_PAUSE:
+	pr_debug("PAUSE : DISABLE\n");
+	dma_ch->enable = 0;
+	// disable engine
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_EN);
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+u32 ast_uart_get_tx_sdma_pt(u8 ch)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	return ast_uart_sdma_read(sdma, UART_TX_R_POINT(ch));
+}
+
+int ast_uart_tx_sdma_update(u8 ch, u16 point)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("TX DMA CTRL [ch %d] point %d\n", ch, point);
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, point, UART_TX_W_POINT(ch));
+	local_irq_restore(flags);
+	return 0;
+}
+
+int ast_uart_tx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+	pr_debug("TX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+	dma_ch->priv = id;
+	dma_ch->callback_fn = rtn;
+
+	// DMA IRQ En
+	ast_uart_sdma_write(sdma,
+		      ast_uart_sdma_read(sdma, UART_TX_SDMA_IER) | (1 << ch),
+		      UART_TX_SDMA_IER);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_rx_sdma_update(u8 ch, u16 point)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("RX DMA CTRL [ch %d] point %x\n", ch, point);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, point, UART_RX_R_POINT(ch));
+	local_irq_restore(flags);
+	return 0;
+}
+
+#ifdef SDDMA_RX_FIX
+char *ast_uart_rx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	pr_debug("RX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return 0;
+	}
+	dma_ch->priv = id;
+
+	dma_ch->callback_fn = rtn;
+
+	// DMA IRQ En
+	ast_uart_sdma_write(sdma,
+		      ast_uart_sdma_read(sdma, UART_RX_SDMA_IER) | (1 << ch),
+		      UART_RX_SDMA_IER);
+
+	local_irq_restore(flags);
+
+	return dma_ch->sdma_virt_addr;
+}
+
+#else
+char *ast_uart_rx_sdma_request(u8 ch, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	pr_debug("RX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+	dma_ch->priv = id;
+
+	local_irq_restore(flags);
+	return dma_ch->sdma_virt_addr;
+}
+#endif
+
+u16 ast_uart_get_rx_sdma_pt(u8 ch)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	return ast_uart_sdma_read(sdma, UART_RX_W_POINT(ch));
+}
+
+void ast_uart_set_sdma_time_out(u16 val)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	ast_uart_sdma_write(sdma, val, UART_SDMA_TIMER);
+}
+
+static inline void ast_sdma_bufffdone(struct ast_sdma_info *sdma_ch)
+{
+	u32 len;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	if (sdma_ch->enable == 0) {
+		pr_debug("sdma Please check ch_no %x %s!!!!!\n",
+			sdma_ch->ch_no, sdma_ch->direction ? "TX" : "RX");
+		if (sdma_ch->direction) {
+			ast_uart_sdma_write(sdma,
+				ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+				& ~(0x1 << sdma_ch->ch_no), UART_TX_SDMA_EN);
+		} else {
+			ast_uart_sdma_write(sdma,
+				ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				~(0x1 << sdma_ch->ch_no), UART_RX_SDMA_EN);
+			ast_uart_rx_sdma_update(sdma_ch->ch_no,
+				ast_uart_get_rx_sdma_pt(sdma_ch->ch_no));
+			pr_debug("OFFSET : UART_RX_SDMA_EN = %x\n ",
+				ast_uart_sdma_read(sdma, UART_RX_SDMA_EN));
+		}
+		return;
+	}
+
+	if (sdma_ch->direction) {
+		len = ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no));
+		pr_debug("tx rp %x , wp %x\n",
+		ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no)),
+		ast_uart_sdma_read(sdma, UART_TX_W_POINT(sdma_ch->ch_no))
+		);
+	} else {
+		pr_debug("rx rp %x , wp %x\n",
+		ast_uart_sdma_read(sdma, UART_RX_R_POINT(sdma_ch->ch_no)),
+		ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no))
+		);
+		len = ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no));
+	}
+
+	pr_debug("<dma dwn>: ch[%d] : %s ,len : %d\n", sdma_ch->ch_no,
+				sdma_ch->direction ? "tx" : "rx", len);
+
+	if (sdma_ch->callback_fn != NULL)
+		(sdma_ch->callback_fn)(sdma_ch->priv, len);
+}
+
+static irqreturn_t ast_uart_sdma_isr(int irq, void *dev_id)
+{
+	struct ast_sdma *sdma = (struct ast_sdma *)dev_id;
+
+	u32 tx_sts = ast_uart_sdma_read(sdma, UART_TX_SDMA_ISR);
+	u32 rx_sts = ast_uart_sdma_read(sdma, UART_RX_SDMA_ISR);
+
+	pr_debug("tx sts : %x, rx sts : %x\n", tx_sts, rx_sts);
+
+	if ((tx_sts == 0) && (rx_sts == 0)) {
+		pr_debug("SDMA IRQ ERROR !!!\n");
+		return IRQ_HANDLED;
+	}
+
+	if (rx_sts & UART_SDMA0_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[0]));
+	} else if (rx_sts & UART_SDMA1_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[1]));
+	} else if (rx_sts & UART_SDMA2_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[2]));
+	} else if (rx_sts & UART_SDMA3_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[3]));
+	} else if (rx_sts & UART_SDMA4_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[4]));
+	} else if (rx_sts & UART_SDMA5_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[5]));
+	} else if (rx_sts & UART_SDMA6_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[6]));
+	} else if (rx_sts & UART_SDMA7_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[7]));
+	} else if (rx_sts & UART_SDMA8_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[8]));
+	} else if (rx_sts & UART_SDMA9_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[9]));
+	} else if (rx_sts & UART_SDMA10_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[10]));
+	} else if (rx_sts & UART_SDMA11_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[11]));
+	} else {
+
+	}
+
+	if (tx_sts & UART_SDMA0_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[0]));
+	} else if (tx_sts & UART_SDMA1_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[1]));
+	} else if (tx_sts & UART_SDMA2_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[2]));
+	} else if (tx_sts & UART_SDMA3_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[3]));
+	} else if (tx_sts & UART_SDMA4_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[4]));
+	} else if (tx_sts & UART_SDMA5_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[5]));
+	} else if (tx_sts & UART_SDMA6_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[6]));
+	} else if (tx_sts & UART_SDMA7_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[7]));
+	} else if (tx_sts & UART_SDMA8_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[8]));
+	} else if (tx_sts & UART_SDMA9_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[9]));
+	} else if (tx_sts & UART_SDMA10_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[10]));
+	} else if (tx_sts & UART_SDMA11_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[11]));
+	} else {
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ast_uart_sdma_probe(void)
+{
+	int i;
+	struct device_node *node;
+	int ret;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	char *rx_dma_virt_addr;
+	dma_addr_t rx_dma_phy_addr;
+
+	sdma->dma_ch = kzalloc(sizeof(struct ast_sdma_ch), GFP_KERNEL);
+	if (!sdma->dma_ch)
+		return -ENOMEM;
+
+	// sdma memory mapping
+	node = of_find_compatible_node(NULL, NULL, "aspeed,ast-uart-sdma");
+	if (!node)
+		return -ENODEV;
+
+	sdma->reg_base = of_iomap(node, 0);
+	if (IS_ERR(sdma->reg_base))
+		return PTR_ERR(sdma->map);
+	rx_dma_virt_addr = dma_alloc_coherent(NULL,
+	SDMA_RX_BUFF_SIZE * AST_UART_SDMA_CH, &rx_dma_phy_addr, GFP_KERNEL);
+
+	if (!rx_dma_virt_addr) {
+		pr_debug("rx_dma_virt_addr Err:dma alloc Failed\n");
+		return -ENOMEM;
+	}
+	for (i = 0; i < AST_UART_SDMA_CH; i++) {
+		// TX ------------------------
+		sdma->dma_ch->tx_dma_info[i].enable = 0;
+		sdma->dma_ch->tx_dma_info[i].ch_no = i;
+		sdma->dma_ch->tx_dma_info[i].direction = 1;
+		ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(i));
+		// RX ------------------------
+		sdma->dma_ch->rx_dma_info[i].enable = 0;
+		sdma->dma_ch->rx_dma_info[i].ch_no = i;
+		sdma->dma_ch->rx_dma_info[i].direction = 0;
+		sdma->dma_ch->rx_dma_info[i].sdma_virt_addr =
+		rx_dma_virt_addr + (SDMA_RX_BUFF_SIZE * i);
+		sdma->dma_ch->rx_dma_info[i].dma_phy_addr =
+		rx_dma_phy_addr + (SDMA_RX_BUFF_SIZE * i);
+		ast_uart_sdma_write(sdma,
+			sdma->dma_ch->rx_dma_info[i].dma_phy_addr,
+			UART_RX_SDMA_ADDR(i));
+		ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(i));
+	}
+
+	ast_uart_sdma_write(sdma, 0xffffffff, UART_TX_SDMA_REST);
+	ast_uart_sdma_write(sdma, 0x0, UART_TX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0xffffffff, UART_RX_SDMA_REST);
+	ast_uart_sdma_write(sdma, 0x0, UART_RX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_EN);
+	ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_EN);
+
+#ifdef SDDMA_RX_FIX
+	ast_uart_sdma_write(sdma, 0x200, UART_SDMA_TIMER);
+#else
+	ast_uart_sdma_write(sdma, 0xffff, UART_SDMA_TIMER);
+#endif
+
+	// TX
+	ast_uart_sdma_write(sdma, 0xfff, UART_TX_SDMA_ISR);
+	ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_IER);
+
+	// RX
+	ast_uart_sdma_write(sdma, 0xfff, UART_RX_SDMA_ISR);
+	ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_IER);
+
+	sdma->dma_irq = of_irq_get(node, 0);
+	ret = request_irq(sdma->dma_irq, ast_uart_sdma_isr, 0,
+						"sdma-intr", sdma);
+	if (ret) {
+		pr_debug("Unable to get UART SDMA IRQ %x\n", ret);
+		return -ENODEV;
+	}
+
+	ast_uart_sdma_write(sdma, SDMA_SET_TX_BUFF_SIZE(SDMA_BUFF_SIZE_4KB) |
+				SDMA_SET_RX_BUFF_SIZE(SDMA_BUFF_SIZE_64KB),
+		      UART_SDMA_CONF);
+	return 0;
+}
+
+// END of SDMA Layer
+
+// UART Driver Layer
+
+static unsigned int ast_serial_in(struct uart_8250_port *up, int offset)
+{
+	offset = map_8250_in_reg(up, offset) << up->port.regshift;
+	return readb(up->port.membase + offset);
+}
+
+static void ast_serial_out(struct uart_8250_port  *up, int offset, int value)
+{
+	/* Save the offset before it's remapped */
+	offset = map_8250_out_reg(up, offset) << up->port.regshift;
+	writeb(value, up->port.membase + offset);
+}
+
+/*
+ * We used to support using pause I/O for certain machines.  We
+ * haven't supported this for a while, but just in case it's badly
+ * needed for certain old 386 machines, I've left these #define's
+ * in....
+ */
+#define serial_inp(up, offset) ast_serial_in(up, offset)
+#define serial_outp(up, offset, value) ast_serial_out(up, offset, value)
+
+/* Uart divisor latch read */
+static inline int _serial_dl_read(struct uart_8250_port *up)
+{
+	return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static inline void _serial_dl_write(struct uart_8250_port *up, int value)
+{
+	serial_outp(up, UART_DLL, value & 0xff);
+	serial_outp(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#define serial_dl_read(up) _serial_dl_read(up)
+#define serial_dl_write(up, value) _serial_dl_write(up, value)
+
+static void ast_uart_tx_sdma_tasklet_func(unsigned long data)
+{
+
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct uart_8250_port *up = priv->up;
+	struct circ_buf *xmit = NULL;
+	u32 tx_pt;
+
+
+	if (!up)
+		return;
+	xmit = &up->port.state->xmit;
+	spin_lock(&up->port.lock);
+	priv->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	dma_sync_single_for_device(up->port.dev, priv->dma_tx_addr,
+					UART_XMIT_SIZE, DMA_TO_DEVICE);
+	tx_pt = ast_uart_get_tx_sdma_pt(priv->dma_ch);
+
+	if (tx_pt > xmit->head)	{
+		if ((tx_pt & 0xfffc) == 0)
+			ast_uart_tx_sdma_update(priv->dma_ch, 0xffff);
+		else
+			ast_uart_tx_sdma_update(priv->dma_ch, 0);
+	} else {
+		ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
+	}
+	ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
+	spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_tx_buffdone(void *dev_id, u16 len)
+{
+
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
+	struct uart_8250_port *up = priv->up;
+	struct circ_buf *xmit;
+
+	if (!up)
+		return;
+	xmit = &(up->port.state->xmit);
+
+	pr_debug("line[%d] : tx len = % d\n", priv->line, len);
+	spin_lock(&up->port.lock);
+	xmit->tail = len;
+	pr_debug(" line[%d], xmit->head = %d, xmit->tail = % d\n",
+			priv->line, xmit->head, xmit->tail);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (xmit->head != xmit->tail)
+		tasklet_schedule(&priv->tx_tasklet);
+
+	spin_unlock(&up->port.lock);
+}
+
+#ifdef SDDMA_RX_FIX
+static void ast_uart_rx_sdma_tasklet_func(unsigned long data)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct circ_buf *rx_ring = &priv->rx_dma_buf;
+	struct tty_port *ttyport;
+	int count;
+	int copy = 0;
+	struct uart_8250_port *up = priv->up;
+
+	if (!up)
+		return;
+
+	ttyport = &up->port.state->port;
+
+	pr_debug("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+			 up->port.line, rx_ring->head, rx_ring->tail);
+	spin_lock(&up->port.lock);
+	if (rx_ring->head > rx_ring->tail) {
+		count = rx_ring->head - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				rx_ring->buf + rx_ring->tail, count);
+	} else if (rx_ring->head < rx_ring->tail) {
+		count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+					rx_ring->buf + rx_ring->tail, count);
+	} else {
+		count = 0;
+	}
+
+	if (copy != count)
+		pr_debug(" !!!!!!!!ERROR 111\n");
+	if (count) {
+		rx_ring->tail += count;
+		rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+		up->port.icount.rx += count;
+		tty_flip_buffer_push(ttyport);
+		ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
+	}
+	spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_rx_buffdone(void *dev_id, u16 len)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
+	struct circ_buf *rx_ring = &priv->rx_dma_buf;
+	struct uart_8250_port *up = priv->up;
+
+	if (!up)
+		return;
+	pr_debug("line[%d], head = %d,len:%d\n",
+			 priv->line, priv->rx_dma_buf.head, len);
+	spin_lock(&up->port.lock);
+	rx_ring->head = len;
+	spin_unlock(&up->port.lock);
+	tasklet_schedule(&priv->rx_tasklet);
+}
+
+#else
+static void ast_uart_rx_timer_func(unsigned long data)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct uart_8250_port *up = priv->up;
+	struct tty_port *ttyport;
+	struct circ_buf *rx_ring;
+	struct tty_struct *tty;
+	char flag;
+	int count;
+	int copy;
+
+
+	if (!up)
+		return;
+	ttyport = &up->port.state->port;
+	rx_ring = &up->rx_dma_buf;
+	tty = up->port.state->port.tty;
+
+	pr_debug("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+				up->port.line, rx_ring->head, rx_ring->tail);
+	rx_ring->head = ast_uart_get_rx_sdma_pt(priv->dma_ch);
+	del_timer(&up->rx_timer);
+
+	if (rx_ring->head > rx_ring->tail) {
+		ast_uart_set_sdma_time_out(0xffff);
+		count = rx_ring->head - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				 rx_ring->buf + rx_ring->tail, count);
+	} else if (rx_ring->head < rx_ring->tail) {
+		ast_uart_set_sdma_time_out(0xffff);
+		count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				 rx_ring->buf + rx_ring->tail, count);
+	} else {
+		count = 0;
+		// pr_debug("@@--%s-- ch = 0x%x\n", __func__, ch);
+	}
+
+	if (copy != count)
+		pr_debug(" !!!!!!!!ERROR 111\n");
+		rx_ring->tail += count;
+		rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+
+	if (count) {
+		//pr_debug("\n count = % d\n", count);
+		up->port.icount.rx += count;
+		spin_lock(&up->port.lock);
+		tty_flip_buffer_push(ttyport);
+		spin_unlock(&up->port.lock);
+		//pr_debug("update rx_ring->tail % x\n", rx_ring->tail);
+		ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
+		priv->workaround = 1;
+	} else {
+		if (priv->workaround) {
+			priv->workaround++;
+			if (priv->workaround > 1)
+				ast_uart_set_sdma_time_out(0);
+			else
+				ast_uart_set_sdma_time_out(0xffff);
+		}
+	}
+	add_timer(&up->rx_timer);
+}
+#endif
+
+/*
+ * FIFO support.
+ */
+static inline void ast25xx_uart_clear_fifos(struct uart_8250_port *p)
+{
+	serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_outp(p, UART_FCR,
+	      UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_outp(p, UART_FCR, 0);
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.
+ */
+static void autoconfig(struct uart_8250_port *up)
+{
+	unsigned long flags;
+
+	pr_debug("line[%d]\n", up->port.line);
+	if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
+		return;
+
+	pr_debug("ttyDMA%d : autoconf (0x%04lx, 0x%p) : ", up->port.line,
+		 up->port.iobase, up->port.membase);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->capabilities = 0;
+	up->bugs = 0;
+
+	up->port.type = PORT_16550A;
+	up->capabilities |= UART_CAP_FIFO;
+
+	up->port.fifosize = uart_config[up->port.type].fifo_size;
+	up->capabilities = uart_config[up->port.type].flags;
+	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+
+	if (up->port.type == PORT_UNKNOWN)
+		goto out;
+
+	/*
+	 * Reset the UART.
+	 */
+	ast25xx_uart_clear_fifos(up);
+	ast_serial_in(up, UART_RX);
+	serial_outp(up, UART_IER, 0);
+
+out:
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pr_debug("type=%s\n", uart_config[up->port.type].name);
+}
+
+static inline void __stop_tx(struct uart_8250_port *p)
+{
+	if (p->ier & UART_IER_THRI) {
+		p->ier &= ~UART_IER_THRI;
+		ast_serial_out(p, UART_IER, p->ier);
+	}
+}
+
+static void ast25xx_uart_stop_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	pr_debug("line[%d]\n", up->port.line);
+	__stop_tx(up);
+}
+
+static void transmit_chars(struct uart_8250_port *up);
+
+static void ast25xx_uart_start_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	struct ast_uart_priv_data *priv = up->port.private_data;
+
+	pr_debug("line[%d]\n", port->line);
+	tasklet_schedule(&priv->tx_tasklet);
+}
+
+static void ast25xx_uart_stop_rx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	pr_debug("line[%d]\n", port->line);
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void ast25xx_uart_enable_ms(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	pr_debug("line[%d]\n", port->line);
+	up->ier |= UART_IER_MSI;
+	ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void transmit_chars(struct uart_8250_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_outp(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_tx_stopped(&up->port)) {
+		ast25xx_uart_stop_tx(&up->port);
+		return;
+	}
+	if (uart_circ_empty(xmit)) {
+		__stop_tx(up);
+		return;
+	}
+
+	count = up->tx_loadsz;
+	do {
+		ast_serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (uart_circ_empty(xmit))
+		__stop_tx(up);
+}
+
+static unsigned int check_modem_status(struct uart_8250_port *up)
+{
+	unsigned int status = ast_serial_in(up, UART_MSR);
+
+	pr_debug("line[%d]\n", up->port.line);
+	status |= up->msr_saved_flags;
+	up->msr_saved_flags = 0;
+	if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI
+					&& up->port.state != NULL) {
+		if (status & UART_MSR_TERI)
+			up->port.icount.rng++;
+		if (status & UART_MSR_DDSR)
+			up->port.icount.dsr++;
+		if (status & UART_MSR_DDCD)
+			uart_handle_dcd_change(&up->port,
+					status & UART_MSR_DCD);
+		if (status & UART_MSR_DCTS)
+			uart_handle_cts_change(&up->port,
+					status & UART_MSR_CTS);
+
+		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+	}
+
+	return status;
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline void ast25xx_uart_handle_port(struct uart_8250_port *up)
+{
+	unsigned int status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	status = serial_inp(up, UART_LSR);
+
+	pr_debug("status = %x\n", status);
+
+	check_modem_status(up);
+	if (status & UART_LSR_THRE)
+		transmit_chars(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ */
+static irqreturn_t ast_uart_interrupt(int irq, void *dev_id)
+{
+	struct irq_info *i = dev_id;
+	int pass_counter = 0, handled = 0, end = 0;
+
+	pr_debug("(%d)-", irq);
+	spin_lock(&i->lock);
+
+	do {
+		struct uart_8250_port *up;
+		unsigned int iir;
+
+		up = (struct uart_8250_port *)(i->up);
+
+		iir = ast_serial_in(up, UART_IIR);
+		if (!(iir & UART_IIR_NO_INT)) {
+			ast25xx_uart_handle_port(up);
+			handled = 1;
+
+		} else
+			end = 1;
+
+		if (pass_counter++ > PASS_LIMIT) {
+			/* If we hit this, we're dead. */
+			pr_err("ast-uart-dma:too much work for irq%d\n", irq);
+			break;
+		}
+	} while (end);
+
+	spin_unlock(&i->lock);
+
+	pr_debug("-(%d)\n", irq);
+
+	return IRQ_RETVAL(handled);
+}
+
+static unsigned int ast25xx_uart_tx_empty(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned long flags;
+	unsigned int lsr;
+
+	pr_debug("line[%d]\n", up->port.line);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	lsr = ast_serial_in(up, UART_LSR);
+	up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ast25xx_uart_get_mctrl(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned int status;
+	unsigned int ret;
+
+	status = check_modem_status(up);
+
+	ret = 0;
+	if (status & UART_MSR_DCD)
+		ret |= TIOCM_CAR;
+	if (status & UART_MSR_RI)
+		ret |= TIOCM_RNG;
+	if (status & UART_MSR_DSR)
+		ret |= TIOCM_DSR;
+	if (status & UART_MSR_CTS)
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+static void ast25xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned char mcr = 0;
+
+	mctrl = 0;
+
+	if (mctrl & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (mctrl & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (mctrl & TIOCM_OUT1)
+		mcr |= UART_MCR_OUT1;
+	if (mctrl & TIOCM_OUT2)
+		mcr |= UART_MCR_OUT2;
+	if (mctrl & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
+
+	ast_serial_out(up, UART_MCR, mcr);
+
+}
+
+static void ast25xx_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		up->lcr |= UART_LCR_SBC;
+	else
+		up->lcr &= ~UART_LCR_SBC;
+	ast_serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int ast25xx_uart_startup(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	// TX DMA
+	struct circ_buf *xmit = &up->port.state->xmit;
+	struct ast_uart_priv_data *priv = up->port.private_data;
+	unsigned long flags;
+	unsigned char lsr, iir;
+	int retval;
+	int irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+	priv->up = up;
+	pr_debug("line[%d]\n", port->line);
+	up->capabilities = uart_config[up->port.type].flags;
+	up->mcr = 0;
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	ast25xx_uart_clear_fifos(up);
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void)serial_inp(up, UART_LSR);
+	(void)serial_inp(up, UART_RX);
+	(void)serial_inp(up, UART_IIR);
+	(void)serial_inp(up, UART_MSR);
+
+	ast_uart_irq[0].up = up;
+	retval = request_irq(up->port.irq, ast_uart_interrupt, irq_flags,
+		       "ast-uart-dma", ast_uart_irq);
+	if (retval)
+		return retval;
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl |= TIOCM_OUT2;
+
+	ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+
+	/*
+	 * Do a quick test to see if we receive an
+	 * interrupt when we enable the TX irq.
+	 */
+	serial_outp(up, UART_IER, UART_IER_THRI);
+	lsr = ast_serial_in(up, UART_LSR);
+	iir = ast_serial_in(up, UART_IIR);
+	serial_outp(up, UART_IER, 0);
+
+	if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+		if (!(up->bugs & UART_BUG_TXEN)) {
+			up->bugs |= UART_BUG_TXEN;
+			pr_debug("ttyDMA%d-enabling bad tx status\n",
+								 port->line);
+		}
+	} else {
+		up->bugs &= ~UART_BUG_TXEN;
+	}
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Clear the interrupt registers again for luck, and clear the
+	 * saved flags to avoid getting false values from polling
+	 * routines or the previous session.
+	 */
+	serial_inp(up, UART_LSR);
+	serial_inp(up, UART_RX);
+	serial_inp(up, UART_IIR);
+	serial_inp(up, UART_MSR);
+	up->lsr_saved_flags = 0;
+	up->msr_saved_flags = 0;
+
+	// RX DMA
+	priv->rx_dma_buf.head = 0;
+	priv->rx_dma_buf.tail = 0;
+	up->port.icount.rx = 0;
+
+	priv->tx_done = 1;
+	priv->tx_count = 0;
+
+	priv->rx_dma_buf.head = 0;
+	priv->rx_dma_buf.tail = 0;
+#ifdef SDDMA_RX_FIX
+#else
+	priv->workaround = 0;
+#endif
+	// pr_debug("Sending trigger for % x\n", priv->dma_ch);
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
+#ifdef SDDMA_RX_FIX
+#else
+	add_timer(&priv->rx_timer);
+#endif
+	priv->tx_dma_buf.head = 0;
+	priv->tx_dma_buf.tail = 0;
+	priv->tx_dma_buf.buf = xmit->buf;
+
+	pr_debug("head:0x%x tail:0x%x\n", xmit->head, xmit->tail);
+	xmit->head = 0;
+	xmit->tail = 0;
+
+	priv->dma_tx_addr = dma_map_single(port->dev, priv->tx_dma_buf.buf,
+				   UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
+	ast_uart_tx_sdma_enqueue(priv->dma_ch, priv->dma_tx_addr);
+	ast_uart_tx_sdma_update(priv->dma_ch, 0);
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
+	return 0;
+}
+
+static void ast25xx_uart_shutdown(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	struct ast_uart_priv_data *priv = up->port.private_data;
+	unsigned long flags;
+
+	pr_debug("line[%d]\n", port->line);
+	priv->up = NULL;
+
+	up->ier = 0;
+	serial_outp(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+
+	ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	ast_serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+	ast25xx_uart_clear_fifos(up);
+
+	(void)ast_serial_in(up, UART_RX);
+
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
+#ifdef SDDMA_RX_FIX
+#else
+	del_timer_sync(&up->rx_timer);
+#endif
+
+	// Tx buffer will free by serial_core.c
+	free_irq(up->port.irq, ast_uart_irq);
+}
+
+static unsigned int ast25xx_uart_get_divisor(struct uart_port *port,
+					   unsigned int baud)
+{
+	unsigned int quot;
+
+	quot = uart_get_divisor(port, baud);
+
+	return quot;
+}
+
+static void ast25xx_uart_set_termios(struct uart_port *port,
+				   struct ktermios *termios,
+				   struct ktermios *old)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (termios->c_cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+#endif
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	quot = ast25xx_uart_get_divisor(port, baud);
+
+	if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
+		if (baud < 2400)
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+		else
+			fcr = uart_config[up->port.type].fcr;
+	}
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characteres to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= UART_LSR_BI;
+	/*
+	 * If we're ignoring parity and break indicators,
+	 * ignore overruns too (for real raw support).
+	 */
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= UART_LSR_DR;
+
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+
+	ast_serial_out(up, UART_IER, up->ier);
+
+	serial_outp(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+
+	serial_dl_write(up, quot);
+
+	/*
+	 * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
+	 * is written without DLAB set, this mode will be disabled.
+	 */
+
+	serial_outp(up, UART_LCR, cval); /* reset DLAB */
+	up->lcr = cval;                  /* Save LCR */
+	if (fcr & UART_FCR_ENABLE_FIFO) {
+	/* emulated UARTs (Lucent Venus 167x) need two steps */
+		serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	}
+	serial_outp(up, UART_FCR, fcr); /* set fcr */
+	ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	/* Don't rewrite B0 */
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+/*
+ * Resource handling.
+ */
+static int ast25xx_uart_request_std_resource(struct uart_8250_port  *up)
+{
+	unsigned int size = 8 << up->port.regshift;
+	int ret = 0;
+
+	if (!up->port.mapbase)
+		return ret;
+
+	if (!request_mem_region(up->port.mapbase, size, "ast-uart-dma")) {
+		ret = -EBUSY;
+		return ret;
+	}
+
+	if (up->port.flags & UPF_IOREMAP) {
+		up->port.membase = ioremap_nocache(up->port.mapbase, size);
+		if (!up->port.membase) {
+			release_mem_region(up->port.mapbase, size);
+			ret = -ENOMEM;
+			return ret;
+		}
+	}
+	return ret;
+}
+
+static void ast25xx_uart_release_std_resource(struct uart_8250_port *up)
+{
+	unsigned int size = 8 << up->port.regshift;
+
+	if (!up->port.mapbase)
+		return;
+
+	if (up->port.flags & UPF_IOREMAP) {
+		iounmap(up->port.membase);
+		up->port.membase = NULL;
+	}
+
+	release_mem_region(up->port.mapbase, size);
+}
+
+static void ast25xx_uart_release_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_request_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	int ret;
+
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret == 0)
+		ast25xx_uart_release_std_resource(up);
+
+	return ret;
+}
+
+static void ast25xx_uart_config_port(struct uart_port *port, int flags)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	int ret;
+
+	/*
+	 * Find the region that we can probe for.  This in turn
+	 * tells us whether we can probe for the type of port.
+	 */
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret < 0)
+		return;
+
+	if (flags & UART_CONFIG_TYPE)
+		autoconfig(up);
+
+	if (up->port.type == PORT_UNKNOWN)
+		ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_verify_port(struct uart_port *port,
+				  struct serial_struct *ser)
+{
+	return 0;
+}
+
+static const char *ast25xx_uart_type(struct uart_port *port)
+{
+	int type = port->type;
+
+	if (type >= ARRAY_SIZE(uart_config))
+		type = 0;
+	return uart_config[type].name;
+}
+
+
+
+static unsigned int ast25xx_uart_serial_in(struct uart_port *port, int offset)
+{
+	offset = offset << port->regshift;
+	return readb(port->membase + offset);
+
+}
+
+
+static void ast25xx_uart_serial_out(struct uart_port *port,
+						 int offset, int value)
+{
+	offset =  offset << port->regshift;
+	writeb(value, port->membase + offset);
+}
+
+static const struct uart_ops ast25xx_uart_pops = {
+	.tx_empty = ast25xx_uart_tx_empty,
+	.set_mctrl = ast25xx_uart_set_mctrl,
+	.get_mctrl = ast25xx_uart_get_mctrl,
+	.stop_tx = ast25xx_uart_stop_tx,
+	.start_tx = ast25xx_uart_start_tx,
+	.stop_rx = ast25xx_uart_stop_rx,
+	.enable_ms = ast25xx_uart_enable_ms,
+	.break_ctl = ast25xx_uart_break_ctl,
+	.startup = ast25xx_uart_startup,
+	.shutdown = ast25xx_uart_shutdown,
+	.set_termios = ast25xx_uart_set_termios,
+	.type = ast25xx_uart_type,
+	.release_port = ast25xx_uart_release_port,
+	.request_port = ast25xx_uart_request_port,
+	.config_port = ast25xx_uart_config_port,
+	.verify_port = ast25xx_uart_verify_port,
+};
+
+
+
+/*
+ * Register a set of serial devices attached to a platform device.  The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+struct clk *clk;
+
+static int ast25xx_uart_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct ast_uart_priv_data *priv;
+	struct uart_8250_port port_8250;
+	struct uart_8250_port *up;
+	int ret;
+	u32 read, dma_channel = 0;
+	struct resource *res;
+
+	if (UART_XMIT_SIZE > DMA_BUFF_SIZE)
+		pr_debug("UART_XMIT_SIZE > DMA_BUFF_SIZE : Please Check\n");
+
+	priv = (struct ast_uart_priv_data *)devm_kzalloc(&pdev->dev,
+			sizeof(struct ast_uart_priv_data), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	up = &port_8250;
+	memset(up, 0, sizeof(struct uart_8250_port));
+	up->port.flags = UPF_IOREMAP;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "IRQ resource not found");
+		return -ENODEV;
+	}
+	up->port.irq = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Register base not found");
+		return -ENODEV;
+	}
+	up->port.mapbase = res->start;
+
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret) {
+		dev_err(&pdev->dev, "ioremap_nocache Failed");
+		return ret;
+	}
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		dev_err(&pdev->dev, "missing controller clock");
+
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		dev_err(&pdev->dev, "failed to enable DMA UART Clk");
+
+	up->port.uartclk = clk_get_rate(clk);
+
+	if (of_property_read_u32(np, "reg-shift", &read) == 0)
+		up->port.regshift = read;
+	if (of_property_read_u32(np, "dma-channel", &read) == 0) {
+		dma_channel = read;
+		priv->dma_ch = dma_channel;
+	}
+	up->port.iotype = UPIO_MEM;
+	up->port.flags |= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
+	up->port.dev        = &pdev->dev;
+	if (share_irqs)
+		up->port.flags |= UPF_SHARE_IRQ;
+	up->port.fifosize = uart_config[up->port.type].fifo_size;
+	up->port.type = PORT_16550;
+	up->port.iotype = UPIO_MEM;
+	up->port.flags =  UPF_FIXED_TYPE;
+	up->port.startup = ast25xx_uart_startup;
+	up->port.shutdown = ast25xx_uart_shutdown;
+	up->port.set_termios = ast25xx_uart_set_termios;
+	up->port.set_mctrl = ast25xx_uart_set_mctrl;
+	up->port.serial_in = ast25xx_uart_serial_in;
+	up->port.serial_out = ast25xx_uart_serial_out;
+	up->capabilities = uart_config[up->port.type].flags;
+	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+	up->capabilities |= UART_CAP_FIFO;
+
+	up->port.private_data = priv;
+
+	ret = serial8250_register_8250_port(up);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+		"unable to registr port (IO%lx MEM%llx IRQ%d):%d\n",
+			up->port.iobase, (unsigned long long)up->port.mapbase,
+							 up->port.irq, ret);
+		return ret;
+	}
+	priv->line = ret;
+
+	tasklet_init(&priv->tx_tasklet, ast_uart_tx_sdma_tasklet_func,
+							 (unsigned long)priv);
+#ifdef SDDMA_RX_FIX
+	tasklet_init(&priv->rx_tasklet, ast_uart_rx_sdma_tasklet_func,
+							 (unsigned long)priv);
+#else
+	uart->rx_timer.data = (unsigned long)port;
+	uart->rx_timer.expires = jiffies + (HZ);
+	uart->rx_timer.function = ast_uart_rx_timer_func;
+	init_timer(&priv->rx_timer);
+#endif
+
+//DMA request
+#ifdef SDDMA_RX_FIX
+	priv->rx_dma_buf.buf =
+	ast_uart_rx_sdma_request(priv->dma_ch, ast_uart_rx_buffdone,
+			      priv);
+	if (priv->rx_dma_buf.buf < 0) {
+		pr_debug("Error : failed to get rx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+}
+#else
+	priv->rx_dma_buf.buf = ast_uart_rx_sdma_request(
+	priv->dma_ch, priv);
+	if (priv->rx_dma_buf.buf < 0) {
+		pr_debug("Error : failed to get rx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+	}
+#endif
+	if (ast_uart_tx_sdma_request(
+			priv->dma_ch, ast_uart_tx_buffdone, priv) < 0) {
+		pr_debug("Error : failed to get tx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int ast25xx_uart_remove(struct platform_device *pdev)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_unregister_port(priv->line);
+	return 0;
+}
+
+static int ast25xx_uart_suspend(struct platform_device *pdev,
+			      pm_message_t state)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_suspend_port(priv->line);
+	return 0;
+}
+
+static int ast25xx_uart_resume(struct platform_device *pdev)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_resume_port(priv->line);
+	return 0;
+}
+
+static const struct of_device_id ast_serial_dt_ids[] = {
+	{ .compatible = "aspeed,ast-sdma-uart", },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver ast25xx_uart_driver = {
+	.probe = ast25xx_uart_probe,
+	.remove = ast25xx_uart_remove,
+	.suspend = ast25xx_uart_suspend,
+	.resume = ast25xx_uart_resume,
+	.driver = {
+	    .name = "ast-uart-dma",
+	    .of_match_table = of_match_ptr(ast_serial_dt_ids),
+	},
+};
+
+static int __init ast_uart_init(void)
+{
+	int ret;
+
+	if (nr_uarts > UART_DMA_NR)
+		nr_uarts = UART_DMA_NR;
+
+	ret = ast_uart_sdma_probe();
+	if (ret) {
+		pr_debug("ast_uart_sdma_probe Failed ret = %d\n", ret);
+		goto out;
+	}
+	pr_debug("UART driver with DMA%d ports IRQ sharing %sabled\n",
+		nr_uarts, share_irqs ? "en" : "dis");
+
+	spin_lock_init(&ast_uart_irq[0].lock);
+
+	ret = platform_driver_register(&ast25xx_uart_driver);
+	if (ret == 0)
+		goto out;
+
+out:
+	return ret;
+}
+
+static void __exit ast_uart_exit(void)
+{
+
+	platform_driver_unregister(&ast25xx_uart_driver);
+
+}
+module_init(ast_uart_init);
+module_exit(ast_uart_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AST DMA serial driver");
+MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
-- 
2.17.1

^ permalink raw reply related

* [patch v3 0/5] *** DMA based UART driver for AST2500 ***
From: sudheer.v @ 2019-06-25 10:44 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed

From: sudheer veliseti <sudheer.open@gmail.com>

Hi,
AST2500 has dedicated Uart DMA controller which has 12 sets of
Tx and RX channels connected to UART controller directly.
Since the DMA controller have dedicated buffers and registers,
there would be little benifit in adding DMA framework overhead.
So the software for DMA controller is included within the UART driver itself.

Thanks and Regards
Sudheer.V

changes in v3:
- custom debugs replaced with pr_debug in 8250_ast2500_uart_dma.c
- change logs added in patches

sudheer veliseti (5):
  AST2500 DMA UART driver
  build configuration for AST2500 DMA UART driver
  DT nodes for AST2500 DMA UART driver
  defconfig and MAINTAINERS updated for AST2500 DMA UART driver
  Documentation: DT bindings AST2500 DMA UART driver

 .../bindings/serial/ast2500-dma-uart.txt      |   40 +
 MAINTAINERS                                   |   13 +
 arch/arm/boot/dts/aspeed-ast2500-evb.dts      |   21 +
 arch/arm/boot/dts/aspeed-g5.dtsi              |   71 +-
 arch/arm/configs/aspeed_g5_defconfig          |    1 +
 .../tty/serial/8250/8250_ast2500_uart_dma.c   | 1879 +++++++++++++++++
 drivers/tty/serial/8250/Kconfig               |   35 +-
 drivers/tty/serial/8250/Makefile              |    1 +
 8 files changed, 2056 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
 create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c

-- 
2.17.1

^ permalink raw reply

* Re: [patch 0/5] *** DMA based UART driver for AST2500 ***
From: Greg KH @ 2019-06-25  7:27 UTC (permalink / raw)
  To: sudheer v
  Cc: Jiri Slaby, Joel Stanley, Andrew Jeffery, Benjamin Herrenschmidt,
	Rob Herring, Mark Rutland, ShivahShankar Shakarnarayan rao,
	Shiva shankar, Sudheer V, sudheer veliseti, linux-kernel,
	linux-serial, devicetree, linux-aspeed
In-Reply-To: <CAE-5=DTdo4qDUPRw+Giu=bCcpqu7EdLDt5ddDvqLSgGbuqE1Fg@mail.gmail.com>

On Tue, Jun 25, 2019 at 10:30:32AM +0530, sudheer v wrote:
> Hi Greg,
> When i last submitted patches, i have not added change-logs.
> and also used custom debugs instead of kernel dynamic debugs.
> So i have resubmitted the patches , considering you have discarded old set.
> I want to send any changes suggested  from now on wards  with versioning
> V1,V2...so on.
> Is this acceptable for you? or should i submit the patches again as V1 once
> again.?

No, please resend now as a v3 series, with the information that says
what you changed from the previous versions.

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH 1/2 v9] serial: mctrl_gpio: Check if GPIO property exisits before requesting it
From: Stefan Roese @ 2019-06-25  5:27 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: open list:SERIAL DRIVERS, Linux Kernel Mailing List,
	Mika Westerberg, Andy Shevchenko, Yegor Yefremov,
	Greg Kroah-Hartman, Giulio Benetti, Linus Walleij,
	Bartosz Golaszewski, open list:GPIO SUBSYSTEM
In-Reply-To: <CAMuHMdWeX6=SuSPVUB=WaYMsUbrmg5sraM=APeXsqr_Yv6u4AA@mail.gmail.com>

Hi Geert,

On 24.06.19 17:35, Geert Uytterhoeven wrote:
> On Mon, Jun 24, 2019 at 5:29 PM Stefan Roese <sr@denx.de> wrote:
>> On 24.06.19 10:42, Geert Uytterhoeven wrote:
>>> On Thu, Jun 20, 2019 at 8:24 AM Stefan Roese <sr@denx.de> wrote:
>>>> This patch adds a check for the GPIOs property existence, before the
>>>> GPIO is requested. This fixes an issue seen when the 8250 mctrl_gpio
>>>> support is added (2nd patch in this patch series) on x86 platforms using
>>>> ACPI.
>>>>
>>>> Here Mika's comments from 2016-08-09:
>>>>
>>>> "
>>>> I noticed that with v4.8-rc1 serial console of some of our Broxton
>>>> systems does not work properly anymore. I'm able to see output but input
>>>> does not work.
>>>>
>>>> I bisected it down to commit 4ef03d328769eddbfeca1f1c958fdb181a69c341
>>>> ("tty/serial/8250: use mctrl_gpio helpers").
>>>>
>>>> The reason why it fails is that in ACPI we do not have names for GPIOs
>>>> (except when _DSD is used) so we use the "idx" to index into _CRS GPIO
>>>> resources. Now mctrl_gpio_init_noauto() goes through a list of GPIOs
>>>> calling devm_gpiod_get_index_optional() passing "idx" of 0 for each. The
>>>> UART device in Broxton has following (simplified) ACPI description:
>>>>
>>>>       Device (URT4)
>>>>       {
>>>>           ...
>>>>           Name (_CRS, ResourceTemplate () {
>>>>               GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
>>>>                       "\\_SB.GPO0", 0x00, ResourceConsumer)
>>>>               {
>>>>                   0x003A
>>>>               }
>>>>               GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
>>>>                       "\\_SB.GPO0", 0x00, ResourceConsumer)
>>>>               {
>>>>                   0x003D
>>>>               }
>>>>           })
>>>>
>>>> In this case it finds the first GPIO (0x003A which happens to be RX pin
>>>> for that UART), turns it into GPIO which then breaks input for the UART
>>>> device. This also breaks systems with bluetooth connected to UART (those
>>>> typically have some GPIOs in their _CRS).
>>>>
>>>> Any ideas how to fix this?
>>>>
>>>> We cannot just drop the _CRS index lookup fallback because that would
>>>> break many existing machines out there so maybe we can limit this to
>>>> only DT enabled machines. Or alternatively probe if the property first
>>>> exists before trying to acquire the GPIOs (using
>>>> device_property_present()).
>>>> "
>>>>
>>>> This patch implements the fix suggested by Mika in his statement above.
>>>>
>>>> Signed-off-by: Stefan Roese <sr@denx.de>
> 
>>>> --- a/drivers/tty/serial/serial_mctrl_gpio.c
>>>> +++ b/drivers/tty/serial/serial_mctrl_gpio.c
> 
>>>> @@ -116,6 +117,19 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
>>>>
>>>>           for (i = 0; i < UART_GPIO_MAX; i++) {
>>>>                   enum gpiod_flags flags;
>>>> +               char *gpio_str;
>>>> +               bool present;
>>>> +
>>>> +               /* Check if GPIO property exists and continue if not */
>>>> +               gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
>>>> +                                    mctrl_gpios_desc[i].name);
>>>
>>> This will silently break DTBs using "(cts|dsr|dcd|rng|rts|dtr)-gpio" instead
>>> of "(cts|dsr|dcd|rng|rts|dtr)-gpios".
>>
>> Should both options be supported ("cts-gpio" vs "cts-gpios")?
>> Documentation/devicetree/bindings/serial/serial.txt only mentions
>> the "-gpios" variant.
> 
> Well, the "-gpio" variant is deprecated, but still supported by
> devm_gpiod_get_index_optional(), and there are active users in upstream
> DTS files.
> 
> My main objection is (trying to) replicate the matching logic inside
> gpiolib.c, causing subtle semantic differences. And keeping it consistent,
> of course.
> 
> It would be nice if this could be fixed inside acpi_find_gpio(), so
> users don't need to be updated.  There may be other subsystems where
> the difference between DT and ACPI may cause issues, unbeknownst.

Sure, I can fix this. I would prefer to do this in a follow-up patch
though, if nobody objects.

Thanks,
Stefan

^ permalink raw reply

* Re: [PATCH 1/2 v9] serial: mctrl_gpio: Check if GPIO property exisits before requesting it
From: Geert Uytterhoeven @ 2019-06-24 15:35 UTC (permalink / raw)
  To: Stefan Roese
  Cc: open list:SERIAL DRIVERS, Linux Kernel Mailing List,
	Mika Westerberg, Andy Shevchenko, Yegor Yefremov,
	Greg Kroah-Hartman, Giulio Benetti, Linus Walleij,
	Bartosz Golaszewski, open list:GPIO SUBSYSTEM
In-Reply-To: <24bfb52c-6f77-b7cd-7421-9e6e4b0aa7d3@denx.de>

Hi Stefan,

On Mon, Jun 24, 2019 at 5:29 PM Stefan Roese <sr@denx.de> wrote:
> On 24.06.19 10:42, Geert Uytterhoeven wrote:
> > On Thu, Jun 20, 2019 at 8:24 AM Stefan Roese <sr@denx.de> wrote:
> >> This patch adds a check for the GPIOs property existence, before the
> >> GPIO is requested. This fixes an issue seen when the 8250 mctrl_gpio
> >> support is added (2nd patch in this patch series) on x86 platforms using
> >> ACPI.
> >>
> >> Here Mika's comments from 2016-08-09:
> >>
> >> "
> >> I noticed that with v4.8-rc1 serial console of some of our Broxton
> >> systems does not work properly anymore. I'm able to see output but input
> >> does not work.
> >>
> >> I bisected it down to commit 4ef03d328769eddbfeca1f1c958fdb181a69c341
> >> ("tty/serial/8250: use mctrl_gpio helpers").
> >>
> >> The reason why it fails is that in ACPI we do not have names for GPIOs
> >> (except when _DSD is used) so we use the "idx" to index into _CRS GPIO
> >> resources. Now mctrl_gpio_init_noauto() goes through a list of GPIOs
> >> calling devm_gpiod_get_index_optional() passing "idx" of 0 for each. The
> >> UART device in Broxton has following (simplified) ACPI description:
> >>
> >>      Device (URT4)
> >>      {
> >>          ...
> >>          Name (_CRS, ResourceTemplate () {
> >>              GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
> >>                      "\\_SB.GPO0", 0x00, ResourceConsumer)
> >>              {
> >>                  0x003A
> >>              }
> >>              GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
> >>                      "\\_SB.GPO0", 0x00, ResourceConsumer)
> >>              {
> >>                  0x003D
> >>              }
> >>          })
> >>
> >> In this case it finds the first GPIO (0x003A which happens to be RX pin
> >> for that UART), turns it into GPIO which then breaks input for the UART
> >> device. This also breaks systems with bluetooth connected to UART (those
> >> typically have some GPIOs in their _CRS).
> >>
> >> Any ideas how to fix this?
> >>
> >> We cannot just drop the _CRS index lookup fallback because that would
> >> break many existing machines out there so maybe we can limit this to
> >> only DT enabled machines. Or alternatively probe if the property first
> >> exists before trying to acquire the GPIOs (using
> >> device_property_present()).
> >> "
> >>
> >> This patch implements the fix suggested by Mika in his statement above.
> >>
> >> Signed-off-by: Stefan Roese <sr@denx.de>

> >> --- a/drivers/tty/serial/serial_mctrl_gpio.c
> >> +++ b/drivers/tty/serial/serial_mctrl_gpio.c

> >> @@ -116,6 +117,19 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
> >>
> >>          for (i = 0; i < UART_GPIO_MAX; i++) {
> >>                  enum gpiod_flags flags;
> >> +               char *gpio_str;
> >> +               bool present;
> >> +
> >> +               /* Check if GPIO property exists and continue if not */
> >> +               gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
> >> +                                    mctrl_gpios_desc[i].name);
> >
> > This will silently break DTBs using "(cts|dsr|dcd|rng|rts|dtr)-gpio" instead
> > of "(cts|dsr|dcd|rng|rts|dtr)-gpios".
>
> Should both options be supported ("cts-gpio" vs "cts-gpios")?
> Documentation/devicetree/bindings/serial/serial.txt only mentions
> the "-gpios" variant.

Well, the "-gpio" variant is deprecated, but still supported by
devm_gpiod_get_index_optional(), and there are active users in upstream
DTS files.

My main objection is (trying to) replicate the matching logic inside
gpiolib.c, causing subtle semantic differences. And keeping it consistent,
of course.

It would be nice if this could be fixed inside acpi_find_gpio(), so
users don't need to be updated.  There may be other subsystems where
the difference between DT and ACPI may cause issues, unbeknownst.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH 1/2 v9] serial: mctrl_gpio: Check if GPIO property exisits before requesting it
From: Stefan Roese @ 2019-06-24 15:29 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: open list:SERIAL DRIVERS, Linux Kernel Mailing List,
	Mika Westerberg, Andy Shevchenko, Yegor Yefremov,
	Greg Kroah-Hartman, Giulio Benetti, Linus Walleij,
	Bartosz Golaszewski, open list:GPIO SUBSYSTEM
In-Reply-To: <CAMuHMdXMpS_pg9N0qSW=Li0QavAMRG79RJcS4s0w6NTCxv_zzg@mail.gmail.com>

On 24.06.19 10:42, Geert Uytterhoeven wrote:
> CC gpio
> 
> This is now commit d99482673f950817 ("serial: mctrl_gpio: Check if GPIO
> property exisits before requesting it") in tty-next.
> 
> On Thu, Jun 20, 2019 at 8:24 AM Stefan Roese <sr@denx.de> wrote:
>> This patch adds a check for the GPIOs property existence, before the
>> GPIO is requested. This fixes an issue seen when the 8250 mctrl_gpio
>> support is added (2nd patch in this patch series) on x86 platforms using
>> ACPI.
>>
>> Here Mika's comments from 2016-08-09:
>>
>> "
>> I noticed that with v4.8-rc1 serial console of some of our Broxton
>> systems does not work properly anymore. I'm able to see output but input
>> does not work.
>>
>> I bisected it down to commit 4ef03d328769eddbfeca1f1c958fdb181a69c341
>> ("tty/serial/8250: use mctrl_gpio helpers").
>>
>> The reason why it fails is that in ACPI we do not have names for GPIOs
>> (except when _DSD is used) so we use the "idx" to index into _CRS GPIO
>> resources. Now mctrl_gpio_init_noauto() goes through a list of GPIOs
>> calling devm_gpiod_get_index_optional() passing "idx" of 0 for each. The
>> UART device in Broxton has following (simplified) ACPI description:
>>
>>      Device (URT4)
>>      {
>>          ...
>>          Name (_CRS, ResourceTemplate () {
>>              GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
>>                      "\\_SB.GPO0", 0x00, ResourceConsumer)
>>              {
>>                  0x003A
>>              }
>>              GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
>>                      "\\_SB.GPO0", 0x00, ResourceConsumer)
>>              {
>>                  0x003D
>>              }
>>          })
>>
>> In this case it finds the first GPIO (0x003A which happens to be RX pin
>> for that UART), turns it into GPIO which then breaks input for the UART
>> device. This also breaks systems with bluetooth connected to UART (those
>> typically have some GPIOs in their _CRS).
>>
>> Any ideas how to fix this?
>>
>> We cannot just drop the _CRS index lookup fallback because that would
>> break many existing machines out there so maybe we can limit this to
>> only DT enabled machines. Or alternatively probe if the property first
>> exists before trying to acquire the GPIOs (using
>> device_property_present()).
>> "
>>
>> This patch implements the fix suggested by Mika in his statement above.
>>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
>> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Tested-by: Yegor Yefremov <yegorslists@googlemail.com>
>> Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
>> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Cc: Yegor Yefremov <yegorslists@googlemail.com>
>> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>> Cc: Giulio Benetti <giulio.benetti@micronovasrl.com>
>> ---
>> v9:
>> - Rebased on top of "tty-next", patch 2/3 dropped as its already applied
>>
>> v8:
>> - Rebased on top of "tty-next"
>>
>> v7:
>> - Include <linux/property.h> to fix compile breakage on OMAP
>>
>> v6:
>> - No change
>>
>> v5:
>> - Simplified the code a bit (Andy)
>> - Added gpio_str == NULL handling (Andy)
>>
>> v4:
>> - Add missing free() calls (Johan)
>> - Added Mika's reviewed by tag
>> - Added Johan to Cc
>>
>> v3:
>> - No change
>>
>> v2:
>> - Include the problem description and analysis from Mika into the commit
>>    text, as suggested by Greg.
>>
>>   drivers/tty/serial/serial_mctrl_gpio.c | 14 ++++++++++++++
>>   1 file changed, 14 insertions(+)
>>
>> diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
>> index 39ed56214cd3..2b400189be91 100644
>> --- a/drivers/tty/serial/serial_mctrl_gpio.c
>> +++ b/drivers/tty/serial/serial_mctrl_gpio.c
>> @@ -12,6 +12,7 @@
>>   #include <linux/termios.h>
>>   #include <linux/serial_core.h>
>>   #include <linux/module.h>
>> +#include <linux/property.h>
>>
>>   #include "serial_mctrl_gpio.h"
>>
>> @@ -116,6 +117,19 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
>>
>>          for (i = 0; i < UART_GPIO_MAX; i++) {
>>                  enum gpiod_flags flags;
>> +               char *gpio_str;
>> +               bool present;
>> +
>> +               /* Check if GPIO property exists and continue if not */
>> +               gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
>> +                                    mctrl_gpios_desc[i].name);
> 
> This will silently break DTBs using "(cts|dsr|dcd|rng|rts|dtr)-gpio" instead
> of "(cts|dsr|dcd|rng|rts|dtr)-gpios".

Should both options be supported ("cts-gpio" vs "cts-gpios")?
Documentation/devicetree/bindings/serial/serial.txt only mentions
the "-gpios" variant.

Thanks,
Stefan

^ permalink raw reply

* Re: junk character issue on minicom and screen
From: Johan Hovold @ 2019-06-24 14:23 UTC (permalink / raw)
  To: Shyam Saini; +Cc: linux-usb, linux-serial, Kernelnewbies
In-Reply-To: <CAOfkYf4kCTWdjDJSG0=KgZZG7F+HpE+m+RbgLZ=NeczZ5uWPRw@mail.gmail.com>

On Mon, Jun 24, 2019 at 02:57:34PM +0530, Shyam Saini wrote:
> Hello everyone,
> 
> I'm working on Rockchip rk3399 Nanopc t4 board and I'm trying get
> serial output from board using minicom and my board has baudrate
> 1500000.
> 
> I'm using a usb to serial converter. So, when i power on my board
> minicom starts to give junk characters.
> Minicom version:  2.7.1
> USB driver probed : cp210x
> 
> But when I use the same setup with my colleagues laptop it gives
> correct output (kernel version 4.15.0-52-generic ).
> 
> Note that my colleague and i used same setup
> Same usb driver probed
> Same power adapter
> same usb to serial converter wire
> same minicom version.
> same baudrate
> 
> Other thing which i tried is switching kernel version and it turns out
> that one of the older distro kernel on my system (debian 4.9.0-5-amd64
> ) is working fine and it seems like there is something changed in
> newer kernel vesions.
> Here is the quick summary
> 4.19.0-4-amd64    -> not working
> 4.15.0-52-generic -> working
> 4.9.0-5-amd64      -> working
> 5.2.0-rc4+             -> not working

Thanks for the report. There were some changes between 4.15 and 4.19
related to the line speed handling which may have broken something in
your setup.

Do you know which variant of cp210x you have? Can your provide the
output of lsusb -v?

Also if you can run driver with debugging enabled when connecting the
device and setting the line speed, that may give a clue about what is
going on. For example,

	echo module cp210x =p > /sys/kernel/debug/dynamic_debug/control

Does 115200 bps still work by the way?

Johan

^ permalink raw reply

* junk character issue on minicom and screen
From: Shyam Saini @ 2019-06-24  9:27 UTC (permalink / raw)
  To: linux-serial; +Cc: Kernelnewbies

Hello everyone,

I'm working on Rockchip rk3399 Nanopc t4 board and I'm trying get serial output
from board using minicom and my board has baudrate 1500000.

I'm using a usb to serial converter. So, when i power on my board minicom starts
to give junk characters.
Minicom version:  2.7.1
USB driver probed : cp210x

But when I use the same setup with my colleagues laptop it gives correct output
(kernel version 4.15.0-52-generic ).

Note that my colleague and i used same setup
Same usb driver probed
Same power adapter
same usb to serial converter wire
same minicom version.
same baudrate

Other thing which i tried is switching kernel version and it turns out
that one of the older
distro kernel on my system (debian 4.9.0-5-amd64 ) is working fine and
it seems like
there is something changed in newer kernel vesions.
Here is the quick summary
4.19.0-4-amd64    -> not working
4.15.0-52-generic -> working
4.9.0-5-amd64      -> working
5.2.0-rc4+             -> not working

Did anyone else faced the similar problem.
Is there any patch available if it is bug ?
Please let me know.

Thanks a lot,
Shyam

^ permalink raw reply

* Re: [PATCH 1/2 v9] serial: mctrl_gpio: Check if GPIO property exisits before requesting it
From: Geert Uytterhoeven @ 2019-06-24  8:42 UTC (permalink / raw)
  To: Stefan Roese
  Cc: open list:SERIAL DRIVERS, Linux Kernel Mailing List,
	Mika Westerberg, Andy Shevchenko, Yegor Yefremov,
	Greg Kroah-Hartman, Giulio Benetti, Linus Walleij,
	Bartosz Golaszewski, open list:GPIO SUBSYSTEM
In-Reply-To: <20190620062420.11650-1-sr@denx.de>

CC gpio

This is now commit d99482673f950817 ("serial: mctrl_gpio: Check if GPIO
property exisits before requesting it") in tty-next.

On Thu, Jun 20, 2019 at 8:24 AM Stefan Roese <sr@denx.de> wrote:
> This patch adds a check for the GPIOs property existence, before the
> GPIO is requested. This fixes an issue seen when the 8250 mctrl_gpio
> support is added (2nd patch in this patch series) on x86 platforms using
> ACPI.
>
> Here Mika's comments from 2016-08-09:
>
> "
> I noticed that with v4.8-rc1 serial console of some of our Broxton
> systems does not work properly anymore. I'm able to see output but input
> does not work.
>
> I bisected it down to commit 4ef03d328769eddbfeca1f1c958fdb181a69c341
> ("tty/serial/8250: use mctrl_gpio helpers").
>
> The reason why it fails is that in ACPI we do not have names for GPIOs
> (except when _DSD is used) so we use the "idx" to index into _CRS GPIO
> resources. Now mctrl_gpio_init_noauto() goes through a list of GPIOs
> calling devm_gpiod_get_index_optional() passing "idx" of 0 for each. The
> UART device in Broxton has following (simplified) ACPI description:
>
>     Device (URT4)
>     {
>         ...
>         Name (_CRS, ResourceTemplate () {
>             GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
>                     "\\_SB.GPO0", 0x00, ResourceConsumer)
>             {
>                 0x003A
>             }
>             GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
>                     "\\_SB.GPO0", 0x00, ResourceConsumer)
>             {
>                 0x003D
>             }
>         })
>
> In this case it finds the first GPIO (0x003A which happens to be RX pin
> for that UART), turns it into GPIO which then breaks input for the UART
> device. This also breaks systems with bluetooth connected to UART (those
> typically have some GPIOs in their _CRS).
>
> Any ideas how to fix this?
>
> We cannot just drop the _CRS index lookup fallback because that would
> break many existing machines out there so maybe we can limit this to
> only DT enabled machines. Or alternatively probe if the property first
> exists before trying to acquire the GPIOs (using
> device_property_present()).
> "
>
> This patch implements the fix suggested by Mika in his statement above.
>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Tested-by: Yegor Yefremov <yegorslists@googlemail.com>
> Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Yegor Yefremov <yegorslists@googlemail.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Giulio Benetti <giulio.benetti@micronovasrl.com>
> ---
> v9:
> - Rebased on top of "tty-next", patch 2/3 dropped as its already applied
>
> v8:
> - Rebased on top of "tty-next"
>
> v7:
> - Include <linux/property.h> to fix compile breakage on OMAP
>
> v6:
> - No change
>
> v5:
> - Simplified the code a bit (Andy)
> - Added gpio_str == NULL handling (Andy)
>
> v4:
> - Add missing free() calls (Johan)
> - Added Mika's reviewed by tag
> - Added Johan to Cc
>
> v3:
> - No change
>
> v2:
> - Include the problem description and analysis from Mika into the commit
>   text, as suggested by Greg.
>
>  drivers/tty/serial/serial_mctrl_gpio.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>
> diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
> index 39ed56214cd3..2b400189be91 100644
> --- a/drivers/tty/serial/serial_mctrl_gpio.c
> +++ b/drivers/tty/serial/serial_mctrl_gpio.c
> @@ -12,6 +12,7 @@
>  #include <linux/termios.h>
>  #include <linux/serial_core.h>
>  #include <linux/module.h>
> +#include <linux/property.h>
>
>  #include "serial_mctrl_gpio.h"
>
> @@ -116,6 +117,19 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
>
>         for (i = 0; i < UART_GPIO_MAX; i++) {
>                 enum gpiod_flags flags;
> +               char *gpio_str;
> +               bool present;
> +
> +               /* Check if GPIO property exists and continue if not */
> +               gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
> +                                    mctrl_gpios_desc[i].name);

This will silently break DTBs using "(cts|dsr|dcd|rng|rts|dtr)-gpio" instead
of "(cts|dsr|dcd|rng|rts|dtr)-gpios".

> +               if (!gpio_str)
> +                       continue;
> +
> +               present = device_property_present(dev, gpio_str);
> +               kfree(gpio_str);
> +               if (!present)
> +                       continue;
>
>                 if (mctrl_gpios_desc[i].dir_out)
>                         flags = GPIOD_OUT_LOW;

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH V34 18/29] Lock down TIOCSSERIAL
From: Kees Cook @ 2019-06-23  0:01 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: jmorris, linux-security-module, linux-kernel, linux-api,
	David Howells, Greg Kroah-Hartman, Matthew Garrett, Jiri Slaby,
	linux-serial
In-Reply-To: <20190622000358.19895-19-matthewgarrett@google.com>

On Fri, Jun 21, 2019 at 05:03:47PM -0700, Matthew Garrett wrote:
> From: David Howells <dhowells@redhat.com>
> 
> Lock down TIOCSSERIAL as that can be used to change the ioport and irq
> settings on a serial port.  This only appears to be an issue for the serial
> drivers that use the core serial code.  All other drivers seem to either
> ignore attempts to change port/irq or give an error.
> 
> Reported-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: David Howells <dhowells@redhat.com>
> Signed-off-by: Matthew Garrett <mjg59@google.com>
> cc: Jiri Slaby <jslaby@suse.com>
> Cc: linux-serial@vger.kernel.org
> ---
>  drivers/tty/serial/serial_core.c | 5 +++++
>  include/linux/security.h         | 1 +
>  security/lockdown/lockdown.c     | 1 +
>  3 files changed, 7 insertions(+)
> 
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index 351843f847c0..a84f231a5df4 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -22,6 +22,7 @@
>  #include <linux/serial_core.h>
>  #include <linux/delay.h>
>  #include <linux/mutex.h>
> +#include <linux/security.h>
>  
>  #include <linux/irq.h>
>  #include <linux/uaccess.h>
> @@ -852,6 +853,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
>  	new_flags = (__force upf_t)new_info->flags;
>  	old_custom_divisor = uport->custom_divisor;
>  
> +	retval = security_locked_down(LOCKDOWN_TIOCSSERIAL);
> +	if (retval && (change_port || change_irq))
> +		goto exit;
> +
>  	if (!capable(CAP_SYS_ADMIN)) {
>  		retval = -EPERM;
>  		if (change_irq || change_port ||

This should be moved after the capable test. With that fixed:

Reviewed-by: Kees Cook <keescook@chromium.org>

-Kees

> diff --git a/include/linux/security.h b/include/linux/security.h
> index 03c125b277ca..61e3f4a62d16 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -91,6 +91,7 @@ enum lockdown_reason {
>  	LOCKDOWN_MSR,
>  	LOCKDOWN_ACPI_TABLES,
>  	LOCKDOWN_PCMCIA_CIS,
> +	LOCKDOWN_TIOCSSERIAL,
>  	LOCKDOWN_INTEGRITY_MAX,
>  	LOCKDOWN_CONFIDENTIALITY_MAX,
>  };
> diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
> index 7be3e8fb5847..c89046dc2155 100644
> --- a/security/lockdown/lockdown.c
> +++ b/security/lockdown/lockdown.c
> @@ -27,6 +27,7 @@ static char *lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
>  	[LOCKDOWN_MSR] = "raw MSR access",
>  	[LOCKDOWN_ACPI_TABLES] = "modified ACPI tables",
>  	[LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",
> +	[LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
>  	[LOCKDOWN_INTEGRITY_MAX] = "integrity",
>  	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
>  };
> -- 
> 2.22.0.410.gd8fdbe21b5-goog
> 

-- 
Kees Cook

^ permalink raw reply

* [PATCH V34 18/29] Lock down TIOCSSERIAL
From: Matthew Garrett @ 2019-06-22  0:03 UTC (permalink / raw)
  To: jmorris
  Cc: linux-security-module, linux-kernel, linux-api, David Howells,
	Greg Kroah-Hartman, Matthew Garrett, Jiri Slaby, linux-serial
In-Reply-To: <20190622000358.19895-1-matthewgarrett@google.com>

From: David Howells <dhowells@redhat.com>

Lock down TIOCSSERIAL as that can be used to change the ioport and irq
settings on a serial port.  This only appears to be an issue for the serial
drivers that use the core serial code.  All other drivers seem to either
ignore attempts to change port/irq or give an error.

Reported-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Matthew Garrett <mjg59@google.com>
cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
---
 drivers/tty/serial/serial_core.c | 5 +++++
 include/linux/security.h         | 1 +
 security/lockdown/lockdown.c     | 1 +
 3 files changed, 7 insertions(+)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 351843f847c0..a84f231a5df4 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -22,6 +22,7 @@
 #include <linux/serial_core.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
+#include <linux/security.h>
 
 #include <linux/irq.h>
 #include <linux/uaccess.h>
@@ -852,6 +853,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
 	new_flags = (__force upf_t)new_info->flags;
 	old_custom_divisor = uport->custom_divisor;
 
+	retval = security_locked_down(LOCKDOWN_TIOCSSERIAL);
+	if (retval && (change_port || change_irq))
+		goto exit;
+
 	if (!capable(CAP_SYS_ADMIN)) {
 		retval = -EPERM;
 		if (change_irq || change_port ||
diff --git a/include/linux/security.h b/include/linux/security.h
index 03c125b277ca..61e3f4a62d16 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -91,6 +91,7 @@ enum lockdown_reason {
 	LOCKDOWN_MSR,
 	LOCKDOWN_ACPI_TABLES,
 	LOCKDOWN_PCMCIA_CIS,
+	LOCKDOWN_TIOCSSERIAL,
 	LOCKDOWN_INTEGRITY_MAX,
 	LOCKDOWN_CONFIDENTIALITY_MAX,
 };
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index 7be3e8fb5847..c89046dc2155 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -27,6 +27,7 @@ static char *lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
 	[LOCKDOWN_MSR] = "raw MSR access",
 	[LOCKDOWN_ACPI_TABLES] = "modified ACPI tables",
 	[LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",
+	[LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
 	[LOCKDOWN_INTEGRITY_MAX] = "integrity",
 	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
 };
-- 
2.22.0.410.gd8fdbe21b5-goog

^ permalink raw reply related

* Re: [PATCH v11 0/6] Add basic node support for Mediatek MT8183 SoC
From: Matthias Brugger @ 2019-06-21 15:53 UTC (permalink / raw)
  To: Erin Lo, Rob Herring, Mark Rutland
  Cc: devicetree, srv_heupstream, linux-kernel, linux-serial,
	linux-mediatek, linux-arm-kernel, mars.cheng, eddie.huang
In-Reply-To: <1558947887-31084-1-git-send-email-erin.lo@mediatek.com>



On 27/05/2019 11:04, Erin Lo wrote:
> MT8183 is a SoC based on 64bit ARMv8 architecture.
> It contains 4 CA53 and 4 CA73 cores.
> MT8183 share many HW IP with MT65xx series.
> This patchset was tested on MT8183 evaluation board and use correct clock to shell.
> 
> Based on v5.2-rc1
> 

Pushed all to v5.2-next/dts64

Please send patches to update the bindings of auxadc and efuse. They use the
fallback right now, but we should make sure to have the binding in sync.

Regards,
Matthias

> Change in v11:
> New add spi node, efuse node, pinctrl node, auxadc node, and capacity-dmips-mhz field
> 
> Change in v10:
> Add the L2 cache node to prevent warning on unable to detect cache
> hierarchy.
> 
> Change in v9:
> Remove pio node since binding is not documented yet
> 
> Change in v8:
> 1. Fix interrupt-parent of pio node
> 2. Remove pinfunc.h and spi node patches
> 
> Change in v7:
> 1. Place all the MMIO peripherals under one or more simple-bus nodes
> 2. Make the pinfunc.h and spi node into seperate patch
> 3. Modify SPIs pamerater from 4 back to 3
>    and remove patch "support 4 interrupt parameters for sysirq"
> 4. Rename intpol-controller to interrupt-controller
> 5. Rename pinctrl@1000b000 to pinctrl@10005000
> 
> Change in v6:
> 1. Remove power and iommu nodes
> 2. Fix dtb build warning
> 3. Fix pinctrl binding doc
> 4. Fix '_' in node names
> 
> Change in v5:
> 1. Collect all device tree nodes to the last patch
> 2. Add PMU
> 3. Add Signed-off-by
> 4. Remove clock driver code and binding doc
> 5. Add pinctrl, iommu, spi, and pwrap nodes
> 
> Change in v4:
> 1. Correct syntax error in dtsi
> 2. Add MT8183 clock support
> 
> Change in v3:
> 1. Fill out GICC, GICH, GICV regions
> 2. Update Copyright to 2018
> 
> Change in v2:
> 1. Split dt-bindings into different patches
> 2. Correct bindings for supported SoCs (mtk-uart.txt)
> 
> Ben Ho (1):
>   arm64: dts: Add Mediatek SoC MT8183 and evaluation board dts and
>     Makefile
> 
> Erin Lo (1):
>   arm64: dts: mt8183: add spi node
> 
> Hsin-Yi, Wang (1):
>   arm64: dts: mt8183: add capacity-dmips-mhz
> 
> Michael Mei (1):
>   arm64: dts: mt8183: add efuse and Mediatek Chip id node to read
> 
> Zhiyong Tao (2):
>   arm64: dts: mt8183: add pinctrl device node
>   arm64: dts: mt8183: Add auxadc device node
> 
>  arch/arm64/boot/dts/mediatek/Makefile       |   1 +
>  arch/arm64/boot/dts/mediatek/mt8183-evb.dts | 140 ++++++
>  arch/arm64/boot/dts/mediatek/mt8183.dtsi    | 447 ++++++++++++++++++++
>  3 files changed, 588 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/mediatek/mt8183-evb.dts
>  create mode 100644 arch/arm64/boot/dts/mediatek/mt8183.dtsi
> 
> --
> 1.8.1.1.dirty
> 

^ permalink raw reply

* Re: [patch 0/5] *** DMA based UART driver for AST2500 ***
From: Greg KH @ 2019-06-21 13:17 UTC (permalink / raw)
  To: sudheer.v
  Cc: jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti,
	sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561115855-4186-1-git-send-email-open.sudheer@gmail.com>

On Fri, Jun 21, 2019 at 04:47:30PM +0530, sudheer.v wrote:
> From: sudheer veliseti <sudheer.open@gmail.com>
> 
> Hi,
> AST2500 has dedicated Uart DMA controller which has 12 sets of
> Tx and RX channels connected to UART controller directly.
> Since the DMA controller have dedicated buffers and registers,
> there would be little benifit in adding DMA framework overhead.
> So the software for DMA controller is included within the UART driver itself.
> 
> Thanks and Regards
> Sudheer.V

Is this a v2 of this patch series?

You always have to version your patches and say what changed from the
previous one, like the documentation says to do.

Please fix this up and resend.

thanks,

greg k-h

^ permalink raw reply

* [patch 5/5] Documentation: DT bindings AST2500 DMA UART driver
From: sudheer.v @ 2019-06-21 11:17 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561115855-4186-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
 .../bindings/serial/ast2500-dma-uart.txt      | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt

diff --git a/Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt b/Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
new file mode 100644
index 000000000000..6f01ddecba56
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
@@ -0,0 +1,40 @@
+
+node for DMA controller:
+                       ast_uart_sdma: uart_sdma@1e79e000 {
+                               compatible = "aspeed,ast-uart-sdma";
+                               reg = <0x1e79e000 0x400>;
+                               interrupts = <50>;
+                               status = "disabled";
+                       };
+this node  doesn't binds with any driver.
+DMA controller is handled as a separate SW layer,and is included in the same driver.
+This DMA controller node is included in DT just for Register base and interrupt details
+
+
+
+node for DMA-UART :
+
+
+Required properties:
+
+- compatible: "aspeed,ast-sdma-uart"
+- reg: The base address of the UART register bank
+- interrupts: should contain interrupt specifier.
+- clocks: Clock driving the hardware;
+- pinctrl-0 : list of pinconfigurations
+- dma-channel: channel of DMA-controller which is used
+
+Example:
+
+                 dma_uart1: dma_uart1@1e783000{
+                          compatible = "aspeed,ast-sdma-uart";
+                          reg = <0x1e783000 0x1000>;
+                          reg-shift = <2>;
+                          interrupts = <9>;
+                          clocks = <&syscon ASPEED_CLK_GATE_UART1CLK>;
+                          dma-channel = <0>;
+                          no-loopback-test;
+                          pinctrl-names = "default";
+                          pinctrl-0 = <&pinctrl_txd1_default &pinctrl_rxd1_default>;
+                          status = "disabled";
+                       };
-- 
2.17.1

^ permalink raw reply related

* [patch 4/5] defconfig and MAINTAINERS updated for AST2500 DMA UART driver
From: sudheer.v @ 2019-06-21 11:17 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561115855-4186-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
 MAINTAINERS                          | 13 +++++++++++++
 arch/arm/configs/aspeed_g5_defconfig |  1 +
 2 files changed, 14 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 997e27ab492f..c9a9790b97f6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1347,6 +1347,19 @@ F:	drivers/crypto/axis
 F:	drivers/pinctrl/pinctrl-artpec*
 F:	Documentation/devicetree/bindings/pinctrl/axis,artpec6-pinctrl.txt
 
+ARM/ASPEED DMA UART DRIVER
+M:	sudheer v <sudheer.open@gmail.com>
+M:	ShivahShankar <shivahshankar.shankarnarayanrao@aspeedtech.com>
+R:	Joel Stanley <joel@jms.id.au>
+R:	Andrew Jeffery <andrew@aj.id.au>
+R:	Vinod Koul <vkoul@kernel.org>
+L:	dmaengine@vger.kernel.org
+L:	openbmc@lists.ozlabs.org
+L:	linux-aspeed@lists.ozlabs.org
+S:	Maintained
+F:	drivers/tty/serial/8250/8250_aspeed_uart_dma.c
+F:	Documentation/devicetree/bindings/serial/ast-sdma-uart.txt
+
 ARM/ASPEED I2C DRIVER
 M:	Brendan Higgins <brendanhiggins@google.com>
 R:	Benjamin Herrenschmidt <benh@kernel.crashing.org>
diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
index 1849cbc161b4..25bf26630939 100644
--- a/arch/arm/configs/aspeed_g5_defconfig
+++ b/arch/arm/configs/aspeed_g5_defconfig
@@ -144,6 +144,7 @@ CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_SERIAL_8250_NR_UARTS=6
 CONFIG_SERIAL_8250_RUNTIME_UARTS=6
+CONFIG_AST_SERIAL_DMA_UART=y
 CONFIG_SERIAL_8250_EXTENDED=y
 CONFIG_SERIAL_8250_ASPEED_VUART=y
 CONFIG_SERIAL_8250_SHARE_IRQ=y
-- 
2.17.1

^ permalink raw reply related

* [patch 3/5] DT nodes for AST2500 DMA UART driver
From: sudheer.v @ 2019-06-21 11:17 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561115855-4186-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

DT node for DMA controller(ast_uart_sdma) doesn't bind to any DMA controller driver.
This is because Software for DMA controller is not based on DMA framework,but is dedicated
and serves only UARTs in AST2500. ast_uart_sdma node is searched by compatible string in the 
driver software.basic use of this node is to provide register base address of DMA controller and DMA irq number(<50>).
IRQ of DMA controller is of crucial importance, which does RX and TX of UART data. 

uart nodes dma_uart1,2...etc binds to the platform driver.
irq numbers <9>,<32>,<33>,<34> in dma_uart nodes install ISRs which are of not much interest in uart data TX/RX .


Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
 arch/arm/boot/dts/aspeed-ast2500-evb.dts | 21 +++++++
 arch/arm/boot/dts/aspeed-g5.dtsi         | 71 ++++++++++++++++++++++--
 2 files changed, 88 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/aspeed-ast2500-evb.dts b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
index 5dbb33c10c4f..4da09fbe94df 100644
--- a/arch/arm/boot/dts/aspeed-ast2500-evb.dts
+++ b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
@@ -64,6 +64,27 @@
 	status = "okay";
 };
 
+&ast_uart_sdma {
+	status = "okay";
+};
+
+&dma_uart1 {
+	status = "okay";
+};
+
+&dma_uart2 {
+	status = "okay";
+};
+
+&dma_uart3 {
+	status = "okay";
+};
+
+&dma_uart4 {
+	status = "okay";
+};
+
+
 &mac0 {
 	status = "okay";
 
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 674746513031..fb7b3ed463de 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -23,10 +23,10 @@
 		i2c11 = &i2c11;
 		i2c12 = &i2c12;
 		i2c13 = &i2c13;
-		serial0 = &uart1;
-		serial1 = &uart2;
-		serial2 = &uart3;
-		serial3 = &uart4;
+		serial0 = &dma_uart1;
+		serial1 = &dma_uart2;
+		serial2 = &dma_uart3;
+		serial3 = &dma_uart4;
 		serial4 = &uart5;
 		serial5 = &vuart;
 		peci0 = &peci0;
@@ -497,6 +497,69 @@
 				status = "disabled";
 			};
 
+			ast_uart_sdma: uart_sdma@1e79e000 {
+				compatible = "aspeed,ast-uart-sdma";
+				reg = <0x1e79e000 0x400>;
+				interrupts = <50>;
+				status = "disabled";
+			};
+
+			dma_uart1: dma_uart1@1e783000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e783000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <9>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART1CLK>;
+				dma-channel = <0>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd1_default
+							 &pinctrl_rxd1_default>;
+				status = "disabled";
+			};
+
+			dma_uart2: dma_uart2@1e78d000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e78d000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <32>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART2CLK>;
+				dma-channel = <1>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd2_default
+							 &pinctrl_rxd2_default>;
+				status = "disabled";
+			};
+
+			dma_uart3: dma_uart3@1e78e000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e78e000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <33>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART3CLK>;
+				dma-channel = <2>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd3_default
+							 &pinctrl_rxd3_default>;
+				status = "disabled";
+			};
+
+			dma_uart4: dma_uart4@1e78f000{
+				compatible = "aspeed,ast-sdma-uart";
+				reg = <0x1e78f000 0x1000>;
+				reg-shift = <2>;
+				interrupts = <34>;
+				clocks = <&syscon ASPEED_CLK_GATE_UART4CLK>;
+				dma-channel = <3>;
+				no-loopback-test;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_txd4_default
+							 &pinctrl_rxd4_default>;
+				status = "disabled";
+			};
+
 			i2c: bus@1e78a000 {
 				compatible = "simple-bus";
 				#address-cells = <1>;
-- 
2.17.1

^ permalink raw reply related

* [patch 2/5] build configuration for AST2500 DMA UART driver
From: sudheer.v @ 2019-06-21 11:17 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561115855-4186-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
 drivers/tty/serial/8250/Kconfig  | 35 +++++++++++++++++++++++++++++++-
 drivers/tty/serial/8250/Makefile |  1 +
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 15c2c5463835..c793466a1c47 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -45,7 +45,7 @@ config SERIAL_8250_DEPRECATED_OPTIONS
 	  keep the 8250_core.* options around until they revert the changes
 	  they already did.
 
-	  If 8250 is built as a module, this adds 8250_core alias instead. 
+	  If 8250 is built as a module, this adds 8250_core alias instead.
 
 	  If you did not notice yet and/or you have userspace from pre-3.7, it
 	  is safe (and recommended) to say N here.
@@ -189,6 +189,39 @@ config SERIAL_8250_RUNTIME_UARTS
 	  with the module parameter "nr_uarts", or boot-time parameter
 	  8250.nr_uarts
 
+config  AST_SERIAL_DMA_UART
+        tristate "AST UART driver with DMA"
+        select SERIAL_CORE
+        help
+          UART driver with DMA support for Aspeed BMC AST25XX.
+          this driver supports UARTs in AST2500,AST2600. It uses
+          DMA channel of DMA engines present in these chips.
+          since this dma engine is used only by UARTs it is not
+          added as a separate DMA driver instead added as a layer
+          within UART driver.
+
+
+config AST_NR_DMA_UARTS
+        int "Maximum number of uart dma serial ports"
+        depends on AST_SERIAL_DMA_UART
+        default "4"
+        help
+          Set this to the number of serial ports you want the driver
+          to support.  This includes any ports discovered via ACPI or
+          PCI enumeration and any ports that may be added at run-time
+          via hot-plug, or any ISA multi-port serial cards.
+
+config AST_RUNTIME_DMA_UARTS
+        int "Number of uart dma serial ports to register at runtime"
+        depends on AST_SERIAL_DMA_UART
+        range 0 AST_NR_DMA_UARTS
+        default "4"
+        help
+          Set this to the maximum number of serial ports you want
+          the kernel to register at boot time.  This can be overridden
+          with the module parameter "nr_uarts", or boot-time parameter
+          8250.nr_uarts
+
 config SERIAL_8250_EXTENDED
 	bool "Extended 8250/16550 serial driver options"
 	depends on SERIAL_8250
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 18751bc63a84..54d40e5c6e2a 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS)		+= 8250_lpss.o
 obj-$(CONFIG_SERIAL_8250_MID)		+= 8250_mid.o
 obj-$(CONFIG_SERIAL_8250_MOXA)		+= 8250_moxa.o
 obj-$(CONFIG_SERIAL_8250_PXA)		+= 8250_pxa.o
+obj-$(CONFIG_AST_SERIAL_DMA_UART)	+= 8250_ast2500_uart_dma.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM)	+= 8250_of.o
 
 CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt
-- 
2.17.1

^ permalink raw reply related

* [patch 1/5] AST2500 DMA UART driver
From: sudheer.v @ 2019-06-21 11:17 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed
In-Reply-To: <1561115855-4186-1-git-send-email-open.sudheer@gmail.com>

From: sudheer veliseti <sudheer.open@gmail.com>

UART driver for Aspeed's bmc chip AST2500

Design approch:
AST2500 has dedicated Uart DMA controller which has 12 sets of Tx and RX channels
connected to UART controller directly.
Since the DMA controller have dedicated buffers and registers,
there would be little benifit in adding DMA framework overhead.
So the software for DMA controller is included within the UART driver itself.

implementation details:
'struct uart_8250_port' serial port  is populated and registered with 8250_core.
Rx and Tx dma channels are requested from DMA controller software Layer, which
is part of uart driver itself.
Interrupt service routine for DMA controller is the crucial one for Handling all
the tx and rx data. ISRs installed for individual uarts are just dummy,and are helpful 
only to report any spurious interrupts in hardware.


Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
 .../tty/serial/8250/8250_ast2500_uart_dma.c   | 1879 +++++++++++++++++
 1 file changed, 1879 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c

diff --git a/drivers/tty/serial/8250/8250_ast2500_uart_dma.c b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
new file mode 100644
index 000000000000..13911a0a745a
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
@@ -0,0 +1,1879 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  DMA UART Driver for ASPEED BMC chip: AST2500
+ *
+ *  Copyright (C) 2019 sudheer Kumar veliseti, Aspeed technology Inc.
+ *  <open.sudheer@gmail.com>
+ *
+ */
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/nmi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "8250.h"
+
+#define DMA_BUFF_SIZE 0x1000      // 4096
+#define SDMA_RX_BUFF_SIZE 0x10000 // 65536
+
+#define SDDMA_RX_FIX 1
+/* enum ast_uart_chan_op
+ * operation codes passed to the DMA code by the user, and also used
+ * to inform the current channel owner of any changes to the system state
+ */
+
+enum ast_uart_chan_op {
+	AST_UART_DMAOP_TRIGGER,
+	AST_UART_DMAOP_STOP,
+	AST_UART_DMAOP_PAUSE,
+};
+
+/* ast_uart_dma_cbfn_t *  * buffer callback routine type */
+typedef void (*ast_uart_dma_cbfn_t)(void *dev_id, u16 len);
+
+struct ast_sdma_info {
+	u8 ch_no;
+	u8 direction;
+	u8 enable;
+	void *priv;
+	char *sdma_virt_addr;
+	dma_addr_t dma_phy_addr;
+	/* cdriver callbacks */
+	ast_uart_dma_cbfn_t callback_fn; /* buffer done callback */
+};
+
+#define AST_UART_SDMA_CH 12
+
+struct ast_sdma_ch {
+	struct ast_sdma_info tx_dma_info[AST_UART_SDMA_CH];
+	struct ast_sdma_info rx_dma_info[AST_UART_SDMA_CH];
+};
+
+struct ast_sdma {
+	void __iomem *reg_base;
+	int dma_irq;
+	struct ast_sdma_ch *dma_ch;
+	struct regmap *map;
+};
+
+
+
+#define UART_TX_SDMA_EN 0x00
+#define UART_RX_SDMA_EN 0x04
+#define UART_SDMA_CONF 0x08
+#define UART_SDMA_TIMER 0x0C
+#define UART_TX_SDMA_REST 0x20
+#define UART_RX_SDMA_REST 0x24
+#define UART_TX_SDMA_IER 0x30
+#define UART_TX_SDMA_ISR 0x34
+#define UART_RX_SDMA_IER 0x38
+#define UART_RX_SDMA_ISR 0x3C
+#define UART_TX_R_POINT(x) (0x40 + (x * 0x20))
+#define UART_TX_W_POINT(x) (0x44 + (x * 0x20))
+#define UART_TX_SDMA_ADDR(x) (0x48 + (x * 0x20))
+#define UART_RX_R_POINT(x) (0x50 + (x * 0x20))
+#define UART_RX_W_POINT(x) (0x54 + (x * 0x20))
+#define UART_RX_SDMA_ADDR(x) (0x58 + (x * 0x20))
+
+/* UART_TX_SDMA_EN-0x00 : UART TX DMA Enable */
+/* UART_RX_SDMA_EN-0x04 : UART RX DMA Enable */
+#define SDMA_CH_EN(x) (0x1 << (x))
+
+/* UART_SDMA_CONF - 0x08 : Misc, Buffer size  */
+#define SDMA_TX_BUFF_SIZE_MASK (0x3)
+#define SDMA_SET_TX_BUFF_SIZE(x) (x)
+#define SDMA_BUFF_SIZE_1KB (0x0)
+#define SDMA_BUFF_SIZE_4KB (0x1)
+#define SDMA_BUFF_SIZE_16KB (0x2)
+#define SDMA_BUFF_SIZE_64KB (0x3)
+#define SDMA_RX_BUFF_SIZE_MASK (0x3 << 2)
+#define SDMA_SET_RX_BUFF_SIZE(x) (x << 2)
+#define SDMA_TIMEOUT_DIS (0x1 << 4)
+
+/* UART_SDMA_TIMER-0x0C :  UART DMA time out timer */
+
+/* UART_TX_SDMA_IER			0x30	*/
+/* UART_TX_SDMA_ISR			0x34	*/
+
+#define UART_SDMA11_INT (1 << 11)
+#define UART_SDMA10_INT (1 << 10)
+#define UART_SDMA9_INT (1 << 9)
+#define UART_SDMA8_INT (1 << 8)
+#define UART_SDMA7_INT (1 << 7)
+#define UART_SDMA6_INT (1 << 6)
+#define UART_SDMA5_INT (1 << 5)
+#define UART_SDMA4_INT (1 << 4)
+#define UART_SDMA3_INT (1 << 3)
+#define UART_SDMA2_INT (1 << 2)
+#define UART_SDMA1_INT (1 << 1)
+#define UART_SDMA0_INT (1 << 0)
+
+
+/*
+ * Configuration:
+ *   share_irqs - whether we pass IRQF_SHARED to request_irq().  This option
+ *                is unsafe when used on edge-triggered interrupts.
+ */
+static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+static unsigned int nr_uarts = CONFIG_AST_RUNTIME_DMA_UARTS;
+
+
+#define PASS_LIMIT 256
+
+#include <asm/serial.h>
+
+#define UART_DMA_NR CONFIG_AST_NR_DMA_UARTS
+
+
+struct ast_uart_priv_data {
+
+	unsigned short line; //index of uart port
+	struct uart_8250_port *up;
+	u8 dma_ch; // dma channel number
+	struct circ_buf rx_dma_buf;
+	struct circ_buf tx_dma_buf;
+	dma_addr_t dma_rx_addr; /* Mapped ADMA descr. table */
+	dma_addr_t dma_tx_addr; /* Mapped ADMA descr. table */
+#ifdef SDDMA_RX_FIX
+	struct tasklet_struct rx_tasklet;
+#else
+	struct timer_list rx_timer;
+#endif
+	struct tasklet_struct tx_tasklet;
+	spinlock_t lock;
+	int tx_done;
+	int tx_count;
+};
+
+
+static inline struct uart_8250_port *
+to_uart_8250_port(struct uart_port *uart) {
+	return container_of(uart, struct uart_8250_port, port);
+}
+
+struct irq_info {
+	spinlock_t lock;
+	struct uart_8250_port *up;
+};
+
+static struct irq_info ast_uart_irq[1];
+static DEFINE_MUTEX(ast_uart_mutex);
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial8250_config uart_config[] = {
+	[PORT_UNKNOWN] = {
+		.name		= "unknown",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_8250] = {
+		.name		= "8250",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16450] = {
+		.name		= "16450",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16550] = {
+		.name		= "16550",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16550A] = {
+		.name		= "16550A",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10
+							| UART_FCR_DMA_SELECT,
+		.flags		= UART_CAP_FIFO,
+	},
+};
+
+/* sane hardware needs no mapping */
+#define map_8250_in_reg(up, offset) (offset)
+#define map_8250_out_reg(up, offset) (offset)
+
+// SDMA - software Layer : (previously was in ast-uart-sdma.c)
+static inline void ast_uart_sdma_write(struct ast_sdma *sdma,
+						u32 val, u32 reg)
+{
+	pr_debug("uart dma write:val:%x,reg:%x\n", val, reg);
+	writel(val, sdma->reg_base + reg);
+}
+
+static inline u32 ast_uart_sdma_read(struct ast_sdma *sdma, u32 reg)
+{
+	return readl(sdma->reg_base + reg);
+}
+
+struct ast_sdma ast_uart_sdma;
+
+int ast_uart_rx_sdma_enqueue(u8 ch, dma_addr_t rx_buff)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("ch = %d, rx buff = %x\n", ch, rx_buff);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, rx_buff, UART_RX_SDMA_ADDR(ch));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_tx_sdma_enqueue(u8 ch, dma_addr_t tx_buff)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("ch = %d, tx buff = %x\n", ch, tx_buff);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, tx_buff, UART_TX_SDMA_ADDR(ch));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_rx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	pr_debug("RX DMA CTRL [ch %d]\n", ch);
+
+	local_irq_save(flags);
+
+	switch (op) {
+	case AST_UART_DMAOP_TRIGGER:
+	pr_debug("Trigger\n");
+	dma_ch->enable = 1;
+#ifdef SDDMA_RX_FIX
+#else
+	ast_uart_set_sdma_time_out(0xffff);
+#endif
+	// set enable
+	ast_uart_sdma_write(sdma,
+			ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) | (0x1 << ch),
+			UART_RX_SDMA_EN);
+	break;
+	case AST_UART_DMAOP_STOP:
+	// disable engine
+	pr_debug("STOP\n");
+	dma_ch->enable = 0;
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_EN);
+	// set reset
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) |
+				  (0x1 << ch),
+			UART_RX_SDMA_REST);
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(ch));
+	ast_uart_sdma_write(sdma, dma_ch->dma_phy_addr, UART_RX_SDMA_ADDR(ch));
+	break;
+	case AST_UART_DMAOP_PAUSE:
+	// disable engine
+	dma_ch->enable = 0;
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_EN);
+	break;
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+int ast_uart_tx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+	pr_debug("TX DMA CTRL [ch %d]\n", ch);
+
+	local_irq_save(flags);
+
+	switch (op) {
+	case AST_UART_DMAOP_TRIGGER:
+	pr_debug("TRIGGER : Enable\n");
+	dma_ch->enable = 1;
+	// set enable
+	ast_uart_sdma_write(sdma,
+			ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) | (0x1 << ch),
+			UART_TX_SDMA_EN);
+	break;
+	case AST_UART_DMAOP_STOP:
+	pr_debug("STOP : DISABLE & RESET\n");
+	dma_ch->enable = 0;
+	// disable engine
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_EN);
+	// set reset
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) |
+				  (0x1 << ch),
+			UART_TX_SDMA_REST);
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(ch));
+	break;
+	case AST_UART_DMAOP_PAUSE:
+	pr_debug("PAUSE : DISABLE\n");
+	dma_ch->enable = 0;
+	// disable engine
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_EN);
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+u32 ast_uart_get_tx_sdma_pt(u8 ch)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	return ast_uart_sdma_read(sdma, UART_TX_R_POINT(ch));
+}
+
+int ast_uart_tx_sdma_update(u8 ch, u16 point)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("TX DMA CTRL [ch %d] point %d\n", ch, point);
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, point, UART_TX_W_POINT(ch));
+	local_irq_restore(flags);
+	return 0;
+}
+
+int ast_uart_tx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+	pr_debug("TX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+	dma_ch->priv = id;
+	dma_ch->callback_fn = rtn;
+
+	// DMA IRQ En
+	ast_uart_sdma_write(sdma,
+		      ast_uart_sdma_read(sdma, UART_TX_SDMA_IER) | (1 << ch),
+		      UART_TX_SDMA_IER);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_rx_sdma_update(u8 ch, u16 point)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	pr_debug("RX DMA CTRL [ch %d] point %x\n", ch, point);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, point, UART_RX_R_POINT(ch));
+	local_irq_restore(flags);
+	return 0;
+}
+
+#ifdef SDDMA_RX_FIX
+char *ast_uart_rx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	pr_debug("RX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return 0;
+	}
+	dma_ch->priv = id;
+
+	dma_ch->callback_fn = rtn;
+
+	// DMA IRQ En
+	ast_uart_sdma_write(sdma,
+		      ast_uart_sdma_read(sdma, UART_RX_SDMA_IER) | (1 << ch),
+		      UART_RX_SDMA_IER);
+
+	local_irq_restore(flags);
+
+	return dma_ch->sdma_virt_addr;
+}
+
+#else
+char *ast_uart_rx_sdma_request(u8 ch, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	pr_debug("RX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+	dma_ch->priv = id;
+
+	local_irq_restore(flags);
+	return dma_ch->sdma_virt_addr;
+}
+#endif
+
+u16 ast_uart_get_rx_sdma_pt(u8 ch)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	return ast_uart_sdma_read(sdma, UART_RX_W_POINT(ch));
+}
+
+void ast_uart_set_sdma_time_out(u16 val)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	ast_uart_sdma_write(sdma, val, UART_SDMA_TIMER);
+}
+
+static inline void ast_sdma_bufffdone(struct ast_sdma_info *sdma_ch)
+{
+	u32 len;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	if (sdma_ch->enable == 0) {
+		pr_debug("sdma Please check ch_no %x %s!!!!!\n",
+			sdma_ch->ch_no, sdma_ch->direction ? "TX" : "RX");
+		if (sdma_ch->direction) {
+			ast_uart_sdma_write(sdma,
+				ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+				& ~(0x1 << sdma_ch->ch_no), UART_TX_SDMA_EN);
+		} else {
+			ast_uart_sdma_write(sdma,
+				ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				~(0x1 << sdma_ch->ch_no), UART_RX_SDMA_EN);
+			ast_uart_rx_sdma_update(sdma_ch->ch_no,
+				ast_uart_get_rx_sdma_pt(sdma_ch->ch_no));
+			pr_debug("OFFSET : UART_RX_SDMA_EN = %x\n ",
+				ast_uart_sdma_read(sdma, UART_RX_SDMA_EN));
+		}
+		return;
+	}
+
+	if (sdma_ch->direction) {
+		len = ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no));
+		pr_debug("tx rp %x , wp %x\n",
+		ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no)),
+		ast_uart_sdma_read(sdma, UART_TX_W_POINT(sdma_ch->ch_no))
+		);
+	} else {
+		pr_debug("rx rp %x , wp %x\n",
+		ast_uart_sdma_read(sdma, UART_RX_R_POINT(sdma_ch->ch_no)),
+		ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no))
+		);
+		len = ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no));
+	}
+
+	pr_debug("<dma dwn>: ch[%d] : %s ,len : %d\n", sdma_ch->ch_no,
+				sdma_ch->direction ? "tx" : "rx", len);
+
+	if (sdma_ch->callback_fn != NULL)
+		(sdma_ch->callback_fn)(sdma_ch->priv, len);
+}
+
+static irqreturn_t ast_uart_sdma_isr(int irq, void *dev_id)
+{
+	struct ast_sdma *sdma = (struct ast_sdma *)dev_id;
+
+	u32 tx_sts = ast_uart_sdma_read(sdma, UART_TX_SDMA_ISR);
+	u32 rx_sts = ast_uart_sdma_read(sdma, UART_RX_SDMA_ISR);
+
+	pr_debug("tx sts : %x, rx sts : %x\n", tx_sts, rx_sts);
+
+	if ((tx_sts == 0) && (rx_sts == 0)) {
+		pr_debug("SDMA IRQ ERROR !!!\n");
+		return IRQ_HANDLED;
+	}
+
+	if (rx_sts & UART_SDMA0_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[0]));
+	} else if (rx_sts & UART_SDMA1_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[1]));
+	} else if (rx_sts & UART_SDMA2_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[2]));
+	} else if (rx_sts & UART_SDMA3_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[3]));
+	} else if (rx_sts & UART_SDMA4_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[4]));
+	} else if (rx_sts & UART_SDMA5_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[5]));
+	} else if (rx_sts & UART_SDMA6_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[6]));
+	} else if (rx_sts & UART_SDMA7_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[7]));
+	} else if (rx_sts & UART_SDMA8_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[8]));
+	} else if (rx_sts & UART_SDMA9_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[9]));
+	} else if (rx_sts & UART_SDMA10_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[10]));
+	} else if (rx_sts & UART_SDMA11_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[11]));
+	} else {
+
+	}
+
+	if (tx_sts & UART_SDMA0_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[0]));
+	} else if (tx_sts & UART_SDMA1_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[1]));
+	} else if (tx_sts & UART_SDMA2_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[2]));
+	} else if (tx_sts & UART_SDMA3_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[3]));
+	} else if (tx_sts & UART_SDMA4_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[4]));
+	} else if (tx_sts & UART_SDMA5_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[5]));
+	} else if (tx_sts & UART_SDMA6_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[6]));
+	} else if (tx_sts & UART_SDMA7_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[7]));
+	} else if (tx_sts & UART_SDMA8_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[8]));
+	} else if (tx_sts & UART_SDMA9_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[9]));
+	} else if (tx_sts & UART_SDMA10_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[10]));
+	} else if (tx_sts & UART_SDMA11_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[11]));
+	} else {
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ast_uart_sdma_probe(void)
+{
+	int i;
+	struct device_node *node;
+	int ret;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	char *rx_dma_virt_addr;
+	dma_addr_t rx_dma_phy_addr;
+
+	sdma->dma_ch = kzalloc(sizeof(struct ast_sdma_ch), GFP_KERNEL);
+	if (!sdma->dma_ch)
+		return -ENOMEM;
+
+	// sdma memory mapping
+	node = of_find_compatible_node(NULL, NULL, "aspeed,ast-uart-sdma");
+	if (!node)
+		return -ENODEV;
+
+	sdma->reg_base = of_iomap(node, 0);
+	if (IS_ERR(sdma->reg_base))
+		return PTR_ERR(sdma->map);
+	rx_dma_virt_addr = dma_alloc_coherent(NULL,
+	SDMA_RX_BUFF_SIZE * AST_UART_SDMA_CH, &rx_dma_phy_addr, GFP_KERNEL);
+
+	if (!rx_dma_virt_addr) {
+		pr_debug("rx_dma_virt_addr Err:dma alloc Failed\n");
+		return -ENOMEM;
+	}
+	for (i = 0; i < AST_UART_SDMA_CH; i++) {
+		// TX ------------------------
+		sdma->dma_ch->tx_dma_info[i].enable = 0;
+		sdma->dma_ch->tx_dma_info[i].ch_no = i;
+		sdma->dma_ch->tx_dma_info[i].direction = 1;
+		ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(i));
+		// RX ------------------------
+		sdma->dma_ch->rx_dma_info[i].enable = 0;
+		sdma->dma_ch->rx_dma_info[i].ch_no = i;
+		sdma->dma_ch->rx_dma_info[i].direction = 0;
+		sdma->dma_ch->rx_dma_info[i].sdma_virt_addr =
+		rx_dma_virt_addr + (SDMA_RX_BUFF_SIZE * i);
+		sdma->dma_ch->rx_dma_info[i].dma_phy_addr =
+		rx_dma_phy_addr + (SDMA_RX_BUFF_SIZE * i);
+		ast_uart_sdma_write(sdma,
+			sdma->dma_ch->rx_dma_info[i].dma_phy_addr,
+			UART_RX_SDMA_ADDR(i));
+		ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(i));
+	}
+
+	ast_uart_sdma_write(sdma, 0xffffffff, UART_TX_SDMA_REST);
+	ast_uart_sdma_write(sdma, 0x0, UART_TX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0xffffffff, UART_RX_SDMA_REST);
+	ast_uart_sdma_write(sdma, 0x0, UART_RX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_EN);
+	ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_EN);
+
+#ifdef SDDMA_RX_FIX
+	ast_uart_sdma_write(sdma, 0x200, UART_SDMA_TIMER);
+#else
+	ast_uart_sdma_write(sdma, 0xffff, UART_SDMA_TIMER);
+#endif
+
+	// TX
+	ast_uart_sdma_write(sdma, 0xfff, UART_TX_SDMA_ISR);
+	ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_IER);
+
+	// RX
+	ast_uart_sdma_write(sdma, 0xfff, UART_RX_SDMA_ISR);
+	ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_IER);
+
+	sdma->dma_irq = of_irq_get(node, 0);
+	ret = request_irq(sdma->dma_irq, ast_uart_sdma_isr, 0,
+						"sdma-intr", sdma);
+	if (ret) {
+		pr_debug("Unable to get UART SDMA IRQ %x\n", ret);
+		return -ENODEV;
+	}
+
+	ast_uart_sdma_write(sdma, SDMA_SET_TX_BUFF_SIZE(SDMA_BUFF_SIZE_4KB) |
+				SDMA_SET_RX_BUFF_SIZE(SDMA_BUFF_SIZE_64KB),
+		      UART_SDMA_CONF);
+	return 0;
+}
+
+// END of SDMA Layer
+
+// UART Driver Layer
+
+static unsigned int ast_serial_in(struct uart_8250_port *up, int offset)
+{
+	offset = map_8250_in_reg(up, offset) << up->port.regshift;
+	return readb(up->port.membase + offset);
+}
+
+static void ast_serial_out(struct uart_8250_port  *up, int offset, int value)
+{
+	/* Save the offset before it's remapped */
+	offset = map_8250_out_reg(up, offset) << up->port.regshift;
+	writeb(value, up->port.membase + offset);
+}
+
+/*
+ * We used to support using pause I/O for certain machines.  We
+ * haven't supported this for a while, but just in case it's badly
+ * needed for certain old 386 machines, I've left these #define's
+ * in....
+ */
+#define serial_inp(up, offset) ast_serial_in(up, offset)
+#define serial_outp(up, offset, value) ast_serial_out(up, offset, value)
+
+/* Uart divisor latch read */
+static inline int _serial_dl_read(struct uart_8250_port *up)
+{
+	return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static inline void _serial_dl_write(struct uart_8250_port *up, int value)
+{
+	serial_outp(up, UART_DLL, value & 0xff);
+	serial_outp(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#define serial_dl_read(up) _serial_dl_read(up)
+#define serial_dl_write(up, value) _serial_dl_write(up, value)
+
+static void ast_uart_tx_sdma_tasklet_func(unsigned long data)
+{
+
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct uart_8250_port *up = priv->up;
+	struct circ_buf *xmit = NULL;
+	u32 tx_pt;
+
+
+	if (!up)
+		return;
+	xmit = &up->port.state->xmit;
+	spin_lock(&up->port.lock);
+	priv->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	dma_sync_single_for_device(up->port.dev, priv->dma_tx_addr,
+					UART_XMIT_SIZE, DMA_TO_DEVICE);
+	tx_pt = ast_uart_get_tx_sdma_pt(priv->dma_ch);
+
+	if (tx_pt > xmit->head)	{
+		if ((tx_pt & 0xfffc) == 0)
+			ast_uart_tx_sdma_update(priv->dma_ch, 0xffff);
+		else
+			ast_uart_tx_sdma_update(priv->dma_ch, 0);
+	} else {
+		ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
+	}
+	ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
+	spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_tx_buffdone(void *dev_id, u16 len)
+{
+
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
+	struct uart_8250_port *up = priv->up;
+	struct circ_buf *xmit;
+
+	if (!up)
+		return;
+	xmit = &(up->port.state->xmit);
+
+	pr_debug("line[%d] : tx len = % d\n", priv->line, len);
+	spin_lock(&up->port.lock);
+	xmit->tail = len;
+	pr_debug(" line[%d], xmit->head = %d, xmit->tail = % d\n",
+			priv->line, xmit->head, xmit->tail);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (xmit->head != xmit->tail)
+		tasklet_schedule(&priv->tx_tasklet);
+
+	spin_unlock(&up->port.lock);
+}
+
+#ifdef SDDMA_RX_FIX
+static void ast_uart_rx_sdma_tasklet_func(unsigned long data)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct circ_buf *rx_ring = &priv->rx_dma_buf;
+	struct tty_port *ttyport;
+	int count;
+	int copy = 0;
+	struct uart_8250_port *up = priv->up;
+
+	if (!up)
+		return;
+
+	ttyport = &up->port.state->port;
+
+	pr_debug("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+			 up->port.line, rx_ring->head, rx_ring->tail);
+	spin_lock(&up->port.lock);
+	if (rx_ring->head > rx_ring->tail) {
+		count = rx_ring->head - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				rx_ring->buf + rx_ring->tail, count);
+	} else if (rx_ring->head < rx_ring->tail) {
+		count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+					rx_ring->buf + rx_ring->tail, count);
+	} else {
+		count = 0;
+	}
+
+	if (copy != count)
+		pr_debug(" !!!!!!!!ERROR 111\n");
+	if (count) {
+		rx_ring->tail += count;
+		rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+		up->port.icount.rx += count;
+		tty_flip_buffer_push(ttyport);
+		ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
+	}
+	spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_rx_buffdone(void *dev_id, u16 len)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
+	struct circ_buf *rx_ring = &priv->rx_dma_buf;
+	struct uart_8250_port *up = priv->up;
+
+	if (!up)
+		return;
+	pr_debug("line[%d], head = %d,len:%d\n",
+			 priv->line, priv->rx_dma_buf.head, len);
+	spin_lock(&up->port.lock);
+	rx_ring->head = len;
+	spin_unlock(&up->port.lock);
+	tasklet_schedule(&priv->rx_tasklet);
+}
+
+#else
+static void ast_uart_rx_timer_func(unsigned long data)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct uart_8250_port *up = priv->up;
+	struct tty_port *ttyport;
+	struct circ_buf *rx_ring;
+	struct tty_struct *tty;
+	char flag;
+	int count;
+	int copy;
+
+
+	if (!up)
+		return;
+	ttyport = &up->port.state->port;
+	rx_ring = &up->rx_dma_buf;
+	tty = up->port.state->port.tty;
+
+	pr_debug("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+				up->port.line, rx_ring->head, rx_ring->tail);
+	rx_ring->head = ast_uart_get_rx_sdma_pt(priv->dma_ch);
+	del_timer(&up->rx_timer);
+
+	if (rx_ring->head > rx_ring->tail) {
+		ast_uart_set_sdma_time_out(0xffff);
+		count = rx_ring->head - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				 rx_ring->buf + rx_ring->tail, count);
+	} else if (rx_ring->head < rx_ring->tail) {
+		ast_uart_set_sdma_time_out(0xffff);
+		count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				 rx_ring->buf + rx_ring->tail, count);
+	} else {
+		count = 0;
+		// pr_debug("@@--%s-- ch = 0x%x\n", __func__, ch);
+	}
+
+	if (copy != count)
+		pr_debug(" !!!!!!!!ERROR 111\n");
+		rx_ring->tail += count;
+		rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+
+	if (count) {
+		//pr_debug("\n count = % d\n", count);
+		up->port.icount.rx += count;
+		spin_lock(&up->port.lock);
+		tty_flip_buffer_push(ttyport);
+		spin_unlock(&up->port.lock);
+		//pr_debug("update rx_ring->tail % x\n", rx_ring->tail);
+		ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
+		priv->workaround = 1;
+	} else {
+		if (priv->workaround) {
+			priv->workaround++;
+			if (priv->workaround > 1)
+				ast_uart_set_sdma_time_out(0);
+			else
+				ast_uart_set_sdma_time_out(0xffff);
+		}
+	}
+	add_timer(&up->rx_timer);
+}
+#endif
+
+/*
+ * FIFO support.
+ */
+static inline void ast25xx_uart_clear_fifos(struct uart_8250_port *p)
+{
+	serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_outp(p, UART_FCR,
+	      UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_outp(p, UART_FCR, 0);
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.
+ */
+static void autoconfig(struct uart_8250_port *up)
+{
+	unsigned long flags;
+
+	pr_debug("line[%d]\n", up->port.line);
+	if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
+		return;
+
+	pr_debug("ttyDMA%d : autoconf (0x%04lx, 0x%p) : ", up->port.line,
+		 up->port.iobase, up->port.membase);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->capabilities = 0;
+	up->bugs = 0;
+
+	up->port.type = PORT_16550A;
+	up->capabilities |= UART_CAP_FIFO;
+
+	up->port.fifosize = uart_config[up->port.type].fifo_size;
+	up->capabilities = uart_config[up->port.type].flags;
+	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+
+	if (up->port.type == PORT_UNKNOWN)
+		goto out;
+
+	/*
+	 * Reset the UART.
+	 */
+	ast25xx_uart_clear_fifos(up);
+	ast_serial_in(up, UART_RX);
+	serial_outp(up, UART_IER, 0);
+
+out:
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pr_debug("type=%s\n", uart_config[up->port.type].name);
+}
+
+static inline void __stop_tx(struct uart_8250_port *p)
+{
+	if (p->ier & UART_IER_THRI) {
+		p->ier &= ~UART_IER_THRI;
+		ast_serial_out(p, UART_IER, p->ier);
+	}
+}
+
+static void ast25xx_uart_stop_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	pr_debug("line[%d]\n", up->port.line);
+	__stop_tx(up);
+}
+
+static void transmit_chars(struct uart_8250_port *up);
+
+static void ast25xx_uart_start_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	struct ast_uart_priv_data *priv = up->port.private_data;
+
+	pr_debug("line[%d]\n", port->line);
+	tasklet_schedule(&priv->tx_tasklet);
+}
+
+static void ast25xx_uart_stop_rx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	pr_debug("line[%d]\n", port->line);
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void ast25xx_uart_enable_ms(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	pr_debug("line[%d]\n", port->line);
+	up->ier |= UART_IER_MSI;
+	ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void transmit_chars(struct uart_8250_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_outp(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_tx_stopped(&up->port)) {
+		ast25xx_uart_stop_tx(&up->port);
+		return;
+	}
+	if (uart_circ_empty(xmit)) {
+		__stop_tx(up);
+		return;
+	}
+
+	count = up->tx_loadsz;
+	do {
+		ast_serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (uart_circ_empty(xmit))
+		__stop_tx(up);
+}
+
+static unsigned int check_modem_status(struct uart_8250_port *up)
+{
+	unsigned int status = ast_serial_in(up, UART_MSR);
+
+	pr_debug("line[%d]\n", up->port.line);
+	status |= up->msr_saved_flags;
+	up->msr_saved_flags = 0;
+	if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI
+					&& up->port.state != NULL) {
+		if (status & UART_MSR_TERI)
+			up->port.icount.rng++;
+		if (status & UART_MSR_DDSR)
+			up->port.icount.dsr++;
+		if (status & UART_MSR_DDCD)
+			uart_handle_dcd_change(&up->port,
+					status & UART_MSR_DCD);
+		if (status & UART_MSR_DCTS)
+			uart_handle_cts_change(&up->port,
+					status & UART_MSR_CTS);
+
+		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+	}
+
+	return status;
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline void ast25xx_uart_handle_port(struct uart_8250_port *up)
+{
+	unsigned int status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	status = serial_inp(up, UART_LSR);
+
+	pr_debug("status = %x\n", status);
+
+	check_modem_status(up);
+	if (status & UART_LSR_THRE)
+		transmit_chars(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ */
+static irqreturn_t ast_uart_interrupt(int irq, void *dev_id)
+{
+	struct irq_info *i = dev_id;
+	int pass_counter = 0, handled = 0, end = 0;
+
+	pr_debug("(%d)-", irq);
+	spin_lock(&i->lock);
+
+	do {
+		struct uart_8250_port *up;
+		unsigned int iir;
+
+		up = (struct uart_8250_port *)(i->up);
+
+		iir = ast_serial_in(up, UART_IIR);
+		if (!(iir & UART_IIR_NO_INT)) {
+			ast25xx_uart_handle_port(up);
+			handled = 1;
+
+		} else
+			end = 1;
+
+		if (pass_counter++ > PASS_LIMIT) {
+			/* If we hit this, we're dead. */
+			pr_err("ast-uart-dma:too much work for irq%d\n", irq);
+			break;
+		}
+	} while (end);
+
+	spin_unlock(&i->lock);
+
+	pr_debug("-(%d)\n", irq);
+
+	return IRQ_RETVAL(handled);
+}
+
+static unsigned int ast25xx_uart_tx_empty(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned long flags;
+	unsigned int lsr;
+
+	pr_debug("line[%d]\n", up->port.line);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	lsr = ast_serial_in(up, UART_LSR);
+	up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ast25xx_uart_get_mctrl(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned int status;
+	unsigned int ret;
+
+	status = check_modem_status(up);
+
+	ret = 0;
+	if (status & UART_MSR_DCD)
+		ret |= TIOCM_CAR;
+	if (status & UART_MSR_RI)
+		ret |= TIOCM_RNG;
+	if (status & UART_MSR_DSR)
+		ret |= TIOCM_DSR;
+	if (status & UART_MSR_CTS)
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+static void ast25xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned char mcr = 0;
+
+	mctrl = 0;
+
+	if (mctrl & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (mctrl & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (mctrl & TIOCM_OUT1)
+		mcr |= UART_MCR_OUT1;
+	if (mctrl & TIOCM_OUT2)
+		mcr |= UART_MCR_OUT2;
+	if (mctrl & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
+
+	ast_serial_out(up, UART_MCR, mcr);
+
+}
+
+static void ast25xx_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		up->lcr |= UART_LCR_SBC;
+	else
+		up->lcr &= ~UART_LCR_SBC;
+	ast_serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int ast25xx_uart_startup(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	// TX DMA
+	struct circ_buf *xmit = &up->port.state->xmit;
+	struct ast_uart_priv_data *priv = up->port.private_data;
+	unsigned long flags;
+	unsigned char lsr, iir;
+	int retval;
+	int irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+	priv->up = up;
+	pr_debug("line[%d]\n", port->line);
+	up->capabilities = uart_config[up->port.type].flags;
+	up->mcr = 0;
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	ast25xx_uart_clear_fifos(up);
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void)serial_inp(up, UART_LSR);
+	(void)serial_inp(up, UART_RX);
+	(void)serial_inp(up, UART_IIR);
+	(void)serial_inp(up, UART_MSR);
+
+	ast_uart_irq[0].up = up;
+	retval = request_irq(up->port.irq, ast_uart_interrupt, irq_flags,
+		       "ast-uart-dma", ast_uart_irq);
+	if (retval)
+		return retval;
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl |= TIOCM_OUT2;
+
+	ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+
+	/*
+	 * Do a quick test to see if we receive an
+	 * interrupt when we enable the TX irq.
+	 */
+	serial_outp(up, UART_IER, UART_IER_THRI);
+	lsr = ast_serial_in(up, UART_LSR);
+	iir = ast_serial_in(up, UART_IIR);
+	serial_outp(up, UART_IER, 0);
+
+	if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+		if (!(up->bugs & UART_BUG_TXEN)) {
+			up->bugs |= UART_BUG_TXEN;
+			pr_debug("ttyDMA%d-enabling bad tx status\n",
+								 port->line);
+		}
+	} else {
+		up->bugs &= ~UART_BUG_TXEN;
+	}
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Clear the interrupt registers again for luck, and clear the
+	 * saved flags to avoid getting false values from polling
+	 * routines or the previous session.
+	 */
+	serial_inp(up, UART_LSR);
+	serial_inp(up, UART_RX);
+	serial_inp(up, UART_IIR);
+	serial_inp(up, UART_MSR);
+	up->lsr_saved_flags = 0;
+	up->msr_saved_flags = 0;
+
+	// RX DMA
+	priv->rx_dma_buf.head = 0;
+	priv->rx_dma_buf.tail = 0;
+	up->port.icount.rx = 0;
+
+	priv->tx_done = 1;
+	priv->tx_count = 0;
+
+	priv->rx_dma_buf.head = 0;
+	priv->rx_dma_buf.tail = 0;
+#ifdef SDDMA_RX_FIX
+#else
+	priv->workaround = 0;
+#endif
+	// pr_debug("Sending trigger for % x\n", priv->dma_ch);
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
+#ifdef SDDMA_RX_FIX
+#else
+	add_timer(&priv->rx_timer);
+#endif
+	priv->tx_dma_buf.head = 0;
+	priv->tx_dma_buf.tail = 0;
+	priv->tx_dma_buf.buf = xmit->buf;
+
+	pr_debug("head:0x%x tail:0x%x\n", xmit->head, xmit->tail);
+	xmit->head = 0;
+	xmit->tail = 0;
+
+	priv->dma_tx_addr = dma_map_single(port->dev, priv->tx_dma_buf.buf,
+				   UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
+	ast_uart_tx_sdma_enqueue(priv->dma_ch, priv->dma_tx_addr);
+	ast_uart_tx_sdma_update(priv->dma_ch, 0);
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
+	return 0;
+}
+
+static void ast25xx_uart_shutdown(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	struct ast_uart_priv_data *priv = up->port.private_data;
+	unsigned long flags;
+
+	pr_debug("line[%d]\n", port->line);
+	priv->up = NULL;
+
+	up->ier = 0;
+	serial_outp(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+
+	ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	ast_serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+	ast25xx_uart_clear_fifos(up);
+
+	(void)ast_serial_in(up, UART_RX);
+
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
+#ifdef SDDMA_RX_FIX
+#else
+	del_timer_sync(&up->rx_timer);
+#endif
+
+	// Tx buffer will free by serial_core.c
+	free_irq(up->port.irq, ast_uart_irq);
+}
+
+static unsigned int ast25xx_uart_get_divisor(struct uart_port *port,
+					   unsigned int baud)
+{
+	unsigned int quot;
+
+	quot = uart_get_divisor(port, baud);
+
+	return quot;
+}
+
+static void ast25xx_uart_set_termios(struct uart_port *port,
+				   struct ktermios *termios,
+				   struct ktermios *old)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (termios->c_cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+#endif
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	quot = ast25xx_uart_get_divisor(port, baud);
+
+	if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
+		if (baud < 2400)
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+		else
+			fcr = uart_config[up->port.type].fcr;
+	}
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characteres to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= UART_LSR_BI;
+	/*
+	 * If we're ignoring parity and break indicators,
+	 * ignore overruns too (for real raw support).
+	 */
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= UART_LSR_DR;
+
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+
+	ast_serial_out(up, UART_IER, up->ier);
+
+	serial_outp(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+
+	serial_dl_write(up, quot);
+
+	/*
+	 * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
+	 * is written without DLAB set, this mode will be disabled.
+	 */
+
+	serial_outp(up, UART_LCR, cval); /* reset DLAB */
+	up->lcr = cval;                  /* Save LCR */
+	if (fcr & UART_FCR_ENABLE_FIFO) {
+	/* emulated UARTs (Lucent Venus 167x) need two steps */
+		serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	}
+	serial_outp(up, UART_FCR, fcr); /* set fcr */
+	ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	/* Don't rewrite B0 */
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+/*
+ * Resource handling.
+ */
+static int ast25xx_uart_request_std_resource(struct uart_8250_port  *up)
+{
+	unsigned int size = 8 << up->port.regshift;
+	int ret = 0;
+
+	if (!up->port.mapbase)
+		return ret;
+
+	if (!request_mem_region(up->port.mapbase, size, "ast-uart-dma")) {
+		ret = -EBUSY;
+		return ret;
+	}
+
+	if (up->port.flags & UPF_IOREMAP) {
+		up->port.membase = ioremap_nocache(up->port.mapbase, size);
+		if (!up->port.membase) {
+			release_mem_region(up->port.mapbase, size);
+			ret = -ENOMEM;
+			return ret;
+		}
+	}
+	return ret;
+}
+
+static void ast25xx_uart_release_std_resource(struct uart_8250_port *up)
+{
+	unsigned int size = 8 << up->port.regshift;
+
+	if (!up->port.mapbase)
+		return;
+
+	if (up->port.flags & UPF_IOREMAP) {
+		iounmap(up->port.membase);
+		up->port.membase = NULL;
+	}
+
+	release_mem_region(up->port.mapbase, size);
+}
+
+static void ast25xx_uart_release_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_request_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	int ret;
+
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret == 0)
+		ast25xx_uart_release_std_resource(up);
+
+	return ret;
+}
+
+static void ast25xx_uart_config_port(struct uart_port *port, int flags)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	int ret;
+
+	/*
+	 * Find the region that we can probe for.  This in turn
+	 * tells us whether we can probe for the type of port.
+	 */
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret < 0)
+		return;
+
+	if (flags & UART_CONFIG_TYPE)
+		autoconfig(up);
+
+	if (up->port.type == PORT_UNKNOWN)
+		ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_verify_port(struct uart_port *port,
+				  struct serial_struct *ser)
+{
+	return 0;
+}
+
+static const char *ast25xx_uart_type(struct uart_port *port)
+{
+	int type = port->type;
+
+	if (type >= ARRAY_SIZE(uart_config))
+		type = 0;
+	return uart_config[type].name;
+}
+
+
+
+static unsigned int ast25xx_uart_serial_in(struct uart_port *port, int offset)
+{
+	offset = offset << port->regshift;
+	return readb(port->membase + offset);
+
+}
+
+
+static void ast25xx_uart_serial_out(struct uart_port *port,
+						 int offset, int value)
+{
+	offset =  offset << port->regshift;
+	writeb(value, port->membase + offset);
+}
+
+static const struct uart_ops ast25xx_uart_pops = {
+	.tx_empty = ast25xx_uart_tx_empty,
+	.set_mctrl = ast25xx_uart_set_mctrl,
+	.get_mctrl = ast25xx_uart_get_mctrl,
+	.stop_tx = ast25xx_uart_stop_tx,
+	.start_tx = ast25xx_uart_start_tx,
+	.stop_rx = ast25xx_uart_stop_rx,
+	.enable_ms = ast25xx_uart_enable_ms,
+	.break_ctl = ast25xx_uart_break_ctl,
+	.startup = ast25xx_uart_startup,
+	.shutdown = ast25xx_uart_shutdown,
+	.set_termios = ast25xx_uart_set_termios,
+	.type = ast25xx_uart_type,
+	.release_port = ast25xx_uart_release_port,
+	.request_port = ast25xx_uart_request_port,
+	.config_port = ast25xx_uart_config_port,
+	.verify_port = ast25xx_uart_verify_port,
+};
+
+
+
+/*
+ * Register a set of serial devices attached to a platform device.  The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+struct clk *clk;
+
+static int ast25xx_uart_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct ast_uart_priv_data *priv;
+	struct uart_8250_port port_8250;
+	struct uart_8250_port *up;
+	int ret;
+	u32 read, dma_channel = 0;
+	struct resource *res;
+
+	if (UART_XMIT_SIZE > DMA_BUFF_SIZE)
+		pr_debug("UART_XMIT_SIZE > DMA_BUFF_SIZE : Please Check\n");
+
+	priv = (struct ast_uart_priv_data *)devm_kzalloc(&pdev->dev,
+			sizeof(struct ast_uart_priv_data), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	up = &port_8250;
+	memset(up, 0, sizeof(struct uart_8250_port));
+	up->port.flags = UPF_IOREMAP;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "IRQ resource not found");
+		return -ENODEV;
+	}
+	up->port.irq = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Register base not found");
+		return -ENODEV;
+	}
+	up->port.mapbase = res->start;
+
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret) {
+		dev_err(&pdev->dev, "ioremap_nocache Failed");
+		return ret;
+	}
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		dev_err(&pdev->dev, "missing controller clock");
+
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		dev_err(&pdev->dev, "failed to enable DMA UART Clk");
+
+	up->port.uartclk = clk_get_rate(clk);
+
+	if (of_property_read_u32(np, "reg-shift", &read) == 0)
+		up->port.regshift = read;
+	if (of_property_read_u32(np, "dma-channel", &read) == 0) {
+		dma_channel = read;
+		priv->dma_ch = dma_channel;
+	}
+	up->port.iotype = UPIO_MEM;
+	up->port.flags |= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
+	up->port.dev        = &pdev->dev;
+	if (share_irqs)
+		up->port.flags |= UPF_SHARE_IRQ;
+	up->port.fifosize = uart_config[up->port.type].fifo_size;
+	up->port.type = PORT_16550;
+	up->port.iotype = UPIO_MEM;
+	up->port.flags =  UPF_FIXED_TYPE;
+	up->port.startup = ast25xx_uart_startup;
+	up->port.shutdown = ast25xx_uart_shutdown;
+	up->port.set_termios = ast25xx_uart_set_termios;
+	up->port.set_mctrl = ast25xx_uart_set_mctrl;
+	up->port.serial_in = ast25xx_uart_serial_in;
+	up->port.serial_out = ast25xx_uart_serial_out;
+	up->capabilities = uart_config[up->port.type].flags;
+	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+	up->capabilities |= UART_CAP_FIFO;
+
+	up->port.private_data = priv;
+
+	ret = serial8250_register_8250_port(up);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+		"unable to registr port (IO%lx MEM%llx IRQ%d):%d\n",
+			up->port.iobase, (unsigned long long)up->port.mapbase,
+							 up->port.irq, ret);
+		return ret;
+	}
+	priv->line = ret;
+
+	tasklet_init(&priv->tx_tasklet, ast_uart_tx_sdma_tasklet_func,
+							 (unsigned long)priv);
+#ifdef SDDMA_RX_FIX
+	tasklet_init(&priv->rx_tasklet, ast_uart_rx_sdma_tasklet_func,
+							 (unsigned long)priv);
+#else
+	uart->rx_timer.data = (unsigned long)port;
+	uart->rx_timer.expires = jiffies + (HZ);
+	uart->rx_timer.function = ast_uart_rx_timer_func;
+	init_timer(&priv->rx_timer);
+#endif
+
+//DMA request
+#ifdef SDDMA_RX_FIX
+	priv->rx_dma_buf.buf =
+	ast_uart_rx_sdma_request(priv->dma_ch, ast_uart_rx_buffdone,
+			      priv);
+	if (priv->rx_dma_buf.buf < 0) {
+		pr_debug("Error : failed to get rx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+}
+#else
+	priv->rx_dma_buf.buf = ast_uart_rx_sdma_request(
+	priv->dma_ch, priv);
+	if (priv->rx_dma_buf.buf < 0) {
+		pr_debug("Error : failed to get rx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+	}
+#endif
+	if (ast_uart_tx_sdma_request(
+			priv->dma_ch, ast_uart_tx_buffdone, priv) < 0) {
+		pr_debug("Error : failed to get tx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int ast25xx_uart_remove(struct platform_device *pdev)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_unregister_port(priv->line);
+	return 0;
+}
+
+static int ast25xx_uart_suspend(struct platform_device *pdev,
+			      pm_message_t state)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_suspend_port(priv->line);
+	return 0;
+}
+
+static int ast25xx_uart_resume(struct platform_device *pdev)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_resume_port(priv->line);
+	return 0;
+}
+
+static const struct of_device_id ast_serial_dt_ids[] = {
+	{ .compatible = "aspeed,ast-sdma-uart", },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver ast25xx_uart_driver = {
+	.probe = ast25xx_uart_probe,
+	.remove = ast25xx_uart_remove,
+	.suspend = ast25xx_uart_suspend,
+	.resume = ast25xx_uart_resume,
+	.driver = {
+	    .name = "ast-uart-dma",
+	    .of_match_table = of_match_ptr(ast_serial_dt_ids),
+	},
+};
+
+static int __init ast_uart_init(void)
+{
+	int ret;
+
+	if (nr_uarts > UART_DMA_NR)
+		nr_uarts = UART_DMA_NR;
+
+	ret = ast_uart_sdma_probe();
+	if (ret) {
+		pr_debug("ast_uart_sdma_probe Failed ret = %d\n", ret);
+		goto out;
+	}
+	pr_debug("UART driver with DMA%d ports IRQ sharing %sabled\n",
+		nr_uarts, share_irqs ? "en" : "dis");
+
+	spin_lock_init(&ast_uart_irq[0].lock);
+
+	ret = platform_driver_register(&ast25xx_uart_driver);
+	if (ret == 0)
+		goto out;
+
+out:
+	return ret;
+}
+
+static void __exit ast_uart_exit(void)
+{
+
+	platform_driver_unregister(&ast25xx_uart_driver);
+
+}
+module_init(ast_uart_init);
+module_exit(ast_uart_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AST DMA serial driver");
+MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
-- 
2.17.1

^ permalink raw reply related

* [patch 0/5] *** DMA based UART driver for AST2500 ***
From: sudheer.v @ 2019-06-21 11:17 UTC (permalink / raw)
  To: gregkh, jslaby, joel, andrew, benh, robh+dt, mark.rutland,
	shivahshankar.shankarnarayanrao, shivahshankar, sudheer.veliseti
  Cc: sudheer veliseti, linux-kernel, linux-serial, devicetree,
	linux-aspeed

From: sudheer veliseti <sudheer.open@gmail.com>

Hi,
AST2500 has dedicated Uart DMA controller which has 12 sets of
Tx and RX channels connected to UART controller directly.
Since the DMA controller have dedicated buffers and registers,
there would be little benifit in adding DMA framework overhead.
So the software for DMA controller is included within the UART driver itself.

Thanks and Regards
Sudheer.V

sudheer veliseti (5):
  AST2500 DMA UART driver
  build configuration for AST2500 DMA UART driver
  DT nodes for AST2500 DMA UART driver
  defconfig and MAINTAINERS updated for AST2500 DMA UART driver
  Documentation: DT bindings AST2500 DMA UART driver

 .../bindings/serial/ast2500-dma-uart.txt      |   40 +
 MAINTAINERS                                   |   13 +
 arch/arm/boot/dts/aspeed-ast2500-evb.dts      |   21 +
 arch/arm/boot/dts/aspeed-g5.dtsi              |   71 +-
 arch/arm/configs/aspeed_g5_defconfig          |    1 +
 .../tty/serial/8250/8250_ast2500_uart_dma.c   | 1879 +++++++++++++++++
 drivers/tty/serial/8250/Kconfig               |   35 +-
 drivers/tty/serial/8250/Makefile              |    1 +
 8 files changed, 2056 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
 create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c

-- 
2.17.1

^ permalink raw reply

* [PATCH V33 19/30] Lock down TIOCSSERIAL
From: Matthew Garrett @ 2019-06-21  1:19 UTC (permalink / raw)
  To: jmorris
  Cc: linux-security, linux-kernel, linux-api, David Howells,
	Greg Kroah-Hartman, Matthew Garrett, Jiri Slaby, linux-serial
In-Reply-To: <20190621011941.186255-1-matthewgarrett@google.com>

From: David Howells <dhowells@redhat.com>

Lock down TIOCSSERIAL as that can be used to change the ioport and irq
settings on a serial port.  This only appears to be an issue for the serial
drivers that use the core serial code.  All other drivers seem to either
ignore attempts to change port/irq or give an error.

Reported-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Matthew Garrett <mjg59@google.com>
cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
---
 drivers/tty/serial/serial_core.c | 7 +++++++
 include/linux/security.h         | 1 +
 security/lockdown/lockdown.c     | 1 +
 3 files changed, 9 insertions(+)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 351843f847c0..2dbef7dc23f6 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -22,6 +22,7 @@
 #include <linux/serial_core.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
+#include <linux/security.h>
 
 #include <linux/irq.h>
 #include <linux/uaccess.h>
@@ -852,6 +853,12 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
 	new_flags = (__force upf_t)new_info->flags;
 	old_custom_divisor = uport->custom_divisor;
 
+	if ((change_port || change_irq) &&
+	    security_is_locked_down(LOCKDOWN_TIOCSSERIAL)) {
+		retval = -EPERM;
+		goto exit;
+	}
+
 	if (!capable(CAP_SYS_ADMIN)) {
 		retval = -EPERM;
 		if (change_irq || change_port ||
diff --git a/include/linux/security.h b/include/linux/security.h
index 87c433f1e7db..cb5d74f9b9ff 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -91,6 +91,7 @@ enum lockdown_reason {
 	LOCKDOWN_MSR,
 	LOCKDOWN_ACPI_TABLES,
 	LOCKDOWN_PCMCIA_CIS,
+	LOCKDOWN_TIOCSSERIAL,
 	LOCKDOWN_INTEGRITY_MAX,
 	LOCKDOWN_CONFIDENTIALITY_MAX,
 };
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index ced4ddbb36b4..c6456f300220 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -27,6 +27,7 @@ static char *lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
 	[LOCKDOWN_MSR] = "raw MSR access",
 	[LOCKDOWN_ACPI_TABLES] = "modified ACPI tables",
 	[LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",
+	[LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
 	[LOCKDOWN_INTEGRITY_MAX] = "integrity",
 	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
 };
-- 
2.22.0.410.gd8fdbe21b5-goog

^ permalink raw reply related

* [PATCH RFC v1 7/7] serial: imx: get rid of imx_uart_rts_auto()
From: Sergey Organov @ 2019-06-20 14:47 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Pengutronix Kernel Team, NXP Linux Team, linux-arm-kernel,
	linux-serial, Uwe Kleine-König
In-Reply-To: <1561042073-617-1-git-send-email-sorganov@gmail.com>

Called in only one place, for RS232, it only obscures things, as it
doesn't go well with 2 similar named functions,
imx_uart_rts_inactive() and imx_uart_rts_active(), that both are
RS485-specific.

Signed-off-by: Sergey Organov <sorganov@gmail.com>
---
 drivers/tty/serial/imx.c | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 171347d..a5e80a0 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -402,13 +402,6 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
 	mctrl_gpio_set(sport->gpios, sport->port.mctrl);
 }
 
-/* called with port.lock taken and irqs caller dependent */
-static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2)
-{
-	if (*ucr2 & UCR2_CTS)
-		*ucr2 |= UCR2_CTSC;
-}
-
 /* called with port.lock taken and irqs off */
 static void imx_uart_start_rx(struct uart_port *port)
 {
@@ -1598,8 +1591,10 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
 		else
 			imx_uart_rts_inactive(sport, &ucr2);
 
-	} else if (termios->c_cflag & CRTSCTS)
-		imx_uart_rts_auto(sport, &ucr2);
+	} else if (termios->c_cflag & CRTSCTS) {
+		if (ucr2 & UCR2_CTS)
+			ucr2 |= UCR2_CTSC;
+	}
 
 	if (termios->c_cflag & CRTSCTS)
 		ucr2 &= ~UCR2_IRTS;
-- 
2.10.0.1.g57b01a3

^ permalink raw reply related

* [PATCH RFC v1 6/7] serial: imx: set_mctrl(): correctly restore autoRTS state
From: Sergey Organov @ 2019-06-20 14:47 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Pengutronix Kernel Team, NXP Linux Team, linux-arm-kernel,
	linux-serial, Uwe Kleine-König
In-Reply-To: <1561042073-617-1-git-send-email-sorganov@gmail.com>

imx_uart_set_mctrl() happened to set UCR2_CTSC bit whenever TIOCM_RTS
was set, no matter if RTS/CTS handshake is enabled or not. Now fixed by
turning handshake on only when CRTSCTS bit for the port is set.

Signed-off-by: Sergey Organov <sorganov@gmail.com>
---
 drivers/tty/serial/imx.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 4867f80..171347d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -970,10 +970,19 @@ static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 	if (!(port->rs485.flags & SER_RS485_ENABLED)) {
 		u32 ucr2;
 
+		/*
+		 * Turn off autoRTS (UCR2_CTSC) if RTS is lowered and restore
+		 * autoRTS setting if RTS is raised. Inverted UCR2_IRTS is set
+		 * if and only if CRTSCTS bit is set for the port, so we use it
+		 * to get the state to restore to.
+		 */
 		ucr2 = imx_uart_readl(sport, UCR2);
 		ucr2 &= ~(UCR2_CTS | UCR2_CTSC);
-		if (mctrl & TIOCM_RTS)
-			ucr2 |= UCR2_CTS | UCR2_CTSC;
+		if (mctrl & TIOCM_RTS) {
+			ucr2 |= UCR2_CTS;
+			if (!(ucr2 & UCR2_IRTS))
+				ucr2 |= UCR2_CTSC;
+		}
 		imx_uart_writel(sport, ucr2, UCR2);
 	}
 
-- 
2.10.0.1.g57b01a3

^ permalink raw reply related

* [PATCH RFC v1 5/7] serial: imx: set_termios(): do not enable autoRTS if RTS is unset
From: Sergey Organov @ 2019-06-20 14:47 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Pengutronix Kernel Team, NXP Linux Team, linux-arm-kernel,
	linux-serial, Uwe Kleine-König
In-Reply-To: <1561042073-617-1-git-send-email-sorganov@gmail.com>

set_termios() shouldn't set UCR2_CTSC bit if UCR2_CTS (=TIOCM_RTS) is
cleared. Added corresponding check in imx_uart_rts_auto() to fix this.

Signed-off-by: Sergey Organov <sorganov@gmail.com>
---
 drivers/tty/serial/imx.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index e0f5365..4867f80 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -405,7 +405,8 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
 /* called with port.lock taken and irqs caller dependent */
 static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2)
 {
-	*ucr2 |= UCR2_CTSC;
+	if (*ucr2 & UCR2_CTS)
+		*ucr2 |= UCR2_CTSC;
 }
 
 /* called with port.lock taken and irqs off */
-- 
2.10.0.1.g57b01a3

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox