summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch')
-rw-r--r--meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch1184
1 files changed, 1184 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch
new file mode 100644
index 000000000..525b910d0
--- /dev/null
+++ b/meta-openbmc-mods/meta-ast2600/recipes-kernel/linux/linux-aspeed/0001-serial-8250-Add-Aspeed-UART-driver-with-DMA-supporte.patch
@@ -0,0 +1,1184 @@
+From a4897aed25195742ec9fd992a52a6e7bf872f318 Mon Sep 17 00:00:00 2001
+From: "Chia-Wei, Wang" <chiawei_wang@aspeedtech.com>
+Date: Wed, 22 Jul 2020 13:46:21 +0800
+Subject: [PATCH] serial: 8250: Add Aspeed UART driver with DMA supported
+
+This patch adds drivers for Aspeed UARTs, which are 16550A compatible.
+The drivers includes an wrapper to support the extended DMA feature of
+UART devices and another UDMA driver to control the UART DMA engine.
+
+Signed-off-by: Chia-Wei, Wang <chiawei_wang@aspeedtech.com>
+---
+ arch/arm/boot/dts/aspeed-ast2600-evb.dts | 5 -
+ .../arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 2 -
+ arch/arm/boot/dts/aspeed-g6.dtsi | 28 +-
+ drivers/soc/aspeed/Kconfig | 8 +
+ drivers/soc/aspeed/Makefile | 1 +
+ drivers/soc/aspeed/aspeed-udma.c | 441 ++++++++++++++++
+ drivers/tty/serial/8250/8250_aspeed.c | 494 ++++++++++++++++++
+ drivers/tty/serial/8250/Kconfig | 8 +
+ drivers/tty/serial/8250/Makefile | 1 +
+ include/linux/soc/aspeed/aspeed-udma.h | 30 ++
+ 10 files changed, 997 insertions(+), 21 deletions(-)
+ create mode 100644 drivers/soc/aspeed/aspeed-udma.c
+ create mode 100644 drivers/tty/serial/8250/8250_aspeed.c
+ create mode 100644 include/linux/soc/aspeed/aspeed-udma.h
+
+diff --git a/arch/arm/boot/dts/aspeed-ast2600-evb.dts b/arch/arm/boot/dts/aspeed-ast2600-evb.dts
+index acbd1c947465..913749205c1d 100644
+--- a/arch/arm/boot/dts/aspeed-ast2600-evb.dts
++++ b/arch/arm/boot/dts/aspeed-ast2600-evb.dts
+@@ -180,11 +180,6 @@
+ };
+ };
+
+-&uart5 {
+- // Workaround for A0
+- compatible = "snps,dw-apb-uart";
+-};
+-
+ &i2c0 {
+ status = "okay";
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+index 02c837c3e2c4..210d2bbdf836 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+@@ -427,8 +427,6 @@
+
+ &uart5 {
+ status = "okay";
+- // Workaround for A0
+- compatible = "snps,dw-apb-uart";
+ };
+
+ &uart_routing {
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index e76dfb73430e..001ecf9ad33c 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -524,23 +524,22 @@
+ };
+
+ uart1: serial@1e783000 {
+- compatible = "ns16550a";
++ compatible = "aspeed,ast2600-uart";
+ reg = <0x1e783000 0x20>;
+- reg-shift = <2>;
+- reg-io-width = <4>;
+ interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART1CLK>;
+ resets = <&lpc_reset 4>;
+ no-loopback-test;
++ dma-mode;
++ dma-channel = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_txd1_default &pinctrl_rxd1_default>;
+ status = "disabled";
+ };
+
+ uart5: serial@1e784000 {
+- compatible = "ns16550a";
++ compatible = "aspeed,ast2600-uart";
+ reg = <0x1e784000 0x1000>;
+- reg-shift = <2>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART5CLK>;
+ no-loopback-test;
+@@ -754,10 +753,8 @@
+ };
+
+ uart2: serial@1e78d000 {
+- compatible = "ns16550a";
++ compatible = "aspeed,ast2600-uart";
+ reg = <0x1e78d000 0x20>;
+- reg-shift = <2>;
+- reg-io-width = <4>;
+ interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART2CLK>;
+ resets = <&lpc_reset 5>;
+@@ -768,10 +765,8 @@
+ };
+
+ uart3: serial@1e78e000 {
+- compatible = "ns16550a";
++ compatible = "aspeed,ast2600-uart";
+ reg = <0x1e78e000 0x20>;
+- reg-shift = <2>;
+- reg-io-width = <4>;
+ interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART3CLK>;
+ resets = <&lpc_reset 6>;
+@@ -782,10 +777,8 @@
+ };
+
+ uart4: serial@1e78f000 {
+- compatible = "ns16550a";
++ compatible = "aspeed,ast2600-uart";
+ reg = <0x1e78f000 0x20>;
+- reg-shift = <2>;
+- reg-io-width = <4>;
+ interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&syscon ASPEED_CLK_GATE_UART4CLK>;
+ resets = <&lpc_reset 7>;
+@@ -834,6 +827,13 @@
+ clocks = <&syscon ASPEED_CLK_GATE_FSICLK>;
+ status = "disabled";
+ };
++
++ udma: uart-dma@1e79e000 {
++ compatible = "aspeed,ast2600-udma";
++ reg = <0x1e79e000 0x400>;
++ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
+ };
+ };
+ };
+diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
+index d36ee43451a5..ca284ac2f0ab 100644
+--- a/drivers/soc/aspeed/Kconfig
++++ b/drivers/soc/aspeed/Kconfig
+@@ -90,6 +90,14 @@ config ASPEED_XDMA
+ SoCs. The XDMA engine can perform PCIe DMA operations between the BMC
+ and a host processor.
+
++config ASPEED_UDMA
++ tristate "Aspeed UDMA Engine Driver"
++ depends on SOC_ASPEED && REGMAP && MFD_SYSCON && HAS_DMA
++ help
++ Enable support for the Aspeed UDMA Engine found on the Aspeed AST2XXX
++ SOCs. The UDMA engine can perform UART DMA operations between the memory
++ buffer and the UART/VUART devices.
++
+ config ASPEED_VGA_SHAREDMEM
+ tristate "Aspeed VGA Shared memory"
+ help
+diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
+index f3afc32b58b9..e6248ecdeee3 100644
+--- a/drivers/soc/aspeed/Makefile
++++ b/drivers/soc/aspeed/Makefile
+@@ -8,5 +8,6 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
+ obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
+ obj-$(CONFIG_ASPEED_XDMA) += aspeed-xdma.o
++obj-$(CONFIG_ASPEED_UDMA) += aspeed-udma.o
+ obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o
+ obj-$(CONFIG_ASPEED_MCTP) += aspeed-mctp.o
+diff --git a/drivers/soc/aspeed/aspeed-udma.c b/drivers/soc/aspeed/aspeed-udma.c
+new file mode 100644
+index 000000000000..01b73ebe1880
+--- /dev/null
++++ b/drivers/soc/aspeed/aspeed-udma.c
+@@ -0,0 +1,441 @@
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/spinlock.h>
++#include <linux/soc/aspeed/aspeed-udma.h>
++
++#define DEVICE_NAME "aspeed-udma"
++
++/* UART DMA registers offset */
++#define UDMA_TX_DMA_EN 0x000
++#define UDMA_RX_DMA_EN 0x004
++#define UDMA_TIMEOUT_TIMER 0x00c
++#define UDMA_TX_DMA_RST 0x020
++#define UDMA_RX_DMA_RST 0x024
++#define UDMA_TX_DMA_INT_EN 0x030
++#define UDMA_TX_DMA_INT_STAT 0x034
++#define UDMA_RX_DMA_INT_EN 0x038
++#define UDMA_RX_DMA_INT_STAT 0x03c
++
++#define UDMA_CHX_OFF(x) ((x) * 0x20)
++#define UDMA_CHX_TX_RD_PTR(x) (0x040 + UDMA_CHX_OFF(x))
++#define UDMA_CHX_TX_WR_PTR(x) (0x044 + UDMA_CHX_OFF(x))
++#define UDMA_CHX_TX_BUF_BASE(x) (0x048 + UDMA_CHX_OFF(x))
++#define UDMA_CHX_TX_CTRL(x) (0x04c + UDMA_CHX_OFF(x))
++#define UDMA_TX_CTRL_TMOUT_DISABLE BIT(4)
++#define UDMA_TX_CTRL_BUFSZ_MASK GENMASK(3, 0)
++#define UDMA_TX_CTRL_BUFSZ_SHIFT 0
++#define UDMA_CHX_RX_RD_PTR(x) (0x050 + UDMA_CHX_OFF(x))
++#define UDMA_CHX_RX_WR_PTR(x) (0x054 + UDMA_CHX_OFF(x))
++#define UDMA_CHX_RX_BUF_BASE(x) (0x058 + UDMA_CHX_OFF(x))
++#define UDMA_CHX_RX_CTRL(x) (0x05c + UDMA_CHX_OFF(x))
++#define UDMA_RX_CTRL_TMOUT_DISABLE BIT(4)
++#define UDMA_RX_CTRL_BUFSZ_MASK GENMASK(3, 0)
++#define UDMA_RX_CTRL_BUFSZ_SHIFT 0
++
++#define UDMA_MAX_CHANNEL 14
++#define UDMA_TIMEOUT 0x200
++
++enum aspeed_udma_bufsz_code {
++ UDMA_BUFSZ_CODE_1KB,
++ UDMA_BUFSZ_CODE_4KB,
++ UDMA_BUFSZ_CODE_16KB,
++ UDMA_BUFSZ_CODE_64KB,
++
++ /*
++ * 128KB and above are supported ONLY for
++ * virtual UARTs. For physical UARTs, the
++ * size code is wrapped around at the 64K
++ * boundary.
++ */
++ UDMA_BUFSZ_CODE_128KB,
++ UDMA_BUFSZ_CODE_256KB,
++ UDMA_BUFSZ_CODE_512KB,
++ UDMA_BUFSZ_CODE_1024KB,
++ UDMA_BUFSZ_CODE_2048KB,
++ UDMA_BUFSZ_CODE_4096KB,
++ UDMA_BUFSZ_CODE_8192KB,
++ UDMA_BUFSZ_CODE_16384KB,
++};
++
++struct aspeed_udma_chan {
++ dma_addr_t dma_addr;
++
++ struct circ_buf *rb;
++ u32 rb_sz;
++
++ aspeed_udma_cb_t cb;
++ void *cb_arg;
++
++ bool dis_tmout;
++};
++
++struct aspeed_udma {
++ struct device *dev;
++ u8 __iomem *regs;
++ u32 irq;
++ struct aspeed_udma_chan tx_chs[UDMA_MAX_CHANNEL];
++ struct aspeed_udma_chan rx_chs[UDMA_MAX_CHANNEL];
++ spinlock_t lock;
++};
++
++struct aspeed_udma udma[1];
++
++static int aspeed_udma_get_bufsz_code(u32 buf_sz)
++{
++ switch (buf_sz) {
++ case 0x400:
++ return UDMA_BUFSZ_CODE_1KB;
++ case 0x1000:
++ return UDMA_BUFSZ_CODE_4KB;
++ case 0x4000:
++ return UDMA_BUFSZ_CODE_16KB;
++ case 0x10000:
++ return UDMA_BUFSZ_CODE_64KB;
++ case 0x20000:
++ return UDMA_BUFSZ_CODE_128KB;
++ case 0x40000:
++ return UDMA_BUFSZ_CODE_256KB;
++ case 0x80000:
++ return UDMA_BUFSZ_CODE_512KB;
++ case 0x100000:
++ return UDMA_BUFSZ_CODE_1024KB;
++ case 0x200000:
++ return UDMA_BUFSZ_CODE_2048KB;
++ case 0x400000:
++ return UDMA_BUFSZ_CODE_4096KB;
++ case 0x800000:
++ return UDMA_BUFSZ_CODE_8192KB;
++ case 0x1000000:
++ return UDMA_BUFSZ_CODE_16384KB;
++ default:
++ return -1;
++ }
++
++ return -1;
++}
++
++static u32 aspeed_udma_get_tx_rptr(u32 ch_no)
++{
++ return readl(udma->regs + UDMA_CHX_TX_RD_PTR(ch_no));
++}
++
++static u32 aspeed_udma_get_rx_wptr(u32 ch_no)
++{
++ return readl(udma->regs + UDMA_CHX_RX_WR_PTR(ch_no));
++}
++
++static void aspeed_udma_set_ptr(u32 ch_no, u32 ptr, bool is_tx)
++{
++ writel(ptr, udma->regs +
++ ((is_tx) ?
++ UDMA_CHX_TX_WR_PTR(ch_no) :
++ UDMA_CHX_RX_RD_PTR(ch_no)));
++}
++
++void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr)
++{
++ aspeed_udma_set_ptr(ch_no, wptr, true);
++}
++EXPORT_SYMBOL(aspeed_udma_set_tx_wptr);
++
++void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr)
++{
++ aspeed_udma_set_ptr(ch_no, rptr, false);
++}
++EXPORT_SYMBOL(aspeed_udma_set_rx_rptr);
++
++static int aspeed_udma_free_chan(u32 ch_no, bool is_tx)
++{
++ u32 reg;
++ unsigned long flags;
++
++ if (ch_no > UDMA_MAX_CHANNEL)
++ return -EINVAL;
++
++ spin_lock_irqsave(&udma->lock, flags);
++
++ reg = readl(udma->regs +
++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN));
++ reg &= ~(0x1 << ch_no);
++
++ writel(reg, udma->regs +
++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN));
++
++ spin_unlock_irqrestore(&udma->lock, flags);
++
++ return 0;
++}
++
++int aspeed_udma_free_tx_chan(u32 ch_no)
++{
++ return aspeed_udma_free_chan(ch_no, true);
++}
++EXPORT_SYMBOL(aspeed_udma_free_tx_chan);
++
++int aspeed_udma_free_rx_chan(u32 ch_no)
++{
++ return aspeed_udma_free_chan(ch_no, false);
++}
++EXPORT_SYMBOL(aspeed_udma_free_rx_chan);
++
++static int aspeed_udma_request_chan(u32 ch_no, dma_addr_t addr,
++ struct circ_buf *rb, u32 rb_sz,
++ aspeed_udma_cb_t cb, void *id, bool dis_tmout, bool is_tx)
++{
++ int retval = 0;
++ int rbsz_code;
++
++ u32 reg;
++ unsigned long flags;
++ struct aspeed_udma_chan *ch;
++
++ if (ch_no > UDMA_MAX_CHANNEL) {
++ retval = -EINVAL;
++ goto out;
++ }
++
++ if (IS_ERR_OR_NULL(rb) || IS_ERR_OR_NULL(rb->buf)) {
++ retval = -EINVAL;
++ goto out;
++ }
++
++ rbsz_code = aspeed_udma_get_bufsz_code(rb_sz);
++ if (rbsz_code < 0) {
++ retval = -EINVAL;
++ goto out;
++ }
++
++ spin_lock_irqsave(&udma->lock, flags);
++
++ if (is_tx) {
++ reg = readl(udma->regs + UDMA_TX_DMA_INT_EN);
++ if (reg & (0x1 << ch_no)) {
++ retval = -EBUSY;
++ goto unlock_n_out;
++ }
++
++ reg |= (0x1 << ch_no);
++ writel(reg, udma->regs + UDMA_TX_DMA_INT_EN);
++
++ reg = readl(udma->regs + UDMA_CHX_TX_CTRL(ch_no));
++ reg |= (dis_tmout) ? UDMA_TX_CTRL_TMOUT_DISABLE : 0;
++ reg |= (rbsz_code << UDMA_TX_CTRL_BUFSZ_SHIFT) & UDMA_TX_CTRL_BUFSZ_MASK;
++ writel(reg, udma->regs + UDMA_CHX_TX_CTRL(ch_no));
++
++ writel(addr, udma->regs + UDMA_CHX_TX_BUF_BASE(ch_no));
++ }
++ else {
++ reg = readl(udma->regs + UDMA_RX_DMA_INT_EN);
++ if (reg & (0x1 << ch_no)) {
++ retval = -EBUSY;
++ goto unlock_n_out;
++ }
++
++ reg |= (0x1 << ch_no);
++ writel(reg, udma->regs + UDMA_RX_DMA_INT_EN);
++
++ reg = readl(udma->regs + UDMA_CHX_RX_CTRL(ch_no));
++ reg |= (dis_tmout) ? UDMA_RX_CTRL_TMOUT_DISABLE : 0;
++ reg |= (rbsz_code << UDMA_RX_CTRL_BUFSZ_SHIFT) & UDMA_RX_CTRL_BUFSZ_MASK;
++ writel(reg, udma->regs + UDMA_CHX_RX_CTRL(ch_no));
++
++ writel(addr, udma->regs + UDMA_CHX_RX_BUF_BASE(ch_no));
++ }
++
++ ch = (is_tx) ? &udma->tx_chs[ch_no] : &udma->rx_chs[ch_no];
++ ch->rb = rb;
++ ch->rb_sz = rb_sz;
++ ch->cb = cb;
++ ch->cb_arg = id;
++ ch->dma_addr = addr;
++ ch->dis_tmout = dis_tmout;
++
++unlock_n_out:
++ spin_unlock_irqrestore(&udma->lock, flags);
++out:
++ return 0;
++}
++
++int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr,
++ struct circ_buf *rb, u32 rb_sz,
++ aspeed_udma_cb_t cb, void *id, bool dis_tmout)
++{
++ return aspeed_udma_request_chan(ch_no, addr, rb, rb_sz, cb, id,
++ dis_tmout, true);
++}
++EXPORT_SYMBOL(aspeed_udma_request_tx_chan);
++
++int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr,
++ struct circ_buf *rb, u32 rb_sz,
++ aspeed_udma_cb_t cb, void *id, bool dis_tmout)
++{
++ return aspeed_udma_request_chan(ch_no, addr, rb, rb_sz, cb, id,
++ dis_tmout, false);
++}
++EXPORT_SYMBOL(aspeed_udma_request_rx_chan);
++
++static void aspeed_udma_chan_ctrl(u32 ch_no, u32 op, bool is_tx)
++{
++ unsigned long flags;
++ u32 reg_en, reg_rst;
++ u32 reg_en_off = (is_tx) ? UDMA_TX_DMA_EN : UDMA_RX_DMA_EN;
++ u32 reg_rst_off = (is_tx) ? UDMA_TX_DMA_RST : UDMA_TX_DMA_RST;
++
++ if (ch_no > UDMA_MAX_CHANNEL)
++ return;
++
++ spin_lock_irqsave(&udma->lock, flags);
++
++ reg_en = readl(udma->regs + reg_en_off);
++ reg_rst = readl(udma->regs + reg_rst_off);
++
++ switch (op) {
++ case ASPEED_UDMA_OP_ENABLE:
++ reg_en |= (0x1 << ch_no);
++ writel(reg_en, udma->regs + reg_en_off);
++ break;
++ case ASPEED_UDMA_OP_DISABLE:
++ reg_en &= ~(0x1 << ch_no);
++ writel(reg_en, udma->regs + reg_en_off);
++ break;
++ case ASPEED_UDMA_OP_RESET:
++ reg_en &= ~(0x1 << ch_no);
++ writel(reg_en, udma->regs + reg_en_off);
++ reg_rst |= (0x1 << ch_no);
++ writel(reg_rst, udma->regs + reg_rst_off);
++ reg_rst &= ~(0x1 << ch_no);
++ writel(reg_rst, udma->regs + reg_rst_off);
++ break;
++ default:
++ break;
++ }
++
++ spin_unlock_irqrestore(&udma->lock, flags);
++}
++
++void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op)
++{
++ aspeed_udma_chan_ctrl(ch_no, op, true);
++}
++EXPORT_SYMBOL(aspeed_udma_tx_chan_ctrl);
++
++void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op)
++{
++ aspeed_udma_chan_ctrl(ch_no, op, false);
++}
++EXPORT_SYMBOL(aspeed_udma_rx_chan_ctrl);
++
++static irqreturn_t aspeed_udma_isr(int irq, void *arg)
++{
++ u32 bit;
++ unsigned long tx_stat = readl(udma->regs + UDMA_TX_DMA_INT_STAT);
++ unsigned long rx_stat = readl(udma->regs + UDMA_RX_DMA_INT_STAT);
++
++ if (udma != (struct aspeed_udma *)arg)
++ return IRQ_NONE;
++
++ if (tx_stat == 0 && rx_stat == 0)
++ return IRQ_NONE;
++
++ for_each_set_bit(bit, &tx_stat, UDMA_MAX_CHANNEL) {
++ writel((0x1 << bit), udma->regs + UDMA_TX_DMA_INT_STAT);
++ if (udma->tx_chs[bit].cb)
++ udma->tx_chs[bit].cb(aspeed_udma_get_tx_rptr(bit),
++ udma->tx_chs[bit].cb_arg);
++ }
++
++ for_each_set_bit(bit, &rx_stat, UDMA_MAX_CHANNEL) {
++ writel((0x1 << bit), udma->regs + UDMA_RX_DMA_INT_STAT);
++ if (udma->rx_chs[bit].cb)
++ udma->rx_chs[bit].cb(aspeed_udma_get_rx_wptr(bit),
++ udma->rx_chs[bit].cb_arg);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static int aspeed_udma_probe(struct platform_device *pdev)
++{
++ int i, rc;
++ struct resource *res;
++ struct device *dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (IS_ERR_OR_NULL(res)) {
++ dev_err(dev, "failed to get register base\n");
++ return -ENODEV;
++ }
++
++ udma->regs = devm_ioremap_resource(dev, res);
++ if (IS_ERR_OR_NULL(udma->regs)) {
++ dev_err(dev, "failed to map registers\n");
++ return PTR_ERR(udma->regs);
++ }
++
++ /* disable for safety */
++ writel(0x0, udma->regs + UDMA_TX_DMA_EN);
++ writel(0x0, udma->regs + UDMA_RX_DMA_EN);
++
++ udma->irq = platform_get_irq(pdev, 0);
++ if (udma->irq < 0) {
++ dev_err(dev, "failed to get IRQ number\n");
++ return -ENODEV;
++ }
++
++ rc = devm_request_irq(dev, udma->irq, aspeed_udma_isr,
++ IRQF_SHARED, DEVICE_NAME, udma);
++ if (rc) {
++ dev_err(dev, "failed to request IRQ handler\n");
++ return rc;
++ }
++
++ for (i = 0; i < UDMA_MAX_CHANNEL; ++i) {
++ writel(0, udma->regs + UDMA_CHX_TX_WR_PTR(i));
++ writel(0, udma->regs + UDMA_CHX_RX_RD_PTR(i));
++ }
++
++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_RST);
++ writel(0x0, udma->regs + UDMA_TX_DMA_RST);
++
++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_RST);
++ writel(0x0, udma->regs + UDMA_RX_DMA_RST);
++
++ writel(0x0, udma->regs + UDMA_TX_DMA_INT_EN);
++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_INT_STAT);
++ writel(0x0, udma->regs + UDMA_RX_DMA_INT_EN);
++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_INT_STAT);
++
++ writel(UDMA_TIMEOUT, udma->regs + UDMA_TIMEOUT_TIMER);
++
++ spin_lock_init(&udma->lock);
++
++ dev_set_drvdata(dev, udma);
++
++ return 0;
++}
++
++static const struct of_device_id aspeed_udma_match[] = {
++ { .compatible = "aspeed,ast2500-udma" },
++ { .compatible = "aspeed,ast2600-udma" },
++};
++
++static struct platform_driver aspeed_udma_driver = {
++ .driver = {
++ .name = DEVICE_NAME,
++ .of_match_table = aspeed_udma_match,
++
++ },
++ .probe = aspeed_udma_probe,
++};
++
++module_platform_driver(aspeed_udma_driver);
++
++MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Aspeed UDMA Engine Driver");
+diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/8250_aspeed.c
+new file mode 100644
+index 000000000000..1dc798298fca
+--- /dev/null
++++ b/drivers/tty/serial/8250/8250_aspeed.c
+@@ -0,0 +1,494 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) ASPEED Technology Inc.
++ */
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/serial_8250.h>
++#include <linux/serial_reg.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/dma-mapping.h>
++#include <linux/circ_buf.h>
++#include <linux/tty_flip.h>
++#include <linux/pm_runtime.h>
++#include <linux/soc/aspeed/aspeed-udma.h>
++
++#include "8250.h"
++
++#define DEVICE_NAME "aspeed-uart"
++
++/* offsets for the aspeed virtual uart registers */
++#define VUART_GCRA 0x20
++#define VUART_GCRA_VUART_EN BIT(0)
++#define VUART_GCRA_SIRQ_POLARITY BIT(1)
++#define VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
++#define VUART_GCRB 0x24
++#define VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
++#define VUART_GCRB_HOST_SIRQ_SHIFT 4
++#define VUART_ADDRL 0x28
++#define VUART_ADDRH 0x2c
++
++#define DMA_TX_BUFSZ PAGE_SIZE
++#define DMA_RX_BUFSZ (64 * 1024)
++
++struct uart_ops ast8250_pops;
++
++struct ast8250_vuart {
++ u32 port;
++ u32 sirq;
++ u32 sirq_pol;
++};
++
++struct ast8250_udma {
++ u32 ch;
++
++ u32 tx_rbsz;
++ u32 rx_rbsz;
++
++ dma_addr_t tx_addr;
++ dma_addr_t rx_addr;
++
++ struct circ_buf *tx_rb;
++ struct circ_buf *rx_rb;
++
++ bool tx_tmout_dis;
++ bool rx_tmout_dis;
++};
++
++struct ast8250_data {
++ int line;
++
++ u8 __iomem *regs;
++
++ bool is_vuart;
++ bool use_dma;
++
++ struct reset_control *rst;
++ struct clk *clk;
++
++ struct ast8250_vuart vuart;
++ struct ast8250_udma dma;
++};
++
++static void ast8250_dma_tx_complete(int tx_rb_rptr, void *id)
++{
++ u32 count;
++ unsigned long flags;
++ struct uart_port *port = (struct uart_port*)id;
++ struct ast8250_data *data = port->private_data;
++
++ spin_lock_irqsave(&port->lock, flags);
++
++ count = CIRC_CNT(tx_rb_rptr, port->state->xmit.tail, data->dma.tx_rbsz);
++ port->state->xmit.tail = tx_rb_rptr;
++ port->icount.tx += count;
++
++ if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)
++ uart_write_wakeup(port);
++
++ spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static void ast8250_dma_rx_complete(int rx_rb_wptr, void *id)
++{
++ unsigned long flags;
++ struct uart_port *up = (struct uart_port*)id;
++ struct tty_port *tp = &up->state->port;
++ struct ast8250_data *data = up->private_data;
++ struct ast8250_udma *dma = &data->dma;
++ struct circ_buf *rx_rb = dma->rx_rb;
++ u32 rx_rbsz = dma->rx_rbsz;
++ u32 count = 0;
++
++ spin_lock_irqsave(&up->lock, flags);
++
++ rx_rb->head = rx_rb_wptr;
++
++ dma_sync_single_for_cpu(up->dev,
++ dma->rx_addr, dma->rx_rbsz, DMA_FROM_DEVICE);
++
++ while (CIRC_CNT(rx_rb->head, rx_rb->tail, rx_rbsz)) {
++ count = CIRC_CNT_TO_END(rx_rb->head, rx_rb->tail, rx_rbsz);
++
++ tty_insert_flip_string(tp, rx_rb->buf + rx_rb->tail, count);
++
++ rx_rb->tail += count;
++ rx_rb->tail %= rx_rbsz;
++
++ up->icount.rx += count;
++ }
++
++ if (count) {
++ aspeed_udma_set_rx_rptr(data->dma.ch, rx_rb->tail);
++ tty_flip_buffer_push(tp);
++ }
++
++ spin_unlock_irqrestore(&up->lock, flags);
++}
++
++static void ast8250_dma_start_tx(struct uart_port *port)
++{
++ struct ast8250_data *data = port->private_data;
++ struct ast8250_udma *dma = &data->dma;
++ struct circ_buf *tx_rb = dma->tx_rb;
++
++ dma_sync_single_for_device(port->dev,
++ dma->tx_addr, dma->tx_rbsz, DMA_TO_DEVICE);
++
++ aspeed_udma_set_tx_wptr(dma->ch, tx_rb->head);
++}
++
++static void ast8250_dma_pops_hook(struct uart_port *port)
++{
++ static int first = 1;
++
++ if (first) {
++ ast8250_pops = *port->ops;
++ ast8250_pops.start_tx = ast8250_dma_start_tx;
++ }
++
++ first = 0;
++ port->ops = &ast8250_pops;
++}
++
++static void ast8250_vuart_init(struct ast8250_data *data)
++{
++ u8 reg;
++ struct ast8250_vuart *vuart = &data->vuart;
++
++ /* IO port address */
++ writeb((u8)(vuart->port >> 0), data->regs + VUART_ADDRL);
++ writeb((u8)(vuart->port >> 8), data->regs + VUART_ADDRH);
++
++ /* SIRQ number */
++ reg = readb(data->regs + VUART_GCRB);
++ reg &= ~VUART_GCRB_HOST_SIRQ_MASK;
++ reg |= ((vuart->sirq << VUART_GCRB_HOST_SIRQ_SHIFT) & VUART_GCRB_HOST_SIRQ_MASK);
++ writeb(reg, data->regs + VUART_GCRB);
++
++ /* SIRQ polarity */
++ reg = readb(data->regs + VUART_GCRA);
++ if (vuart->sirq_pol)
++ reg |= VUART_GCRA_SIRQ_POLARITY;
++ else
++ reg &= ~VUART_GCRA_SIRQ_POLARITY;
++ writeb(reg, data->regs + VUART_GCRA);
++}
++
++static void ast8250_vuart_set_host_tx_discard(struct ast8250_data *data, bool discard)
++{
++ u8 reg;
++
++ reg = readb(data->regs + VUART_GCRA);
++ if (discard)
++ reg &= ~VUART_GCRA_DISABLE_HOST_TX_DISCARD;
++ else
++ reg |= VUART_GCRA_DISABLE_HOST_TX_DISCARD;
++ writeb(reg, data->regs + VUART_GCRA);
++}
++
++static void ast8250_vuart_set_enable(struct ast8250_data *data, bool enable)
++{
++ u8 reg;
++
++ reg = readb(data->regs + VUART_GCRA);
++ if (enable)
++ reg |= VUART_GCRA_VUART_EN;
++ else
++ reg &= ~VUART_GCRA_VUART_EN;
++ writeb(reg, data->regs + VUART_GCRA);
++}
++
++static int ast8250_handle_irq(struct uart_port *port)
++{
++ u32 iir = port->serial_in(port, UART_IIR);
++ return serial8250_handle_irq(port, iir);
++}
++
++static int ast8250_startup(struct uart_port *port)
++{
++ int rc = 0;
++ struct ast8250_data *data = port->private_data;
++ struct ast8250_udma *dma;
++
++ if (data->is_vuart)
++ ast8250_vuart_set_host_tx_discard(data, false);
++
++ if (data->use_dma) {
++ dma = &data->dma;
++
++ dma->tx_rbsz = DMA_TX_BUFSZ;
++ dma->rx_rbsz = DMA_RX_BUFSZ;
++
++ /*
++ * We take the xmit buffer passed from upper layers as
++ * the DMA TX buffer and allocate a new buffer for the
++ * RX use.
++ *
++ * To keep the TX/RX operation consistency, we use the
++ * streaming DMA interface instead of the coherent one
++ */
++ dma->tx_rb = &port->state->xmit;
++ dma->rx_rb->buf = kzalloc(data->dma.rx_rbsz, GFP_KERNEL);
++ if (IS_ERR_OR_NULL(dma->rx_rb->buf)) {
++ dev_err(port->dev, "failed to allcoate RX DMA buffer\n");
++ rc = -ENOMEM;
++ goto out;
++ }
++
++ dma->tx_addr = dma_map_single(port->dev, dma->tx_rb->buf,
++ dma->tx_rbsz, DMA_TO_DEVICE);
++ if (dma_mapping_error(port->dev, dma->tx_addr)) {
++ dev_err(port->dev, "failed to map streaming TX DMA region\n");
++ rc = -ENOMEM;
++ goto free_dma_n_out;
++ }
++
++ dma->rx_addr = dma_map_single(port->dev, dma->rx_rb->buf,
++ dma->rx_rbsz, DMA_FROM_DEVICE);
++ if (dma_mapping_error(port->dev, dma->rx_addr)) {
++ dev_err(port->dev, "failed to map streaming RX DMA region\n");
++ rc = -ENOMEM;
++ goto free_dma_n_out;
++ }
++
++ rc = aspeed_udma_request_tx_chan(dma->ch, dma->tx_addr,
++ dma->tx_rb, dma->tx_rbsz, ast8250_dma_tx_complete, port, dma->tx_tmout_dis);
++ if (rc) {
++ dev_err(port->dev, "failed to request DMA TX channel\n");
++ goto free_dma_n_out;
++ }
++
++ rc = aspeed_udma_request_rx_chan(dma->ch, dma->rx_addr,
++ dma->rx_rb, dma->rx_rbsz, ast8250_dma_rx_complete, port, dma->tx_tmout_dis);
++ if (rc) {
++ dev_err(port->dev, "failed to request DMA RX channel\n");
++ goto free_dma_n_out;
++ }
++
++ ast8250_dma_pops_hook(port);
++
++ aspeed_udma_tx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_ENABLE);
++ aspeed_udma_rx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_ENABLE);
++ }
++
++ memset(&port->icount, 0, sizeof(port->icount));
++ return serial8250_do_startup(port);
++
++free_dma_n_out:
++ kfree(dma->rx_rb->buf);
++out:
++ return rc;
++}
++
++static void ast8250_shutdown(struct uart_port *port)
++{
++ int rc;
++ struct ast8250_data *data = port->private_data;
++ struct ast8250_udma *dma;
++
++ if (data->use_dma) {
++ dma = &data->dma;
++
++ aspeed_udma_tx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_DISABLE);
++ aspeed_udma_rx_chan_ctrl(dma->ch, ASPEED_UDMA_OP_DISABLE);
++
++ rc = aspeed_udma_free_tx_chan(dma->ch);
++ if (rc)
++ dev_err(port->dev, "failed to free DMA TX channel, rc=%d\n", rc);
++
++ rc = aspeed_udma_free_rx_chan(dma->ch);
++ if (rc)
++ dev_err(port->dev, "failed to free DMA TX channel, rc=%d\n", rc);
++
++ dma_unmap_single(port->dev, dma->tx_addr,
++ dma->tx_rbsz, DMA_TO_DEVICE);
++ dma_unmap_single(port->dev, dma->rx_addr,
++ dma->rx_rbsz, DMA_FROM_DEVICE);
++
++ if (dma->rx_rb->buf)
++ kfree(dma->rx_rb->buf);
++ }
++
++ if (data->is_vuart)
++ ast8250_vuart_set_host_tx_discard(data, true);
++
++ serial8250_do_shutdown(port);
++}
++
++static int __maybe_unused ast8250_suspend(struct device *dev)
++{
++ struct ast8250_data *data = dev_get_drvdata(dev);
++ serial8250_suspend_port(data->line);
++ return 0;
++}
++
++static int __maybe_unused ast8250_resume(struct device *dev)
++{
++ struct ast8250_data *data = dev_get_drvdata(dev);
++ serial8250_resume_port(data->line);
++ return 0;
++}
++
++static int ast8250_probe(struct platform_device *pdev)
++{
++ int rc;
++ struct uart_8250_port uart = {};
++ struct uart_port *port = &uart.port;
++ struct device *dev = &pdev->dev;
++ struct ast8250_data *data;
++
++ struct resource *res;
++ u32 irq;
++
++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
++ if (data == NULL)
++ return -ENOMEM;
++
++ data->dma.rx_rb = devm_kzalloc(dev, sizeof(data->dma.rx_rb), GFP_KERNEL);
++ if (data->dma.rx_rb == NULL)
++ return -ENOMEM;
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ if (irq != -EPROBE_DEFER)
++ dev_err(dev, "failed to get IRQ number\n");
++ return irq;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (res == NULL) {
++ dev_err(dev, "failed to get register base\n");
++ return -ENODEV;
++ }
++
++ data->regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(data->regs)) {
++ dev_err(dev, "failed to map registers\n");
++ return PTR_ERR(data->regs);
++ }
++
++ data->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(data->clk)) {
++ dev_err(dev, "failed to get clocks\n");
++ return -ENODEV;
++ }
++
++ rc = clk_prepare_enable(data->clk);
++ if (rc) {
++ dev_err(dev, "failed to enable clock\n");
++ return rc;
++ }
++
++ data->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
++ if (!IS_ERR(data->rst))
++ reset_control_deassert(data->rst);
++
++ data->is_vuart = of_property_read_bool(dev->of_node, "virtual");
++ if (data->is_vuart) {
++ rc = of_property_read_u32(dev->of_node, "port", &data->vuart.port);
++ if (rc) {
++ dev_err(dev, "failed to get VUART port address\n");
++ return -ENODEV;
++ }
++
++ rc = of_property_read_u32(dev->of_node, "sirq", &data->vuart.sirq);
++ if (rc) {
++ dev_err(dev, "failed to get VUART SIRQ number\n");
++ return -ENODEV;
++ }
++
++ rc = of_property_read_u32(dev->of_node, "sirq-polarity", &data->vuart.sirq_pol);
++ if (rc) {
++ dev_err(dev, "failed to get VUART SIRQ polarity\n");
++ return -ENODEV;
++ }
++
++ ast8250_vuart_init(data);
++ ast8250_vuart_set_host_tx_discard(data, true);
++ ast8250_vuart_set_enable(data, true);
++ }
++
++ data->use_dma = of_property_read_bool(dev->of_node, "dma-mode");
++ if (data->use_dma) {
++ rc = of_property_read_u32(dev->of_node, "dma-channel", &data->dma.ch);
++ if (rc) {
++ dev_err(dev, "failed to get DMA channel\n");
++ return -ENODEV;
++ }
++
++ data->dma.tx_tmout_dis = of_property_read_bool(dev->of_node, "dma-tx-timeout-disable");
++ data->dma.rx_tmout_dis = of_property_read_bool(dev->of_node, "dma-rx-timeout-disable");
++ }
++
++ spin_lock_init(&port->lock);
++ port->dev = dev;
++ port->type = PORT_16550;
++ port->irq = irq;
++ port->line = of_alias_get_id(dev->of_node, "serial");
++ port->handle_irq = ast8250_handle_irq;
++ port->mapbase = res->start;
++ port->mapsize = resource_size(res);
++ port->membase = data->regs;
++ port->uartclk = clk_get_rate(data->clk);
++ port->regshift = 2;
++ port->iotype = UPIO_MEM32;
++ port->flags = UPF_FIXED_PORT | UPF_SHARE_IRQ;
++ port->startup = ast8250_startup;
++ port->shutdown = ast8250_shutdown;
++ port->private_data = data;
++
++ data->line = serial8250_register_8250_port(&uart);
++ if (data->line < 0) {
++ dev_err(dev, "failed to register 8250 port\n");
++ return data->line;
++ }
++
++ pm_runtime_set_active(&pdev->dev);
++ pm_runtime_enable(&pdev->dev);
++
++ platform_set_drvdata(pdev, data);
++ return 0;
++}
++
++static int ast8250_remove(struct platform_device *pdev)
++{
++ struct ast8250_data *data = platform_get_drvdata(pdev);
++
++ if (data->is_vuart)
++ ast8250_vuart_set_enable(data, false);
++
++ serial8250_unregister_port(data->line);
++ return 0;
++}
++
++static const struct dev_pm_ops ast8250_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(ast8250_suspend, ast8250_resume)
++};
++
++static const struct of_device_id ast8250_of_match[] = {
++ { .compatible = "aspeed,ast2500-uart" },
++ { .compatible = "aspeed,ast2600-uart" },
++};
++
++static struct platform_driver ast8250_platform_driver = {
++ .driver = {
++ .name = DEVICE_NAME,
++ .pm = &ast8250_pm_ops,
++ .of_match_table = ast8250_of_match,
++ },
++ .probe = ast8250_probe,
++ .remove = ast8250_remove,
++};
++
++module_platform_driver(ast8250_platform_driver);
++
++MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Aspeed UART Driver");
+diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
+index d1b3c2373fa4..b62a972c9c97 100644
+--- a/drivers/tty/serial/8250/Kconfig
++++ b/drivers/tty/serial/8250/Kconfig
+@@ -249,6 +249,14 @@ config SERIAL_8250_ACCENT
+ To compile this driver as a module, choose M here: the module
+ will be called 8250_accent.
+
++config SERIAL_8250_ASPEED
++ tristate "Aspeed serial port support"
++ depends on SERIAL_8250 && ARCH_ASPEED
++ select ASPEED_UDMA
++ help
++ If you have a system using an Aspeed ASTXXXX SoCs and wish to make use
++ of its UARTs, say Y to this option. If unsure, say N.
++
+ config SERIAL_8250_ASPEED_VUART
+ tristate "Aspeed Virtual UART"
+ depends on SERIAL_8250
+diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
+index b9bcd73c8997..310375fe8d7e 100644
+--- a/drivers/tty/serial/8250/Makefile
++++ b/drivers/tty/serial/8250/Makefile
+@@ -16,6 +16,7 @@ obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o
+ obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
+ obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o
+ obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
++obj-$(CONFIG_SERIAL_8250_ASPEED) += 8250_aspeed.o
+ obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o
+ obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o
+ obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o
+diff --git a/include/linux/soc/aspeed/aspeed-udma.h b/include/linux/soc/aspeed/aspeed-udma.h
+new file mode 100644
+index 000000000000..33acea745f1c
+--- /dev/null
++++ b/include/linux/soc/aspeed/aspeed-udma.h
+@@ -0,0 +1,30 @@
++#ifndef __ASPEED_UDMA_H__
++#define __ASPEED_UDMA_H__
++
++#include <linux/circ_buf.h>
++
++typedef void (*aspeed_udma_cb_t)(int rb_rwptr, void *id);
++
++enum aspeed_udma_ops {
++ ASPEED_UDMA_OP_ENABLE,
++ ASPEED_UDMA_OP_DISABLE,
++ ASPEED_UDMA_OP_RESET,
++};
++
++void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr);
++void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr);
++
++void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op);
++void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op);
++
++int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr,
++ struct circ_buf *rb, u32 rb_sz,
++ aspeed_udma_cb_t cb, void *id, bool en_tmout);
++int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr,
++ struct circ_buf *rb, u32 rb_sz,
++ aspeed_udma_cb_t cb, void *id, bool en_tmout);
++
++int aspeed_udma_free_tx_chan(u32 ch_no);
++int aspeed_udma_free_rx_chan(u32 ch_no);
++
++#endif
+--
+2.17.1
+