--- v10
+++ v7
@@ -4,48 +4,21 @@
Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
- drivers/dma/mediatek/Kconfig | 11 +
- drivers/dma/mediatek/Makefile | 1 +
- drivers/dma/mediatek/mtk-uart-apdma.c | 669 +++++++++++++++++++++++++++++++++
- 3 files changed, 681 insertions(+)
- create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c
+ drivers/dma/mediatek/8250_mtk_dma.c | 694 +++++++++++++++++++++++++++++++++++
+ drivers/dma/mediatek/Kconfig | 11 +
+ drivers/dma/mediatek/Makefile | 1 +
+ 3 files changed, 706 insertions(+)
+ create mode 100644 drivers/dma/mediatek/8250_mtk_dma.c
-diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig
-index 680fc05..ac49eb6 100644
---- a/drivers/dma/mediatek/Kconfig
-+++ b/drivers/dma/mediatek/Kconfig
-@@ -24,3 +24,14 @@ config MTK_CQDMA
-
- This controller provides the channels which is dedicated to
- memory-to-memory transfer to offload from CPU.
-+
-+config MTK_UART_APDMA
-+ tristate "MediaTek SoCs APDMA support for UART"
-+ depends on OF && SERIAL_8250_MT6577
-+ select DMA_ENGINE
-+ select DMA_VIRTUAL_CHANNELS
-+ help
-+ Support for the UART DMA engine found on MediaTek MTK SoCs.
-+ When SERIAL_8250_MT6577 is enabled, and if you want to use DMA,
-+ you can enable the config. The DMA engine can only be used
-+ with MediaTek SoCs.
-diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile
-index 41bb381..61a6d29 100644
---- a/drivers/dma/mediatek/Makefile
-+++ b/drivers/dma/mediatek/Makefile
-@@ -1,2 +1,3 @@
-+obj-$(CONFIG_MTK_UART_APDMA) += mtk-uart-apdma.o
- obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
- obj-$(CONFIG_MTK_CQDMA) += mtk-cqdma.o
-diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c
+diff --git a/drivers/dma/mediatek/8250_mtk_dma.c b/drivers/dma/mediatek/8250_mtk_dma.c
new file mode 100644
-index 0000000..427db69
+index 0000000..c4090f2
--- /dev/null
-+++ b/drivers/dma/mediatek/mtk-uart-apdma.c
-@@ -0,0 +1,669 @@
++++ b/drivers/dma/mediatek/8250_mtk_dma.c
+@@ -0,0 +1,694 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
-+ * MediaTek Uart APDMA driver.
++ * MediaTek 8250 DMA driver.
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Long Cheng <long.cheng@mediatek.com>
@@ -57,33 +30,33 @@
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
-+#include <linux/iopoll.h>
++#include <linux/list.h>
+#include <linux/kernel.h>
-+#include <linux/list.h>
+#include <linux/module.h>
++#include <linux/of_dma.h>
+#include <linux/of_device.h>
-+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
-+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
++#include <linux/pm_runtime.h>
++#include <linux/iopoll.h>
+
+#include "../virt-dma.h"
+
-+/* The default number of virtual channel */
-+#define MTK_UART_APDMA_NR_VCHANS 8
++#define MTK_UART_APDMA_CHANNELS (CONFIG_SERIAL_8250_NR_UARTS * 2)
+
+#define VFF_EN_B BIT(0)
+#define VFF_STOP_B BIT(0)
+#define VFF_FLUSH_B BIT(0)
+#define VFF_4G_SUPPORT_B BIT(0)
-+#define VFF_RX_INT_EN0_B BIT(0) /* rx valid size >= vff thre */
++#define VFF_RX_INT_EN0_B BIT(0) /*rx valid size >= vff thre*/
+#define VFF_RX_INT_EN1_B BIT(1)
-+#define VFF_TX_INT_EN_B BIT(0) /* tx left size >= vff thre */
++#define VFF_TX_INT_EN_B BIT(0) /*tx left size >= vff thre*/
+#define VFF_WARM_RST_B BIT(0)
-+#define VFF_RX_INT_CLR_B (BIT(0) | BIT(1))
-+#define VFF_TX_INT_CLR_B 0
++#define VFF_RX_INT_FLAG_CLR_B (BIT(0) | BIT(1))
++#define VFF_TX_INT_FLAG_CLR_B 0
+#define VFF_STOP_CLR_B 0
++#define VFF_FLUSH_CLR_B 0
+#define VFF_INT_EN_CLR_B 0
+#define VFF_4G_SUPPORT_CLR_B 0
+
@@ -92,9 +65,9 @@
+/* interrupt trigger level for rx */
+#define VFF_RX_THRE(n) ((n) * 3 / 4)
+
-+#define VFF_RING_SIZE 0xffffU
-+/* invert this bit when wrap ring head again */
-+#define VFF_RING_WRAP 0x10000U
++#define MTK_UART_APDMA_RING_SIZE 0xffffU
++/* invert this bit when wrap ring head again*/
++#define MTK_UART_APDMA_RING_WRAP 0x10000U
+
+#define VFF_INT_FLAG 0x00
+#define VFF_INT_EN 0x04
@@ -107,9 +80,9 @@
+#define VFF_THRE 0x28
+#define VFF_WPT 0x2c
+#define VFF_RPT 0x30
-+/* TX: the buffer size HW can read. RX: the buffer size SW can read. */
++/*TX: the buffer size HW can read. RX: the buffer size SW can read.*/
+#define VFF_VALID_SIZE 0x3c
-+/* TX: the buffer size SW can write. RX: the buffer size HW can write. */
++/*TX: the buffer size SW can write. RX: the buffer size HW can write.*/
+#define VFF_LEFT_SIZE 0x40
+#define VFF_DEBUG_STATUS 0x50
+#define VFF_4G_SUPPORT 0x54
@@ -118,8 +91,7 @@
+ struct dma_device ddev;
+ struct clk *clk;
+ bool support_33bits;
-+ unsigned int dma_requests;
-+ unsigned int *dma_irq;
++ unsigned int dma_irq[MTK_UART_APDMA_CHANNELS];
+};
+
+struct mtk_uart_apdma_desc {
@@ -134,8 +106,6 @@
+ void __iomem *base;
+ struct mtk_uart_apdma_desc *desc;
+
-+ enum dma_transfer_direction dir;
-+
+ bool requested;
+
+ unsigned int rx_status;
@@ -158,13 +128,14 @@
+ return container_of(t, struct mtk_uart_apdma_desc, vd.tx);
+}
+
-+static void mtk_uart_apdma_write(struct mtk_chan *c,
++static void mtk_uart_apdma_chan_write(struct mtk_chan *c,
+ unsigned int reg, unsigned int val)
+{
+ writel(val, c->base + reg);
+}
+
-+static unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg)
++static unsigned int
++mtk_uart_apdma_chan_read(struct mtk_chan *c, unsigned int reg)
+{
+ return readl(c->base + reg);
+}
@@ -175,70 +146,73 @@
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+
+ kfree(c->desc);
++ c->desc = NULL;
+}
+
+static void mtk_uart_apdma_start_tx(struct mtk_chan *c)
+{
-+ unsigned int len, send, left, wpt, d_wpt, tmp;
-+ int ret;
-+
-+ left = mtk_uart_apdma_read(c, VFF_LEFT_SIZE);
-+ if (!left) {
-+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
++ unsigned int txcount = c->desc->avail_len;
++ unsigned int len, send, left, wpt, wrap;
++
++ if (mtk_uart_apdma_chan_read(c, VFF_LEFT_SIZE) == 0U) {
++ mtk_uart_apdma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
++ } else {
++ len = mtk_uart_apdma_chan_read(c, VFF_LEN);
++
++ while (((left = mtk_uart_apdma_chan_read(c,
++ VFF_LEFT_SIZE)) > 0U)
++ && (c->desc->avail_len != 0U)) {
++ send = min_t(unsigned int, left, c->desc->avail_len);
++ wpt = mtk_uart_apdma_chan_read(c, VFF_WPT);
++ wrap = wpt & MTK_UART_APDMA_RING_WRAP ?
++ 0U : MTK_UART_APDMA_RING_WRAP;
++
++ if ((wpt & (len - 1U)) + send < len)
++ mtk_uart_apdma_chan_write(c,
++ VFF_WPT, wpt + send);
++ else
++ mtk_uart_apdma_chan_write(c, VFF_WPT,
++ ((wpt + send) & (len - 1U))
++ | wrap);
++
++ c->desc->avail_len -= send;
++ }
++
++ if (txcount != c->desc->avail_len) {
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_EN, VFF_TX_INT_EN_B);
++ if (mtk_uart_apdma_chan_read(c,
++ VFF_FLUSH) == 0U)
++ mtk_uart_apdma_chan_write(c,
++ VFF_FLUSH, VFF_FLUSH_B);
++ }
++ }
++}
++
++static void mtk_uart_apdma_start_rx(struct mtk_chan *c)
++{
++ struct mtk_uart_apdma_desc *d = c->desc;
++ unsigned int rx_len, wg, rg, count;
++
++ if (mtk_uart_apdma_chan_read(c, VFF_VALID_SIZE) == 0U)
+ return;
-+ }
-+
-+ /* Wait 1sec for flush, can't sleep */
-+ ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
-+ tmp != VFF_FLUSH_B, 0, 1000000);
-+ if (ret)
-+ dev_warn(c->vc.chan.device->dev, "tx: fail, debug=0x%x\n",
-+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
-+
-+ send = min_t(unsigned int, left, c->desc->avail_len);
-+ wpt = mtk_uart_apdma_read(c, VFF_WPT);
-+ len = mtk_uart_apdma_read(c, VFF_LEN);
-+
-+ d_wpt = wpt + send;
-+ if ((d_wpt & VFF_RING_SIZE) >= len) {
-+ d_wpt = d_wpt - len;
-+ d_wpt = d_wpt ^ VFF_RING_WRAP;
-+ }
-+ mtk_uart_apdma_write(c, VFF_WPT, d_wpt);
-+
-+ c->desc->avail_len -= send;
-+
-+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
-+ if (mtk_uart_apdma_read(c, VFF_FLUSH) == 0U)
-+ mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
-+}
-+
-+static void mtk_uart_apdma_start_rx(struct mtk_chan *c)
-+{
-+ struct mtk_uart_apdma_desc *d = c->desc;
-+ unsigned int len, wg, rg;
-+ int cnt;
-+
-+ if ((mtk_uart_apdma_read(c, VFF_VALID_SIZE) == 0U) ||
-+ !d || !vchan_next_desc(&c->vc))
-+ return;
-+
-+ len = mtk_uart_apdma_read(c, VFF_LEN);
-+ rg = mtk_uart_apdma_read(c, VFF_RPT);
-+ wg = mtk_uart_apdma_read(c, VFF_WPT);
-+ cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE);
-+ /*
-+ * The buffer is ring buffer. If wrap bit different,
-+ * represents the start of the next cycle for WPT
-+ */
-+ if ((rg ^ wg) & VFF_RING_WRAP)
-+ cnt += len;
-+
-+ c->rx_status = cnt;
-+ mtk_uart_apdma_write(c, VFF_RPT, wg);
-+
-+ list_del(&d->vd.node);
-+ vchan_cookie_complete(&d->vd);
++
++ if (d && vchan_next_desc(&c->vc)) {
++ rx_len = mtk_uart_apdma_chan_read(c, VFF_LEN);
++ rg = mtk_uart_apdma_chan_read(c, VFF_RPT);
++ wg = mtk_uart_apdma_chan_read(c, VFF_WPT);
++ count = ((rg ^ wg) & MTK_UART_APDMA_RING_WRAP) ?
++ ((wg & MTK_UART_APDMA_RING_SIZE) +
++ rx_len - (rg & MTK_UART_APDMA_RING_SIZE)) :
++ ((wg & MTK_UART_APDMA_RING_SIZE) -
++ (rg & MTK_UART_APDMA_RING_SIZE));
++
++ c->rx_status = count;
++ mtk_uart_apdma_chan_write(c, VFF_RPT, wg);
++
++ list_del(&d->vd.node);
++ vchan_cookie_complete(&d->vd);
++ }
+}
+
+static irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id)
@@ -249,13 +223,17 @@
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
-+ if (c->dir == DMA_DEV_TO_MEM) {
-+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
++ switch (c->cfg.direction) {
++ case DMA_DEV_TO_MEM:
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
+ mtk_uart_apdma_start_rx(c);
-+ } else if (c->dir == DMA_MEM_TO_DEV) {
++ break;
++ case DMA_MEM_TO_DEV:
+ d = c->desc;
+
-+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
+
+ if (d->avail_len != 0U) {
+ mtk_uart_apdma_start_tx(c);
@@ -263,6 +241,9 @@
+ list_del(&d->vd.node);
+ vchan_cookie_complete(&d->vd);
+ }
++ break;
++ default:
++ break;
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
@@ -273,26 +254,30 @@
+{
+ struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device);
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
-+ unsigned int tmp;
-+ int ret;
++ u32 status;
++ int ret = -EBUSY;
+
+ pm_runtime_get_sync(mtkd->ddev.dev);
+
-+ mtk_uart_apdma_write(c, VFF_ADDR, 0);
-+ mtk_uart_apdma_write(c, VFF_THRE, 0);
-+ mtk_uart_apdma_write(c, VFF_LEN, 0);
-+ mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B);
-+
-+ ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
++ mtk_uart_apdma_chan_write(c, VFF_ADDR, 0);
++ mtk_uart_apdma_chan_write(c, VFF_THRE, 0);
++ mtk_uart_apdma_chan_write(c, VFF_LEN, 0);
++ mtk_uart_apdma_chan_write(c, VFF_RST, VFF_WARM_RST_B);
++
++ ret = readx_poll_timeout(readl,
++ c->base + VFF_EN,
++ status, status == 0, 10, 100);
+ if (ret) {
-+ dev_err(chan->device->dev, "dma reset: fail, timeout\n");
-+ return ret;
++ dev_err(c->vc.chan.device->dev,
++ "dma reset: fail, timeout\n");
++ goto exit;
+ }
+
+ if (!c->requested) {
+ c->requested = true;
+ ret = request_irq(mtkd->dma_irq[chan->chan_id],
-+ mtk_uart_apdma_irq_handler, IRQF_TRIGGER_NONE,
++ mtk_uart_apdma_irq_handler,
++ IRQF_TRIGGER_NONE,
+ KBUILD_MODNAME, chan);
+ if (ret < 0) {
+ dev_err(chan->device->dev, "Can't request dma IRQ\n");
@@ -301,8 +286,10 @@
+ }
+
+ if (mtkd->support_33bits)
-+ mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
-+
++ mtk_uart_apdma_chan_write(c,
++ VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
++
++exit:
+ return ret;
+}
+
@@ -337,9 +324,10 @@
+ ret = dma_cookie_status(chan, cookie, txstate);
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (ret == DMA_IN_PROGRESS) {
-+ c->rx_status = mtk_uart_apdma_read(c, VFF_RPT) & VFF_RING_SIZE;
++ c->rx_status = mtk_uart_apdma_chan_read(c, VFF_RPT)
++ & MTK_UART_APDMA_RING_SIZE;
+ dma_set_residue(txstate, c->rx_status);
-+ } else if (ret == DMA_COMPLETE && c->dir == DMA_DEV_TO_MEM) {
++ } else if (ret == DMA_COMPLETE && c->cfg.direction == DMA_DEV_TO_MEM) {
+ dma_set_residue(txstate, c->rx_status);
+ } else {
+ dma_set_residue(txstate, 0);
@@ -347,49 +335,6 @@
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return ret;
-+}
-+
-+static void mtk_uart_apdma_config_write(struct dma_chan *chan,
-+ struct dma_slave_config *cfg,
-+ enum dma_transfer_direction dir)
-+{
-+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
-+ struct mtk_uart_apdmadev *mtkd =
-+ to_mtk_uart_apdma_dev(c->vc.chan.device);
-+ unsigned int tmp;
-+
-+ if (mtk_uart_apdma_read(c, VFF_EN) == VFF_EN_B)
-+ return;
-+
-+ c->dir = dir;
-+
-+ if (dir == DMA_DEV_TO_MEM) {
-+ tmp = cfg->src_addr_width * 1024;
-+
-+ mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr);
-+ mtk_uart_apdma_write(c, VFF_LEN, tmp);
-+ mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(tmp));
-+ mtk_uart_apdma_write(c, VFF_INT_EN,
-+ VFF_RX_INT_EN0_B | VFF_RX_INT_EN1_B);
-+ mtk_uart_apdma_write(c, VFF_RPT, 0);
-+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
-+ } else if (dir == DMA_MEM_TO_DEV) {
-+ tmp = cfg->dst_addr_width * 1024;
-+
-+ mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr);
-+ mtk_uart_apdma_write(c, VFF_LEN, tmp);
-+ mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tmp));
-+ mtk_uart_apdma_write(c, VFF_WPT, 0);
-+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
-+ }
-+
-+ mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B);
-+
-+ if (mtkd->support_33bits)
-+ mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B);
-+
-+ if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B)
-+ dev_err(chan->device->dev, "dir[%d] fail\n", dir);
+}
+
+/*
@@ -398,16 +343,17 @@
+ */
+static struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg
+ (struct dma_chan *chan, struct scatterlist *sgl,
-+ unsigned int sglen, enum dma_transfer_direction dir,
++ unsigned int sglen, enum dma_transfer_direction dir,
+ unsigned long tx_flags, void *context)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct mtk_uart_apdma_desc *d;
+
-+ if (!is_slave_direction(dir))
++ if ((dir != DMA_DEV_TO_MEM) &&
++ (dir != DMA_MEM_TO_DEV)) {
++ dev_err(chan->device->dev, "bad direction\n");
+ return NULL;
-+
-+ mtk_uart_apdma_config_write(chan, &c->cfg, dir);
++ }
+
+ /* Now allocate and setup the descriptor */
+ d = kzalloc(sizeof(*d), GFP_ATOMIC);
@@ -427,25 +373,64 @@
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
-+ if (vchan_issue_pending(&c->vc)) {
-+ vd = vchan_next_desc(&c->vc);
-+ c->desc = to_mtk_uart_apdma_desc(&vd->tx);
-+ }
-+
-+ if (c->dir == DMA_DEV_TO_MEM)
-+ mtk_uart_apdma_start_rx(c);
-+ else if (c->dir == DMA_MEM_TO_DEV)
-+ mtk_uart_apdma_start_tx(c);
-+
++ if (c->cfg.direction == DMA_DEV_TO_MEM) {
++ if (vchan_issue_pending(&c->vc) && !c->desc) {
++ vd = vchan_next_desc(&c->vc);
++ c->desc = to_mtk_uart_apdma_desc(&vd->tx);
++ mtk_uart_apdma_start_rx(c);
++ }
++ } else if (c->cfg.direction == DMA_MEM_TO_DEV) {
++ if (vchan_issue_pending(&c->vc) && !c->desc) {
++ vd = vchan_next_desc(&c->vc);
++ c->desc = to_mtk_uart_apdma_desc(&vd->tx);
++ mtk_uart_apdma_start_tx(c);
++ }
++ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static int mtk_uart_apdma_slave_config(struct dma_chan *chan,
-+ struct dma_slave_config *config)
-+{
-+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
-+
-+ memcpy(&c->cfg, config, sizeof(*config));
++ struct dma_slave_config *cfg)
++{
++ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
++ struct mtk_uart_apdmadev *mtkd =
++ to_mtk_uart_apdma_dev(c->vc.chan.device);
++
++ c->cfg = *cfg;
++
++ if (cfg->direction == DMA_DEV_TO_MEM) {
++ unsigned int rx_len = cfg->src_addr_width * 1024;
++
++ mtk_uart_apdma_chan_write(c, VFF_ADDR, cfg->src_addr);
++ mtk_uart_apdma_chan_write(c, VFF_LEN, rx_len);
++ mtk_uart_apdma_chan_write(c, VFF_THRE, VFF_RX_THRE(rx_len));
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_EN, VFF_RX_INT_EN0_B
++ | VFF_RX_INT_EN1_B);
++ mtk_uart_apdma_chan_write(c, VFF_RPT, 0);
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
++ mtk_uart_apdma_chan_write(c, VFF_EN, VFF_EN_B);
++ } else if (cfg->direction == DMA_MEM_TO_DEV) {
++ unsigned int tx_len = cfg->dst_addr_width * 1024;
++
++ mtk_uart_apdma_chan_write(c, VFF_ADDR, cfg->dst_addr);
++ mtk_uart_apdma_chan_write(c, VFF_LEN, tx_len);
++ mtk_uart_apdma_chan_write(c, VFF_THRE, VFF_TX_THRE(tx_len));
++ mtk_uart_apdma_chan_write(c, VFF_WPT, 0);
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
++ mtk_uart_apdma_chan_write(c, VFF_EN, VFF_EN_B);
++ }
++
++ if (mtkd->support_33bits)
++ mtk_uart_apdma_chan_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B);
++
++ if (mtk_uart_apdma_chan_read(c, VFF_EN) != VFF_EN_B) {
++ dev_err(chan->device->dev,
++ "config dma dir[%d] fail\n", cfg->direction);
++ return -EINVAL;
++ }
+
+ return 0;
+}
@@ -454,33 +439,48 @@
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ unsigned long flags;
-+ unsigned int tmp;
++ u32 status;
+ int ret;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+
-+ mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
-+ /* Wait 1sec for flush, can't sleep */
-+ ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
-+ tmp != VFF_FLUSH_B, 0, 1000000);
++ mtk_uart_apdma_chan_write(c, VFF_FLUSH, VFF_FLUSH_CLR_B);
++ /* Wait for flush */
++ ret = readx_poll_timeout(readl,
++ c->base + VFF_FLUSH,
++ status,
++ (status & VFF_FLUSH_B) != VFF_FLUSH_B,
++ 10, 100);
+ if (ret)
-+ dev_err(c->vc.chan.device->dev, "flush: fail, debug=0x%x\n",
-+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
-+
-+ /* set stop as 1 -> wait until en is 0 -> set stop as 0 */
-+ mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B);
-+ ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
++ dev_err(c->vc.chan.device->dev,
++ "dma stop: polling FLUSH fail, DEBUG=0x%x\n",
++ mtk_uart_apdma_chan_read(c, VFF_DEBUG_STATUS));
++
++ /*set stop as 1 -> wait until en is 0 -> set stop as 0*/
++ mtk_uart_apdma_chan_write(c, VFF_STOP, VFF_STOP_B);
++ ret = readx_poll_timeout(readl,
++ c->base + VFF_EN,
++ status, status == 0, 10, 100);
+ if (ret)
-+ dev_err(c->vc.chan.device->dev, "stop: fail, debug=0x%x\n",
-+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
-+
-+ mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B);
-+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B);
-+
-+ if (c->dir == DMA_DEV_TO_MEM)
-+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
-+ else if (c->dir == DMA_MEM_TO_DEV)
-+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
++ dev_err(c->vc.chan.device->dev,
++ "dma stop: polling VFF_EN fail, DEBUG=0x%x\n",
++ mtk_uart_apdma_chan_read(c, VFF_DEBUG_STATUS));
++
++ mtk_uart_apdma_chan_write(c, VFF_STOP, VFF_STOP_CLR_B);
++ mtk_uart_apdma_chan_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B);
++
++ switch (c->cfg.direction) {
++ case DMA_DEV_TO_MEM:
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
++ break;
++ case DMA_MEM_TO_DEV:
++ mtk_uart_apdma_chan_write(c,
++ VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
++ break;
++ default:
++ break;
++ }
+
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
@@ -493,9 +493,15 @@
+ return 0;
+}
+
++static int mtk_uart_apdma_device_resume(struct dma_chan *chan)
++{
++ /* just for check caps pass */
++ return 0;
++}
++
+static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd)
+{
-+ while (!list_empty(&mtkd->ddev.channels)) {
++ while (list_empty(&mtkd->ddev.channels) == 0) {
+ struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels,
+ struct mtk_chan, vc.chan.device_node);
+
@@ -512,12 +518,11 @@
+
+static int mtk_uart_apdma_probe(struct platform_device *pdev)
+{
-+ struct device_node *np = pdev->dev.of_node;
+ struct mtk_uart_apdmadev *mtkd;
+ struct resource *res;
+ struct mtk_chan *c;
-+ int bit_mask = 32, rc;
+ unsigned int i;
++ int rc;
+
+ mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL);
+ if (!mtkd)
@@ -527,18 +532,18 @@
+ if (IS_ERR(mtkd->clk)) {
+ dev_err(&pdev->dev, "No clock specified\n");
+ rc = PTR_ERR(mtkd->clk);
-+ return rc;
-+ }
-+
-+ if (of_property_read_bool(np, "dma-33bits"))
++ goto err_no_dma;
++ }
++
++ if (of_property_read_bool(pdev->dev.of_node, "dma-33bits")) {
++ dev_info(&pdev->dev, "Support dma 33bits\n");
+ mtkd->support_33bits = true;
-+
-+ if (mtkd->support_33bits)
-+ bit_mask = 33;
-+
-+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask));
++ }
++
++ rc = dma_set_mask_and_coherent(&pdev->dev,
++ DMA_BIT_MASK(32 | mtkd->support_33bits));
+ if (rc)
-+ return rc;
++ goto err_no_dma;
+
+ dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask);
+ mtkd->ddev.device_alloc_chan_resources =
@@ -550,6 +555,7 @@
+ mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg;
+ mtkd->ddev.device_config = mtk_uart_apdma_slave_config;
+ mtkd->ddev.device_pause = mtk_uart_apdma_device_pause;
++ mtkd->ddev.device_resume = mtk_uart_apdma_device_resume;
+ mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all;
+ mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+ mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
@@ -558,19 +564,7 @@
+ mtkd->ddev.dev = &pdev->dev;
+ INIT_LIST_HEAD(&mtkd->ddev.channels);
+
-+ mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS;
-+ if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) {
-+ dev_info(&pdev->dev,
-+ "Using %u as missing dma-requests property\n",
-+ MTK_UART_APDMA_NR_VCHANS);
-+ }
-+
-+ mtkd->dma_irq = devm_kcalloc(&pdev->dev, mtkd->dma_requests,
-+ sizeof(*mtkd->dma_irq), GFP_KERNEL);
-+ if (!mtkd->dma_irq)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < mtkd->dma_requests; i++) {
++ for (i = 0; i < MTK_UART_APDMA_CHANNELS; i++) {
+ c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ rc = -ENODEV;
@@ -609,10 +603,14 @@
+
+ platform_set_drvdata(pdev, mtkd);
+
-+ /* Device-tree DMA controller registration */
-+ rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd);
-+ if (rc)
-+ goto dma_remove;
++ if (pdev->dev.of_node) {
++ /* Device-tree DMA controller registration */
++ rc = of_dma_controller_register(pdev->dev.of_node,
++ of_dma_xlate_by_chan_id,
++ mtkd);
++ if (rc)
++ goto dma_remove;
++ }
+
+ return rc;
+
@@ -712,3 +710,30 @@
+MODULE_AUTHOR("Long Cheng <long.cheng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
+diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig
+index 27bac0b..1a523c87 100644
+--- a/drivers/dma/mediatek/Kconfig
++++ b/drivers/dma/mediatek/Kconfig
+@@ -1,4 +1,15 @@
+
++config DMA_MTK_UART
++ tristate "MediaTek SoCs APDMA support for UART"
++ depends on OF && SERIAL_8250_MT6577
++ select DMA_ENGINE
++ select DMA_VIRTUAL_CHANNELS
++ help
++ Support for the UART DMA engine found on MediaTek MTK SoCs.
++ when SERIAL_8250_MT6577 is enabled, and if you want to use DMA,
++ you can enable the config. the DMA engine can only be used
++ with MediaTek SoCs.
++
+ config MTK_HSDMA
+ tristate "MediaTek High-Speed DMA controller support"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile
+index 6e778f8..2f2efd9 100644
+--- a/drivers/dma/mediatek/Makefile
++++ b/drivers/dma/mediatek/Makefile
+@@ -1 +1,2 @@
++obj-$(CONFIG_DMA_MTK_UART) += 8250_mtk_dma.o
+ obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o