Inter-revision diff: patch 1

Comparing v10 (message) to v7 (message)

--- 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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help