Linux Serial subsystem development
 help / color / mirror / Atom feed
* [PATCH v2 3/4] serial: 8250-mtk: add uart DMA support
From: Long Cheng @ 2018-12-05  8:42 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Sean Wang, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen
In-Reply-To: <1543999380-7946-1-git-send-email-long.cheng@mediatek.com>

Modify uart register to support DMA function.

Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
 drivers/tty/serial/8250/8250_mtk.c |  210 +++++++++++++++++++++++++++++++++++-
 1 file changed, 209 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index dd5e1ce..1da73e8 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -14,6 +14,10 @@
 #include <linux/pm_runtime.h>
 #include <linux/serial_8250.h>
 #include <linux/serial_reg.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
 
 #include "8250.h"
 
@@ -22,12 +26,172 @@
 #define UART_MTK_SAMPLE_POINT	0x0b	/* Sample point register */
 #define MTK_UART_RATE_FIX	0x0d	/* UART Rate Fix Register */
 
+#define MTK_UART_DMA_EN		0x13	/* DMA Enable register */
+#define MTK_UART_DMA_EN_TX	0x2
+#define MTK_UART_DMA_EN_RX	0x5
+
+#define MTK_UART_TX_SIZE	UART_XMIT_SIZE
+#define MTK_UART_RX_SIZE	0x8000
+#define MTK_UART_TX_TRIGGER	1
+#define MTK_UART_RX_TRIGGER	MTK_UART_RX_SIZE
+
+#ifdef CONFIG_SERIAL_8250_DMA
+enum dma_rx_status {
+	DMA_RX_START = 0,
+	DMA_RX_RUNNING = 1,
+	DMA_RX_SHUTDOWN = 2,
+};
+#endif
+
 struct mtk8250_data {
 	int			line;
+	unsigned int		rx_pos;
 	struct clk		*uart_clk;
 	struct clk		*bus_clk;
+	struct uart_8250_dma	*dma;
+#ifdef CONFIG_SERIAL_8250_DMA
+	enum dma_rx_status	rx_status;
+#endif
 };
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static void mtk8250_rx_dma(struct uart_8250_port *up);
+
+static void mtk8250_dma_rx_complete(void *param)
+{
+	struct uart_8250_port *up = param;
+	struct uart_8250_dma *dma = up->dma;
+	struct mtk8250_data *data = up->port.private_data;
+	struct tty_port *tty_port = &up->port.state->port;
+	struct dma_tx_state state;
+	unsigned char *ptr;
+	int copied;
+
+	dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
+				dma->rx_size, DMA_FROM_DEVICE);
+
+	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+
+	if (data->rx_status == DMA_RX_SHUTDOWN)
+		return;
+
+	if ((data->rx_pos + state.residue) <= dma->rx_size) {
+		ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+		copied = tty_insert_flip_string(tty_port, ptr, state.residue);
+	} else {
+		ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+		copied = tty_insert_flip_string(tty_port, ptr,
+						dma->rx_size - data->rx_pos);
+		ptr = (unsigned char *)(dma->rx_buf);
+		copied += tty_insert_flip_string(tty_port, ptr,
+				data->rx_pos + state.residue - dma->rx_size);
+	}
+	up->port.icount.rx += copied;
+
+	tty_flip_buffer_push(tty_port);
+
+	mtk8250_rx_dma(up);
+}
+
+static void mtk8250_rx_dma(struct uart_8250_port *up)
+{
+	struct uart_8250_dma *dma = up->dma;
+	struct mtk8250_data *data = up->port.private_data;
+	struct dma_async_tx_descriptor	*desc;
+	struct dma_tx_state	 state;
+
+	desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
+					   dma->rx_size, DMA_DEV_TO_MEM,
+					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		pr_err("failed to prepare rx slave single\n");
+		return;
+	}
+
+	desc->callback = mtk8250_dma_rx_complete;
+	desc->callback_param = up;
+
+	dma->rx_cookie = dmaengine_submit(desc);
+
+	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+	data->rx_pos = state.residue;
+
+	dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
+				   dma->rx_size, DMA_FROM_DEVICE);
+
+	dma_async_issue_pending(dma->rxchan);
+}
+
+static void mtk8250_dma_enable(struct uart_8250_port *up)
+{
+	struct uart_8250_dma *dma = up->dma;
+	struct mtk8250_data *data = up->port.private_data;
+	int lcr = serial_in(up, UART_LCR);
+
+	if (data->rx_status != DMA_RX_START)
+		return;
+
+	dma->rxconf.direction		= DMA_DEV_TO_MEM;
+	dma->rxconf.src_addr_width	= dma->rx_size / 1024;
+	dma->rxconf.src_addr		= dma->rx_addr;
+
+	dma->txconf.direction		= DMA_MEM_TO_DEV;
+	dma->txconf.dst_addr_width	= MTK_UART_TX_SIZE / 1024;
+	dma->txconf.dst_addr		= dma->tx_addr;
+
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+		UART_FCR_CLEAR_XMIT);
+	serial_out(up, MTK_UART_DMA_EN,
+		   MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, UART_EFR_ECB);
+	serial_out(up, UART_LCR, lcr);
+
+	if (dmaengine_slave_config(dma->rxchan, &dma->rxconf) != 0)
+		pr_err("failed to configure rx dma channel\n");
+	if (dmaengine_slave_config(dma->txchan, &dma->txconf) != 0)
+		pr_err("failed to configure tx dma channel\n");
+
+	data->rx_status = DMA_RX_RUNNING;
+	data->rx_pos = 0;
+	mtk8250_rx_dma(up);
+}
+#endif
+
+static int mtk8250_startup(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_8250_DMA
+	struct uart_8250_port *up = up_to_u8250p(port);
+	struct mtk8250_data *data = port->private_data;
+
+	/* disable DMA for console */
+	if (uart_console(port))
+		up->dma = NULL;
+
+	if (up->dma) {
+		data->rx_status = DMA_RX_START;
+		uart_circ_clear(&port->state->xmit);
+	}
+#endif
+	memset(&port->icount, 0, sizeof(port->icount));
+
+	return serial8250_do_startup(port);
+}
+
+static void mtk8250_shutdown(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_8250_DMA
+	struct uart_8250_port *up = up_to_u8250p(port);
+	struct mtk8250_data *data = port->private_data;
+
+	if (up->dma)
+		data->rx_status = DMA_RX_SHUTDOWN;
+#endif
+
+	return serial8250_do_shutdown(port);
+}
+
 static void
 mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
 			struct ktermios *old)
@@ -36,6 +200,17 @@ struct mtk8250_data {
 	unsigned long flags;
 	unsigned int baud, quot;
 
+#ifdef CONFIG_SERIAL_8250_DMA
+	if (up->dma) {
+		if (uart_console(port)) {
+			devm_kfree(up->port.dev, up->dma);
+			up->dma = NULL;
+		} else {
+			mtk8250_dma_enable(up);
+		}
+	}
+#endif
+
 	serial8250_do_set_termios(port, termios, old);
 
 	/*
@@ -143,9 +318,20 @@ static int __maybe_unused mtk8250_runtime_resume(struct device *dev)
 		pm_runtime_put_sync_suspend(port->dev);
 }
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static bool mtk8250_dma_filter(struct dma_chan *chan, void *param)
+{
+	return false;
+}
+#endif
+
 static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
 			   struct mtk8250_data *data)
 {
+#ifdef CONFIG_SERIAL_8250_DMA
+	int dmacnt;
+#endif
+
 	data->uart_clk = devm_clk_get(&pdev->dev, "baud");
 	if (IS_ERR(data->uart_clk)) {
 		/*
@@ -162,7 +348,23 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
 	}
 
 	data->bus_clk = devm_clk_get(&pdev->dev, "bus");
-	return PTR_ERR_OR_ZERO(data->bus_clk);
+	if (IS_ERR(data->bus_clk))
+		return PTR_ERR(data->bus_clk);
+
+	data->dma = NULL;
+#ifdef CONFIG_SERIAL_8250_DMA
+	dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names");
+	if (dmacnt == 2) {
+		data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma),
+					 GFP_KERNEL);
+		data->dma->fn = mtk8250_dma_filter;
+		data->dma->rx_size = MTK_UART_RX_SIZE;
+		data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER;
+		data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER;
+	}
+#endif
+
+	return 0;
 }
 
 static int mtk8250_probe(struct platform_device *pdev)
@@ -204,8 +406,14 @@ static int mtk8250_probe(struct platform_device *pdev)
 	uart.port.iotype = UPIO_MEM32;
 	uart.port.regshift = 2;
 	uart.port.private_data = data;
+	uart.port.shutdown = mtk8250_shutdown;
+	uart.port.startup = mtk8250_startup;
 	uart.port.set_termios = mtk8250_set_termios;
 	uart.port.uartclk = clk_get_rate(data->uart_clk);
+#ifdef CONFIG_SERIAL_8250_DMA
+	if (data->dma)
+		uart.dma = data->dma;
+#endif
 
 	/* Disable Rate Fix function */
 	writel(0x0, uart.port.membase +
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2 2/4] dmaengine: mtk_uart_dma: add Mediatek uart DMA support
From: Long Cheng @ 2018-12-05  8:42 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Sean Wang, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen
In-Reply-To: <1543999380-7946-1-git-send-email-long.cheng@mediatek.com>

In DMA engine framework, add 8250 mtk dma to support it.

Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
 drivers/dma/mediatek/8250_mtk_dma.c |  894 +++++++++++++++++++++++++++++++++++
 drivers/dma/mediatek/Kconfig        |   11 +
 drivers/dma/mediatek/Makefile       |    1 +
 3 files changed, 906 insertions(+)
 create mode 100644 drivers/dma/mediatek/8250_mtk_dma.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..3454679
--- /dev/null
+++ b/drivers/dma/mediatek/8250_mtk_dma.c
@@ -0,0 +1,894 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek 8250 DMA driver.
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Long Cheng <long.cheng@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_dma.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+
+#include "../virt-dma.h"
+
+#define MTK_APDMA_DEFAULT_REQUESTS	127
+#define MTK_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_EN1_B	BIT(1)
+#define VFF_TX_INT_EN_B		BIT(0)	/*tx left size >= vff thre*/
+#define VFF_WARM_RST_B		BIT(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
+
+/* interrupt trigger level for tx */
+#define VFF_TX_THRE(n)		((n) * 7 / 8)
+/* interrupt trigger level for rx */
+#define VFF_RX_THRE(n)		((n) * 3 / 4)
+
+#define MTK_DMA_RING_SIZE	0xffffU
+/* invert this bit when wrap ring head again*/
+#define MTK_DMA_RING_WRAP	0x10000U
+
+#define VFF_INT_FLAG		0x00
+#define VFF_INT_EN		0x04
+#define VFF_EN			0x08
+#define VFF_RST			0x0c
+#define VFF_STOP		0x10
+#define VFF_FLUSH		0x14
+#define VFF_ADDR		0x1c
+#define VFF_LEN			0x24
+#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.*/
+#define VFF_VALID_SIZE		0x3c
+/*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
+
+struct mtk_dmadev {
+	struct dma_device ddev;
+	void __iomem *mem_base[MTK_APDMA_CHANNELS];
+	spinlock_t lock; /* dma dev lock */
+	struct tasklet_struct task;
+	struct list_head pending;
+	struct clk *clk;
+	unsigned int dma_requests;
+	bool support_33bits;
+	unsigned int dma_irq[MTK_APDMA_CHANNELS];
+	struct mtk_chan *ch[MTK_APDMA_CHANNELS];
+};
+
+struct mtk_chan {
+	struct virt_dma_chan vc;
+	struct list_head node;
+	struct dma_slave_config	cfg;
+	void __iomem *base;
+	struct mtk_dma_desc *desc;
+
+	bool stop;
+	bool requested;
+
+	unsigned int dma_sig;
+	unsigned int dma_ch;
+	unsigned int sgidx;
+	unsigned int remain_size;
+	unsigned int rx_ptr;
+};
+
+struct mtk_dma_sg {
+	dma_addr_t addr;
+	unsigned int en;		/* number of elements (24-bit) */
+	unsigned int fn;		/* number of frames (16-bit) */
+};
+
+struct mtk_dma_desc {
+	struct virt_dma_desc vd;
+	enum dma_transfer_direction dir;
+
+	unsigned int sglen;
+	struct mtk_dma_sg sg[0];
+};
+
+static bool mtk_dma_filter_fn(struct dma_chan *chan, void *param);
+static struct of_dma_filter_info mtk_dma_info = {
+	.filter_fn = mtk_dma_filter_fn,
+};
+
+static inline struct mtk_dmadev *to_mtk_dma_dev(struct dma_device *d)
+{
+	return container_of(d, struct mtk_dmadev, ddev);
+}
+
+static inline struct mtk_chan *to_mtk_dma_chan(struct dma_chan *c)
+{
+	return container_of(c, struct mtk_chan, vc.chan);
+}
+
+static inline struct mtk_dma_desc *to_mtk_dma_desc
+	(struct dma_async_tx_descriptor *t)
+{
+	return container_of(t, struct mtk_dma_desc, vd.tx);
+}
+
+static void mtk_dma_chan_write(struct mtk_chan *c,
+			       unsigned int reg, unsigned int val)
+{
+	writel(val, c->base + reg);
+}
+
+static unsigned int mtk_dma_chan_read(struct mtk_chan *c, unsigned int reg)
+{
+	return readl(c->base + reg);
+}
+
+static void mtk_dma_desc_free(struct virt_dma_desc *vd)
+{
+	struct dma_chan *chan = vd->tx.chan;
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+
+	kfree(c->desc);
+	c->desc = NULL;
+}
+
+static int mtk_dma_clk_enable(struct mtk_dmadev *mtkd)
+{
+	int ret;
+
+	ret = clk_prepare_enable(mtkd->clk);
+	if (ret) {
+		dev_err(mtkd->ddev.dev, "Couldn't enable the clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mtk_dma_clk_disable(struct mtk_dmadev *mtkd)
+{
+	clk_disable_unprepare(mtkd->clk);
+}
+
+static void mtk_dma_remove_virt_list(dma_cookie_t cookie,
+				     struct virt_dma_chan *vc)
+{
+	struct virt_dma_desc *vd;
+
+	if (list_empty(&vc->desc_issued) == 0) {
+		list_for_each_entry(vd, &vc->desc_issued, node) {
+			if (cookie == vd->tx.cookie) {
+				INIT_LIST_HEAD(&vc->desc_issued);
+				break;
+			}
+		}
+	}
+}
+
+static void mtk_dma_tx_flush(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+
+	if (mtk_dma_chan_read(c, VFF_FLUSH) == 0U)
+		mtk_dma_chan_write(c, VFF_FLUSH, VFF_FLUSH_B);
+}
+
+static void mtk_dma_tx_write(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	unsigned int txcount = c->remain_size;
+	unsigned int len, send, left, wpt, wrap;
+
+	len = mtk_dma_chan_read(c, VFF_LEN);
+
+	while ((left = mtk_dma_chan_read(c, VFF_LEFT_SIZE)) > 0U) {
+		if (c->remain_size == 0U)
+			break;
+		send = min(left, c->remain_size);
+		wpt = mtk_dma_chan_read(c, VFF_WPT);
+		wrap = wpt & MTK_DMA_RING_WRAP ? 0U : MTK_DMA_RING_WRAP;
+
+		if ((wpt & (len - 1U)) + send < len)
+			mtk_dma_chan_write(c, VFF_WPT, wpt + send);
+		else
+			mtk_dma_chan_write(c, VFF_WPT,
+					   ((wpt + send) & (len - 1U))
+					   | wrap);
+
+		c->remain_size -= send;
+	}
+
+	if (txcount != c->remain_size) {
+		mtk_dma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+		mtk_dma_tx_flush(chan);
+	}
+}
+
+static void mtk_dma_start_tx(struct mtk_chan *c)
+{
+	if (mtk_dma_chan_read(c, VFF_LEFT_SIZE) == 0U)
+		mtk_dma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+	else
+		mtk_dma_tx_write(&c->vc.chan);
+
+	c->stop = false;
+}
+
+static void mtk_dma_get_rx_size(struct mtk_chan *c)
+{
+	unsigned int rx_size = mtk_dma_chan_read(c, VFF_LEN);
+	unsigned int rdptr, wrptr, wrreg, rdreg, count;
+
+	rdreg = mtk_dma_chan_read(c, VFF_RPT);
+	wrreg = mtk_dma_chan_read(c, VFF_WPT);
+	rdptr = rdreg & MTK_DMA_RING_SIZE;
+	wrptr = wrreg & MTK_DMA_RING_SIZE;
+	count = ((rdreg ^ wrreg) & MTK_DMA_RING_WRAP) ?
+			(wrptr + rx_size - rdptr) : (wrptr - rdptr);
+
+	c->remain_size = count;
+	c->rx_ptr = rdptr;
+
+	mtk_dma_chan_write(c, VFF_RPT, wrreg);
+}
+
+static void mtk_dma_start_rx(struct mtk_chan *c)
+{
+	struct dma_chan *chan = &c->vc.chan;
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_dma_desc *d = c->desc;
+
+	if (mtk_dma_chan_read(c, VFF_VALID_SIZE) == 0U)
+		return;
+
+	if (d && d->vd.tx.cookie != 0) {
+		mtk_dma_get_rx_size(c);
+		mtk_dma_remove_virt_list(d->vd.tx.cookie, &c->vc);
+		vchan_cookie_complete(&d->vd);
+	} else {
+		spin_lock(&mtkd->lock);
+		if (list_empty(&mtkd->pending))
+			list_add_tail(&c->node, &mtkd->pending);
+		spin_unlock(&mtkd->lock);
+		tasklet_schedule(&mtkd->task);
+	}
+}
+
+static void mtk_dma_reset(struct mtk_chan *c)
+{
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(c->vc.chan.device);
+	u32 status;
+	int ret;
+
+	mtk_dma_chan_write(c, VFF_ADDR, 0);
+	mtk_dma_chan_write(c, VFF_THRE, 0);
+	mtk_dma_chan_write(c, VFF_LEN, 0);
+	mtk_dma_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(c->vc.chan.device->dev,
+				"dma reset: fail, timeout\n");
+		return;
+	}
+
+	if (c->cfg.direction == DMA_DEV_TO_MEM)
+		mtk_dma_chan_write(c, VFF_RPT, 0);
+	else if (c->cfg.direction == DMA_MEM_TO_DEV)
+		mtk_dma_chan_write(c, VFF_WPT, 0);
+
+	if (mtkd->support_33bits)
+		mtk_dma_chan_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
+}
+
+static void mtk_dma_stop(struct mtk_chan *c)
+{
+	u32 status;
+	int ret;
+
+	mtk_dma_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,
+			"dma stop: polling FLUSH fail, DEBUG=0x%x\n",
+			mtk_dma_chan_read(c, VFF_DEBUG_STATUS));
+
+	/*set stop as 1 -> wait until en is 0 -> set stop as 0*/
+	mtk_dma_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,
+			"dma stop: polling VFF_EN fail, DEBUG=0x%x\n",
+			mtk_dma_chan_read(c, VFF_DEBUG_STATUS));
+
+	mtk_dma_chan_write(c, VFF_STOP, VFF_STOP_CLR_B);
+	mtk_dma_chan_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B);
+
+	if (c->cfg.direction == DMA_DEV_TO_MEM)
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
+	else
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
+
+	c->stop = true;
+}
+
+/*
+ * This callback schedules all pending channels. We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void mtk_dma_sched(unsigned long data)
+{
+	struct mtk_dmadev *mtkd = (struct mtk_dmadev *)data;
+	struct virt_dma_desc *vd;
+	struct mtk_chan *c;
+	dma_cookie_t cookie;
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&mtkd->lock);
+	list_splice_tail_init(&mtkd->pending, &head);
+	spin_unlock_irq(&mtkd->lock);
+
+	if (!list_empty(&head)) {
+		c = list_first_entry(&head, struct mtk_chan, node);
+		cookie = c->vc.chan.cookie;
+
+		spin_lock_irqsave(&c->vc.lock, flags);
+		if (c->cfg.direction == DMA_DEV_TO_MEM) {
+			list_del_init(&c->node);
+			mtk_dma_start_rx(c);
+		} else if (c->cfg.direction == DMA_MEM_TO_DEV) {
+			vd = vchan_find_desc(&c->vc, cookie);
+			c->desc = to_mtk_dma_desc(&vd->tx);
+			list_del_init(&c->node);
+			mtk_dma_start_tx(c);
+		}
+		spin_unlock_irqrestore(&c->vc.lock, flags);
+	}
+}
+
+static int mtk_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	int ret = -EBUSY;
+
+	pm_runtime_get_sync(mtkd->ddev.dev);
+
+	if (!mtkd->ch[c->dma_ch]) {
+		c->base = mtkd->mem_base[c->dma_ch];
+		mtkd->ch[c->dma_ch] = c;
+		ret = 1;
+	}
+	c->requested = false;
+	mtk_dma_reset(c);
+
+	return ret;
+}
+
+static void mtk_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+
+	if (c->requested) {
+		c->requested = false;
+		free_irq(mtkd->dma_irq[c->dma_ch], chan);
+	}
+
+	tasklet_kill(&mtkd->task);
+	tasklet_kill(&c->vc.task);
+
+	c->base = NULL;
+	mtkd->ch[c->dma_ch] = NULL;
+	vchan_free_chan_resources(&c->vc);
+
+	dev_dbg(mtkd->ddev.dev, "freeing channel for %u\n", c->dma_sig);
+	c->dma_sig = 0;
+
+	pm_runtime_put_sync(mtkd->ddev.dev);
+}
+
+static enum dma_status mtk_dma_tx_status(struct dma_chan *chan,
+					 dma_cookie_t cookie,
+					 struct dma_tx_state *txstate)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	enum dma_status ret;
+	unsigned long flags;
+
+	if (!txstate)
+		return DMA_ERROR;
+
+	ret = dma_cookie_status(chan, cookie, txstate);
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (ret == DMA_IN_PROGRESS) {
+		c->rx_ptr = mtk_dma_chan_read(c, VFF_RPT) & MTK_DMA_RING_SIZE;
+		dma_set_residue(txstate, c->rx_ptr);
+	} else if (ret == DMA_COMPLETE && c->cfg.direction == DMA_DEV_TO_MEM) {
+		dma_set_residue(txstate, c->remain_size);
+	} else {
+		dma_set_residue(txstate, 0);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	return ret;
+}
+
+static struct dma_async_tx_descriptor *mtk_dma_prep_slave_sg
+	(struct dma_chan *chan, struct scatterlist *sgl,
+	unsigned int sglen,	enum dma_transfer_direction dir,
+	unsigned long tx_flags, void *context)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct scatterlist *sgent;
+	struct mtk_dma_desc *d;
+	struct mtk_dma_sg *sg;
+	unsigned int size, i, j, en;
+
+	en = 1;
+
+	if ((dir != DMA_DEV_TO_MEM) &&
+		(dir != DMA_MEM_TO_DEV)) {
+		dev_err(chan->device->dev, "bad direction\n");
+		return NULL;
+	}
+
+	/* Now allocate and setup the descriptor. */
+	d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+	if (!d)
+		return NULL;
+
+	d->dir = dir;
+
+	j = 0;
+	for_each_sg(sgl, sgent, sglen, i) {
+		d->sg[j].addr = sg_dma_address(sgent);
+		d->sg[j].en = en;
+		d->sg[j].fn = sg_dma_len(sgent) / en;
+		j++;
+	}
+
+	d->sglen = j;
+
+	if (dir == DMA_MEM_TO_DEV) {
+		for (size = i = 0; i < d->sglen; i++) {
+			sg = &d->sg[i];
+			size += sg->en * sg->fn;
+		}
+		c->remain_size = size;
+	}
+
+	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static void mtk_dma_issue_pending(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct virt_dma_desc *vd;
+	struct mtk_dmadev *mtkd;
+	dma_cookie_t cookie;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (c->cfg.direction == DMA_DEV_TO_MEM) {
+		cookie = c->vc.chan.cookie;
+		mtkd = to_mtk_dma_dev(chan->device);
+		if (vchan_issue_pending(&c->vc) && !c->desc) {
+			vd = vchan_find_desc(&c->vc, cookie);
+			c->desc = to_mtk_dma_desc(&vd->tx);
+		}
+	} else if (c->cfg.direction == DMA_MEM_TO_DEV) {
+		cookie = c->vc.chan.cookie;
+		if (vchan_issue_pending(&c->vc) && !c->desc) {
+			vd = vchan_find_desc(&c->vc, cookie);
+			c->desc = to_mtk_dma_desc(&vd->tx);
+			mtk_dma_start_tx(c);
+		}
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static irqreturn_t mtk_dma_rx_interrupt(int irq, void *dev_id)
+{
+	struct dma_chan *chan = (struct dma_chan *)dev_id;
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
+
+	mtk_dma_start_rx(c);
+
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dma_tx_interrupt(int irq, void *dev_id)
+{
+	struct dma_chan *chan = (struct dma_chan *)dev_id;
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct mtk_dma_desc *d = c->desc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (c->remain_size != 0U) {
+		list_add_tail(&c->node, &mtkd->pending);
+		tasklet_schedule(&mtkd->task);
+	} else {
+		mtk_dma_remove_virt_list(d->vd.tx.cookie, &c->vc);
+		vchan_cookie_complete(&d->vd);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_dma_slave_config(struct dma_chan *chan,
+				struct dma_slave_config *cfg)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(c->vc.chan.device);
+	int ret;
+
+	c->cfg = *cfg;
+
+	if (cfg->direction == DMA_DEV_TO_MEM) {
+		unsigned int rx_len = cfg->src_addr_width * 1024;
+
+		mtk_dma_chan_write(c, VFF_ADDR, cfg->src_addr);
+		mtk_dma_chan_write(c, VFF_LEN, rx_len);
+		mtk_dma_chan_write(c, VFF_THRE, VFF_RX_THRE(rx_len));
+		mtk_dma_chan_write(c,
+				   VFF_INT_EN, VFF_RX_INT_EN0_B
+				   | VFF_RX_INT_EN1_B);
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
+		mtk_dma_chan_write(c, VFF_EN, VFF_EN_B);
+
+		if (!c->requested) {
+			c->requested = true;
+			ret = request_irq(mtkd->dma_irq[c->dma_ch],
+					  mtk_dma_rx_interrupt,
+					  IRQF_TRIGGER_NONE,
+					  KBUILD_MODNAME, chan);
+			if (ret < 0) {
+				dev_err(chan->device->dev, "Can't request rx dma IRQ\n");
+				return -EINVAL;
+			}
+		}
+	} else if (cfg->direction == DMA_MEM_TO_DEV)	{
+		unsigned int tx_len = cfg->dst_addr_width * 1024;
+
+		mtk_dma_chan_write(c, VFF_ADDR, cfg->dst_addr);
+		mtk_dma_chan_write(c, VFF_LEN, tx_len);
+		mtk_dma_chan_write(c, VFF_THRE, VFF_TX_THRE(tx_len));
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
+		mtk_dma_chan_write(c, VFF_EN, VFF_EN_B);
+
+		if (!c->requested) {
+			c->requested = true;
+			ret = request_irq(mtkd->dma_irq[c->dma_ch],
+					  mtk_dma_tx_interrupt,
+					  IRQF_TRIGGER_NONE,
+					  KBUILD_MODNAME, chan);
+			if (ret < 0) {
+				dev_err(chan->device->dev, "Can't request tx dma IRQ\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (mtkd->support_33bits)
+		mtk_dma_chan_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B);
+
+	if (mtk_dma_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;
+}
+
+static int mtk_dma_terminate_all(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	list_del_init(&c->node);
+	mtk_dma_stop(c);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	return 0;
+}
+
+static int mtk_dma_device_pause(struct dma_chan *chan)
+{
+	/* just for check caps pass */
+	return -EINVAL;
+}
+
+static int mtk_dma_device_resume(struct dma_chan *chan)
+{
+	/* just for check caps pass */
+	return -EINVAL;
+}
+
+static void mtk_dma_free(struct mtk_dmadev *mtkd)
+{
+	tasklet_kill(&mtkd->task);
+	while (list_empty(&mtkd->ddev.channels) == 0) {
+		struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels,
+			struct mtk_chan, vc.chan.device_node);
+
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
+		devm_kfree(mtkd->ddev.dev, c);
+	}
+}
+
+static const struct of_device_id mtk_uart_dma_match[] = {
+	{ .compatible = "mediatek,mt6577-uart-dma", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_uart_dma_match);
+
+static int mtk_apdma_probe(struct platform_device *pdev)
+{
+	struct mtk_dmadev *mtkd;
+	struct resource *res;
+	struct mtk_chan *c;
+	unsigned int i;
+	int rc;
+
+	mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL);
+	if (!mtkd)
+		return -ENOMEM;
+
+	for (i = 0; i < MTK_APDMA_CHANNELS; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		if (!res)
+			return -ENODEV;
+		mtkd->mem_base[i] = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(mtkd->mem_base[i]))
+			return PTR_ERR(mtkd->mem_base[i]);
+	}
+
+	for (i = 0; i < MTK_APDMA_CHANNELS; i++) {
+		mtkd->dma_irq[i] = platform_get_irq(pdev, i);
+		if ((int)mtkd->dma_irq[i] < 0) {
+			dev_err(&pdev->dev, "failed to get IRQ[%d]\n", i);
+			return -EINVAL;
+		}
+	}
+
+	mtkd->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mtkd->clk)) {
+		dev_err(&pdev->dev, "No clock specified\n");
+		return PTR_ERR(mtkd->clk);
+	}
+
+	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)
+		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
+	else
+		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (rc)
+		return rc;
+
+	dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask);
+	mtkd->ddev.device_alloc_chan_resources = mtk_dma_alloc_chan_resources;
+	mtkd->ddev.device_free_chan_resources = mtk_dma_free_chan_resources;
+	mtkd->ddev.device_tx_status = mtk_dma_tx_status;
+	mtkd->ddev.device_issue_pending = mtk_dma_issue_pending;
+	mtkd->ddev.device_prep_slave_sg = mtk_dma_prep_slave_sg;
+	mtkd->ddev.device_config = mtk_dma_slave_config;
+	mtkd->ddev.device_pause = mtk_dma_device_pause;
+	mtkd->ddev.device_resume = mtk_dma_device_resume;
+	mtkd->ddev.device_terminate_all = mtk_dma_terminate_all;
+	mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+	mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+	mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+	mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+	mtkd->ddev.dev = &pdev->dev;
+	INIT_LIST_HEAD(&mtkd->ddev.channels);
+	INIT_LIST_HEAD(&mtkd->pending);
+
+	spin_lock_init(&mtkd->lock);
+	tasklet_init(&mtkd->task, mtk_dma_sched, (unsigned long)mtkd);
+
+	mtkd->dma_requests = MTK_APDMA_DEFAULT_REQUESTS;
+	if (of_property_read_u32(pdev->dev.of_node,
+				 "dma-requests", &mtkd->dma_requests)) {
+		dev_info(&pdev->dev,
+			 "Missing dma-requests property, using %u.\n",
+			 MTK_APDMA_DEFAULT_REQUESTS);
+	}
+
+	for (i = 0; i < MTK_APDMA_CHANNELS; i++) {
+		c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL);
+		if (!c)
+			goto err_no_dma;
+
+		c->vc.desc_free = mtk_dma_desc_free;
+		vchan_init(&c->vc, &mtkd->ddev);
+		INIT_LIST_HEAD(&c->node);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+
+	rc = dma_async_device_register(&mtkd->ddev);
+	if (rc)
+		goto rpm_disable;
+
+	platform_set_drvdata(pdev, mtkd);
+
+	if (pdev->dev.of_node) {
+		mtk_dma_info.dma_cap = mtkd->ddev.cap_mask;
+
+		/* Device-tree DMA controller registration */
+		rc = of_dma_controller_register(pdev->dev.of_node,
+						of_dma_simple_xlate,
+						&mtk_dma_info);
+		if (rc)
+			goto dma_remove;
+	}
+
+	return rc;
+
+dma_remove:
+	dma_async_device_unregister(&mtkd->ddev);
+rpm_disable:
+	pm_runtime_disable(&pdev->dev);
+err_no_dma:
+	mtk_dma_free(mtkd);
+	return rc;
+}
+
+static int mtk_apdma_remove(struct platform_device *pdev)
+{
+	struct mtk_dmadev *mtkd = platform_get_drvdata(pdev);
+
+	if (pdev->dev.of_node)
+		of_dma_controller_free(pdev->dev.of_node);
+
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+
+	dma_async_device_unregister(&mtkd->ddev);
+
+	mtk_dma_free(mtkd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dma_suspend(struct device *dev)
+{
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	if (!pm_runtime_suspended(dev))
+		mtk_dma_clk_disable(mtkd);
+
+	return 0;
+}
+
+static int mtk_dma_resume(struct device *dev)
+{
+	int ret;
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	if (!pm_runtime_suspended(dev)) {
+		ret = mtk_dma_clk_enable(mtkd);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int mtk_dma_runtime_suspend(struct device *dev)
+{
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	mtk_dma_clk_disable(mtkd);
+
+	return 0;
+}
+
+static int mtk_dma_runtime_resume(struct device *dev)
+{
+	int ret;
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	ret = mtk_dma_clk_enable(mtkd);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops mtk_dma_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtk_dma_suspend, mtk_dma_resume)
+	SET_RUNTIME_PM_OPS(mtk_dma_runtime_suspend,
+			   mtk_dma_runtime_resume, NULL)
+};
+
+static struct platform_driver mtk_dma_driver = {
+	.probe	= mtk_apdma_probe,
+	.remove	= mtk_apdma_remove,
+	.driver = {
+		.name		= KBUILD_MODNAME,
+		.pm		= &mtk_dma_pm_ops,
+		.of_match_table = of_match_ptr(mtk_uart_dma_match),
+	},
+};
+
+static bool mtk_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	if (chan->device->dev->driver == &mtk_dma_driver.driver) {
+		struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+		struct mtk_chan *c = to_mtk_dma_chan(chan);
+		unsigned int req = *(unsigned int *)param;
+
+		if (req <= mtkd->dma_requests) {
+			c->dma_sig = req;
+			c->dma_ch = req;
+			return true;
+		}
+	}
+	return false;
+}
+
+module_platform_driver(mtk_dma_driver);
+
+MODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver");
+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..bef436e 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
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Support for the UART DMA engine found on MediaTek MTK SoCs.
+	  when 8250 mtk uart is enabled, and if you want to using DMA,
+	  you can enable the config. the DMA engine just 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
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2 1/4] dt-bindings: dma: uart: add uart dma bindings
From: Long Cheng @ 2018-12-05  8:42 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Sean Wang, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen
In-Reply-To: <1543999380-7946-1-git-send-email-long.cheng@mediatek.com>

add uart dma bindings

Signed-off-by: Long Cheng <long.cheng@mediatek.com>
Reviewed-by: Rob Herring <robh@kernel.org> 
---
 .../devicetree/bindings/dma/8250_mtk_dma.txt       |   33 ++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/8250_mtk_dma.txt

diff --git a/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt b/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
new file mode 100644
index 0000000..3fe0961
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
@@ -0,0 +1,33 @@
+* Mediatek UART APDMA Controller
+
+Required properties:
+- compatible should contain:
+  * "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
+  * "mediatek,mt6577-uart-dma" for MT6577 and all of the above
+
+- reg: The base address of the APDMA register bank.
+
+- interrupts: A single interrupt specifier.
+
+- clocks : Must contain an entry for each entry in clock-names.
+  See ../clocks/clock-bindings.txt for details.
+- clock-names: The APDMA clock for register accesses
+
+Examples:
+
+	apdma: dma-controller@11000380 {
+		compatible = "mediatek,mt2712-uart-dma";
+		reg = <0 0x11000380 0 0x400>;
+		interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 65 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 66 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 67 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 68 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 69 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 70 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&pericfg CLK_PERI_AP_DMA>;
+		clock-names = "apdma";
+		#dma-cells = <1>;
+	};
+
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2 0/4] add uart DMA function
From: Long Cheng @ 2018-12-05  8:42 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Sean Wang, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen

In Mediatek SOCs, the uart can support DMA function.
Base on DMA engine formwork, we add the DMA code to support uart. And put the code under drivers/dma.

This series contains document bindings, Kconfig to control the function enable or not,
device tree including interrupt and dma device node, the code of UART DM

Changes compared to v1:
-mian revised file, 8250_mtk_dma.c
--parameters renamed for standard
--remove atomic operation

Long Cheng (4):
  dt-bindings: dma: uart: add uart dma bindings
  dmaengine: mtk_uart_dma: add Mediatek uart DMA support
  serial: 8250-mtk: add uart DMA support
  arm: dts: mt2701: add uart APDMA to device tree

 .../devicetree/bindings/dma/8250_mtk_dma.txt       |   33 +
 arch/arm64/boot/dts/mediatek/mt2712e.dtsi          |   50 ++
 drivers/dma/mediatek/8250_mtk_dma.c                |  894 ++++++++++++++++++++
 drivers/dma/mediatek/Kconfig                       |   11 +
 drivers/dma/mediatek/Makefile                      |    1 +
 drivers/tty/serial/8250/8250_mtk.c                 |  210 ++++-
 6 files changed, 1198 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
 create mode 100644 drivers/dma/mediatek/8250_mtk_dma.c

^ permalink raw reply

* Re: [PATCH 07/14] clock: milbeaut: Add Milbeaut M10V clock control
From: Stephen Boyd @ 2018-12-05  6:57 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: sugaya.taichi, DTML, linux-arm-kernel, linux-clk,
	Linux Kernel Mailing List, linux-serial, Michael Turquette,
	Rob Herring, Mark Rutland, Greg Kroah-Hartman, Daniel Lezcano,
	Thomas Gleixner, Russell King, Jiri Slaby, Masami Hiramatsu,
	Jassi Brar
In-Reply-To: <CAK7LNARwq7Qc527L8P8_=taApi-UE3knHceKBUosi5Yx97+row@mail.gmail.com>

Quoting Masahiro Yamada (2018-12-04 20:26:06)
> On Wed, Dec 5, 2018 at 3:14 AM Stephen Boyd <sboyd@kernel.org> wrote:
> >
> > Quoting Masahiro Yamada (2018-12-04 03:03:53)
> > > Hi Stephen,
> > >
> > >
> > > On Fri, Nov 30, 2018 at 5:31 PM Stephen Boyd <sboyd@kernel.org> wrote:
> > > >
> > > > Quoting Sugaya Taichi (2018-11-18 17:01:12)
> > > > > Add Milbeaut M10V clock ( including PLL ) control.
> > > >
> > > > Please give some more details here.
> > > >
> > > > >
> > > > > Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
> > > > > ---
> > > > >  drivers/clk/Makefile   |   1 +
> > > > >  drivers/clk/clk-m10v.c | 671 +++++++++++++++++++++++++++++++++++++++++++++++++
> > > >
> > > > And this is different from Uniphier? Maybe we need a socionext
> > > > directory under drivers/clk/.
> > >
> > >
> > >
> > > This is always a difficult question,
> > > and I do not have a strong opinion.
> > >
> > >
> > > I am fine with moving the files to drivers/clk/socionext
> > > although no file would be shared.
> > >
> > >
> > > FYI
> > >
> > > UniPhier and Milbeaut are completely different platforms
> > > developed/maintained by different teams.
> > >
> > > They happen to live in the same company now
> > > just because Socionext merged the LSI business from Panasonic and Fujitsu.
> > >
> > > UniPhier originates in Panasonic, while Milbeaut in Fujitsu.
> > >
> >
> > Thanks for the background info. I'd prefer to defer to however the dts
> > files are getting split up into directories. If they're all put under
> > arch/arm64/boot/dts/socionext/ then I would say combine the two clk
> > drivers into a socionext directory. Otherwise, keep them split out.
> 
> 
> If you want to align with the DT directory structure,
> the answer is clear.
> 
> 
> Milbeaut DT files will be put together with UniPhier ones
> into socionext directory.
> 
> 
> For arm64, DT directories are already sorted out by vendors.
> 
> Even 32-bit ARM is going to that way.
> 
> Rob Herring just posted a python script
> to move all DT files in arch/arm/boot/dts/
> into vendor subdirectories.
> 
> 
> Please let me know if you want me to
> move drivers/clk/uniphier/* to drivers/clk/socionext/*.
> 

Maybe the dts needs to be split up instead? Looks like the gpio drivers
are in a uniphier directory and there is some precedence to keep the
"taken over" company name when vendors are merged into other vendors.
Maybe that's how things have happened here? It would be nice to be
consistent, but I leave the decision up to you to figure out if that
really matters to you. I'll be fine either way.

^ permalink raw reply

* Re: [PATCH 11/14] pinctrl: milbeaut: Add Milbeaut M10V pinctrl
From: Sugaya, Taichi @ 2018-12-05  5:03 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: linux-clk, DTML, linux-arm-kernel, Linux Kernel Mailing List,
	linux-serial, Michael Turquette, Stephen Boyd, Rob Herring,
	Mark Rutland, Greg Kroah-Hartman, Daniel Lezcano, Thomas Gleixner,
	Russell King, Jiri Slaby, Masami Hiramatsu, Jassi Brar
In-Reply-To: <CAK7LNASnBobLm51VL=EVt+97-KBX3vEqXGjTH-L-q1Qn8_GUQA@mail.gmail.com>

Hi

Thank you for your comments.

On 2018/12/04 20:23, Masahiro Yamada wrote:
> Hi Sugaya-san
> 
> On Mon, Nov 19, 2018 at 10:01 AM Sugaya Taichi
> <sugaya.taichi@socionext.com> wrote:
>>
>> Add Milbeaut M10V pinctrl.
>> The M10V has the pins that can be used GPIOs or take multiple other
>> functions.
>>
>> Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
> 
> 
> This patch was sent to:
> 
> linux-clk@vger.kernel.org,
> devicetree@vger.kernel.org,
> linux-arm-kernel@lists.infradead.org,
> linux-kernel@vger.kernel.org,
> linux-serial@vger.kernel.org
> 
> 
> 
> 
> Unfortunately, the most important ML
> 
>    linux-gpio@vger.kernel.org
> 
> was not addressed.
> 
> 
> The pinctrl maintainer may not notice this patch.

Ah I took a critical mistake...

> 
> 
> 
> 
> 
> 
> 
>> diff --git a/drivers/pinctrl/pinctrl-m10v.c b/drivers/pinctrl/pinctrl-m10v.c
>> new file mode 100644
>> index 0000000..d4ca713
>> --- /dev/null
>> +++ b/drivers/pinctrl/pinctrl-m10v.c
>> @@ -0,0 +1,765 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2018 Socionext Ltd.
>> + * Copyright (C) 2015 Linaro Ltd.
>> + * Author: Jassi Brar <jaswinder.singh@linaro.org>
> 
> 
> My company name is "Socionext Inc." instead of "Socionext Ltd."

Yes, modify it.

> 
> 
> 
> 
> 
> 
> 
>> +static struct platform_driver m10v_pinctrl_driver = {
>> +       .probe  = m10v_pinctrl_probe,
>> +       .driver = {
>> +               .name           = "m10v-pinctrl",
>> +               .of_match_table = m10v_pmatch,
>> +       },
>> +};
>> +
>> +static int __init m10v_pinctrl_init(void)
>> +{
>> +       return platform_driver_register(&m10v_pinctrl_driver);
>> +}
>> +arch_initcall(m10v_pinctrl_init);
> 
> 
> Can't it be builtin_platform_driver()?

I think using builtin_platform_driver() is no problem.

> 
> Which device requires this to be arch_initcall()?

This driver was originally a module, so no need to be arch_initcall().
I will use arch_initcall() instead.

Thanks
Sugaya Taichi

> 
> 
> 
> 
> 
> --
> Best Regards
> Masahiro Yamada
> 

^ permalink raw reply

* Re: [PATCH 07/14] clock: milbeaut: Add Milbeaut M10V clock control
From: Masahiro Yamada @ 2018-12-05  4:26 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: sugaya.taichi, DTML, linux-arm-kernel, linux-clk,
	Linux Kernel Mailing List, linux-serial, Michael Turquette,
	Rob Herring, Mark Rutland, Greg Kroah-Hartman, Daniel Lezcano,
	Thomas Gleixner, Russell King, Jiri Slaby, Masami Hiramatsu,
	Jassi Brar
In-Reply-To: <154394724886.88331.4940706362054124931@swboyd.mtv.corp.google.com>

On Wed, Dec 5, 2018 at 3:14 AM Stephen Boyd <sboyd@kernel.org> wrote:
>
> Quoting Masahiro Yamada (2018-12-04 03:03:53)
> > Hi Stephen,
> >
> >
> > On Fri, Nov 30, 2018 at 5:31 PM Stephen Boyd <sboyd@kernel.org> wrote:
> > >
> > > Quoting Sugaya Taichi (2018-11-18 17:01:12)
> > > > Add Milbeaut M10V clock ( including PLL ) control.
> > >
> > > Please give some more details here.
> > >
> > > >
> > > > Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
> > > > ---
> > > >  drivers/clk/Makefile   |   1 +
> > > >  drivers/clk/clk-m10v.c | 671 +++++++++++++++++++++++++++++++++++++++++++++++++
> > >
> > > And this is different from Uniphier? Maybe we need a socionext
> > > directory under drivers/clk/.
> >
> >
> >
> > This is always a difficult question,
> > and I do not have a strong opinion.
> >
> >
> > I am fine with moving the files to drivers/clk/socionext
> > although no file would be shared.
> >
> >
> > FYI
> >
> > UniPhier and Milbeaut are completely different platforms
> > developed/maintained by different teams.
> >
> > They happen to live in the same company now
> > just because Socionext merged the LSI business from Panasonic and Fujitsu.
> >
> > UniPhier originates in Panasonic, while Milbeaut in Fujitsu.
> >
>
> Thanks for the background info. I'd prefer to defer to however the dts
> files are getting split up into directories. If they're all put under
> arch/arm64/boot/dts/socionext/ then I would say combine the two clk
> drivers into a socionext directory. Otherwise, keep them split out.


If you want to align with the DT directory structure,
the answer is clear.


Milbeaut DT files will be put together with UniPhier ones
into socionext directory.


For arm64, DT directories are already sorted out by vendors.

Even 32-bit ARM is going to that way.

Rob Herring just posted a python script
to move all DT files in arch/arm/boot/dts/
into vendor subdirectories.


Please let me know if you want me to
move drivers/clk/uniphier/* to drivers/clk/socionext/*.



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply

* Re: [PATCH v2 2/2] serial: 8250: Rate limit serial port rx interrupts during input overruns
From: Darwin Dingel @ 2018-12-05  1:41 UTC (permalink / raw)
  To: andriy.shevchenko@linux.intel.com, gregkh@linuxfoundation.org,
	jslaby@suse.com, linux-serial@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org
In-Reply-To: <20181205005058.14095-2-darwin.dingel@alliedtelesis.co.nz>

So sorry for the premature "Reviewed-by" bits on the commit messages on the previous patches
submitted. Please ignore these while I fix the patch up for v3.


Regards,
Darwin
________________________________________
From: Darwin Dingel <darwin.dingel@alliedtelesis.co.nz>
Sent: Wednesday, 5 December 2018 1:50 p.m.
To: andriy.shevchenko@linux.intel.com; gregkh@linuxfoundation.org; jslaby@suse.com; linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org; Darwin Dingel
Subject: [PATCH v2 2/2] serial: 8250: Rate limit serial port rx interrupts during input overruns

When a serial port gets faulty or gets flooded with inputs, its interrupt
handler starts to work double time to get the characters to the workqueue
for the tty layer to handle them. When this busy time on the serial/tty
subsystem happens during boot, where it is also busy on the userspace
trying to initialise, some processes can continuously get preempted
and will be on hold until the interrupts subside.

The fix is to backoff on processing received characters for a specified
amount of time when an input overrun is seen (received a new character
before the previous one is processed). This only stops receive and will
continue to transmit characters to serial port. After the backoff period
is done, it receive will be re-enabled. This is optional and will only
be enabled by setting 'overrun-throttle-ms' in the dts.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jiri Slaby <jslaby@suse.com>
Signed-off-by: Darwin Dingel <darwin.dingel@alliedtelesis.co.nz>
---
 drivers/tty/serial/8250/8250_core.c | 25 +++++++++++++++++++++++++
 drivers/tty/serial/8250/8250_fsl.c  | 23 ++++++++++++++++++++++-
 drivers/tty/serial/8250/8250_of.c   |  5 +++++
 include/linux/serial_8250.h         |  4 ++++
 4 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 94f3e1c64490..189ab1212d9a 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -942,6 +942,21 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
        return NULL;
 }

+static void serial_8250_overrun_backoff_work(struct work_struct *work)
+{
+       struct uart_8250_port *up =
+           container_of(to_delayed_work(work), struct uart_8250_port,
+                        overrun_backoff);
+       struct uart_port *port = &up->port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       up->ier |= UART_IER_RLSI | UART_IER_RDI;
+       up->port.read_status_mask |= UART_LSR_DR;
+       serial_out(up, UART_IER, up->ier);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
 /**
  *     serial8250_register_8250_port - register a serial port
  *     @up: serial port template
@@ -1056,6 +1071,16 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
                        ret = 0;
                }
        }
+
+       /* Initialise interrupt backoff work if required */
+       if (up->overrun_backoff_time_ms > 0) {
+               uart->overrun_backoff_time_ms = up->overrun_backoff_time_ms;
+               INIT_DELAYED_WORK(&uart->overrun_backoff,
+                                 serial_8250_overrun_backoff_work);
+       } else {
+               uart->overrun_backoff_time_ms = 0;
+       }
+
        mutex_unlock(&serial_mutex);

        return ret;
diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
index 6640a4c7ddd1..bb9571eed275 100644
--- a/drivers/tty/serial/8250/8250_fsl.c
+++ b/drivers/tty/serial/8250/8250_fsl.c
@@ -45,8 +45,29 @@ int fsl8250_handle_irq(struct uart_port *port)

        lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);

-       if (lsr & (UART_LSR_DR | UART_LSR_BI))
+       /* Process incoming characters first */
+       if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
+           (up->ier & (UART_IER_RLSI | UART_IER_RDI))) {
                lsr = serial8250_rx_chars(up, lsr);
+       }
+
+       /* Stop processing interrupts on input overrun */
+       if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
+               unsigned long delay;
+
+               up->ier = port->serial_in(port, UART_IER);
+               if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
+                       port->ops->stop_rx(port);
+               } else {
+                       /* Keep restarting the timer until
+                        * the input overrun subsides.
+                        */
+                       cancel_delayed_work(&up->overrun_backoff);
+               }
+
+               delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
+               schedule_delayed_work(&up->overrun_backoff, delay);
+       }

        serial8250_modem_status(up);

diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 877fd7f8a8ed..a1a85805d010 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -240,6 +240,11 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
        if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control"))
                port8250.capabilities |= UART_CAP_AFE;

+       if (of_property_read_u32(ofdev->dev.of_node,
+                       "overrun-throttle-ms",
+                       &port8250.overrun_backoff_time_ms) != 0)
+               port8250.overrun_backoff_time_ms = 0;
+
        ret = serial8250_register_8250_port(&port8250);
        if (ret < 0)
                goto err_dispose;
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 18e21427bce4..5a655ba8d273 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -134,6 +134,10 @@ struct uart_8250_port {
        void                    (*dl_write)(struct uart_8250_port *, int);

        struct uart_8250_em485 *em485;
+
+       /* Serial port overrun backoff */
+       struct delayed_work overrun_backoff;
+       u32 overrun_backoff_time_ms;
 };

 static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up)
--
2.19.2

changes in v2:
- separated dts binding to another patch

^ permalink raw reply related

* [PATCH v2 2/2] serial: 8250: Rate limit serial port rx interrupts during input overruns
From: Darwin Dingel @ 2018-12-05  0:50 UTC (permalink / raw)
  To: andriy.shevchenko, gregkh, jslaby, linux-serial
  Cc: linux-kernel, Darwin Dingel
In-Reply-To: <20181205005058.14095-1-darwin.dingel@alliedtelesis.co.nz>

When a serial port gets faulty or gets flooded with inputs, its interrupt
handler starts to work double time to get the characters to the workqueue
for the tty layer to handle them. When this busy time on the serial/tty
subsystem happens during boot, where it is also busy on the userspace
trying to initialise, some processes can continuously get preempted
and will be on hold until the interrupts subside.

The fix is to backoff on processing received characters for a specified
amount of time when an input overrun is seen (received a new character
before the previous one is processed). This only stops receive and will
continue to transmit characters to serial port. After the backoff period
is done, it receive will be re-enabled. This is optional and will only
be enabled by setting 'overrun-throttle-ms' in the dts.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jiri Slaby <jslaby@suse.com>
Signed-off-by: Darwin Dingel <darwin.dingel@alliedtelesis.co.nz>
---
 drivers/tty/serial/8250/8250_core.c | 25 +++++++++++++++++++++++++
 drivers/tty/serial/8250/8250_fsl.c  | 23 ++++++++++++++++++++++-
 drivers/tty/serial/8250/8250_of.c   |  5 +++++
 include/linux/serial_8250.h         |  4 ++++
 4 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 94f3e1c64490..189ab1212d9a 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -942,6 +942,21 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
 	return NULL;
 }
 
+static void serial_8250_overrun_backoff_work(struct work_struct *work)
+{
+	struct uart_8250_port *up =
+	    container_of(to_delayed_work(work), struct uart_8250_port,
+			 overrun_backoff);
+	struct uart_port *port = &up->port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	up->ier |= UART_IER_RLSI | UART_IER_RDI;
+	up->port.read_status_mask |= UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
 /**
  *	serial8250_register_8250_port - register a serial port
  *	@up: serial port template
@@ -1056,6 +1071,16 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 			ret = 0;
 		}
 	}
+
+	/* Initialise interrupt backoff work if required */
+	if (up->overrun_backoff_time_ms > 0) {
+		uart->overrun_backoff_time_ms = up->overrun_backoff_time_ms;
+		INIT_DELAYED_WORK(&uart->overrun_backoff,
+				  serial_8250_overrun_backoff_work);
+	} else {
+		uart->overrun_backoff_time_ms = 0;
+	}
+
 	mutex_unlock(&serial_mutex);
 
 	return ret;
diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
index 6640a4c7ddd1..bb9571eed275 100644
--- a/drivers/tty/serial/8250/8250_fsl.c
+++ b/drivers/tty/serial/8250/8250_fsl.c
@@ -45,8 +45,29 @@ int fsl8250_handle_irq(struct uart_port *port)
 
 	lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
 
-	if (lsr & (UART_LSR_DR | UART_LSR_BI))
+	/* Process incoming characters first */
+	if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
+	    (up->ier & (UART_IER_RLSI | UART_IER_RDI))) {
 		lsr = serial8250_rx_chars(up, lsr);
+	}
+
+	/* Stop processing interrupts on input overrun */
+	if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
+		unsigned long delay;
+
+		up->ier = port->serial_in(port, UART_IER);
+		if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
+			port->ops->stop_rx(port);
+		} else {
+			/* Keep restarting the timer until
+			 * the input overrun subsides.
+			 */
+			cancel_delayed_work(&up->overrun_backoff);
+		}
+
+		delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
+		schedule_delayed_work(&up->overrun_backoff, delay);
+	}
 
 	serial8250_modem_status(up);
 
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 877fd7f8a8ed..a1a85805d010 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -240,6 +240,11 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
 	if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control"))
 		port8250.capabilities |= UART_CAP_AFE;
 
+	if (of_property_read_u32(ofdev->dev.of_node,
+			"overrun-throttle-ms",
+			&port8250.overrun_backoff_time_ms) != 0)
+		port8250.overrun_backoff_time_ms = 0;
+
 	ret = serial8250_register_8250_port(&port8250);
 	if (ret < 0)
 		goto err_dispose;
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 18e21427bce4..5a655ba8d273 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -134,6 +134,10 @@ struct uart_8250_port {
 	void			(*dl_write)(struct uart_8250_port *, int);
 
 	struct uart_8250_em485 *em485;
+
+	/* Serial port overrun backoff */
+	struct delayed_work overrun_backoff;
+	u32 overrun_backoff_time_ms;
 };
 
 static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up)
-- 
2.19.2

changes in v2:
- separated dts binding to another patch

^ permalink raw reply related

* [PATCH v2 1/2] serial: 8250: Add dts binding to rate limit serial port during overruns
From: Darwin Dingel @ 2018-12-05  0:50 UTC (permalink / raw)
  To: andriy.shevchenko, gregkh, jslaby, linux-serial
  Cc: linux-kernel, Darwin Dingel

When this dts binding is enabled and input overrun on the serial port is
detected, serial port receive will be throttled to give some breathing room
for processing other tasks. This is particularly useful to avoid situations
where serial port HW malfunctions triggering input overruns and then other
processes get starved of processing time. Value provided will be in
milliseconds.

&serial0{
	overrun-throttle-ms = <500>;
};

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jiri Slaby <jslaby@suse.com>
Signed-off-by: Darwin Dingel <darwin.dingel@alliedtelesis.co.nz>
---
 Documentation/devicetree/bindings/serial/8250.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt
index aeb6db4e35c3..da50321da34d 100644
--- a/Documentation/devicetree/bindings/serial/8250.txt
+++ b/Documentation/devicetree/bindings/serial/8250.txt
@@ -51,6 +51,7 @@ Optional properties:
 - tx-threshold: Specify the TX FIFO low water indication for parts with
   programmable TX FIFO thresholds.
 - resets : phandle + reset specifier pairs
+- overrun-throttle-ms : how long to pause uart rx when input overrun is encountered.
 
 Note:
 * fsl,ns16550:
-- 
2.19.2

changes in v2:
- separated dts binding to another patch

^ permalink raw reply related

* Re: [PATCH 10/14] dt-bindings: pinctrl: milbeaut: Add Milbeaut M10V pinctrl description
From: Rob Herring @ 2018-12-04 23:11 UTC (permalink / raw)
  To: Sugaya Taichi
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-kernel,
	linux-serial, Michael Turquette, Stephen Boyd, Mark Rutland,
	Greg Kroah-Hartman, Daniel Lezcano, Thomas Gleixner, Russell King,
	Jiri Slaby, Masami Hiramatsu, Jassi Brar
In-Reply-To: <1542589336-13925-1-git-send-email-sugaya.taichi@socionext.com>

On Mon, Nov 19, 2018 at 10:02:12AM +0900, Sugaya Taichi wrote:
> Add DT bindings document for Milbeaut M10V pinctrl.
> 
> Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
> ---
>  .../pinctrl/socionext,milbeaut-pinctrl.txt         | 33 ++++++++++++++++++++++
>  1 file changed, 33 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pinctrl/socionext,milbeaut-pinctrl.txt
> 
> diff --git a/Documentation/devicetree/bindings/pinctrl/socionext,milbeaut-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/socionext,milbeaut-pinctrl.txt
> new file mode 100644
> index 0000000..7469189
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/socionext,milbeaut-pinctrl.txt
> @@ -0,0 +1,33 @@
> +Milbeaut SoCs pin controller
> +
> +Required properties:
> +- compatible: should be one of the following:
> +    "socionext,milbeaut-m10v-pinctrl"  - for m10v SoC
> +- reg: offset and length of the register set.
> +- reg-names: should be "pinctrl", "exiu".
> +- gpio-cells; should be 2.

#gpio-cells

gpio-controller?

> +- interrupt-cells: should be 2.

#interrupt-cells

interrupt-controller?

> +- clocks: phandle to the input clock.
> +- interrupts: three interrupts specifer.
> +- interrupt-names: corresponds "interrupts" factor.
> +
> +Example:
> +	pinctrl: pinctrl@1d022000 {
> +		compatible = "socionext,milbeaut-m10v-pinctrl";
> +		reg = <0x1d022000 0x1000>,
> +			<0x1c26f000 0x1000>;
> +		reg-names = "pinctrl", "exiu";
> +		gpio-controller;
> +		#gpio-cells = <2>;
> +		interrupt-controller;
> +		#interrupt-cells = <2>;
> +		clocks = <&dummy_clk>;
> +		interrupts = <0 54 4>, <0 55 4>, <0 56 4>, <0 57 4>,
> +				<0 58 4>, <0 59 4>, <0 60 4>, <0 61 4>,
> +				<0 62 4>, <0 63 4>, <0 64 4>, <0 65 4>,
> +				<0 66 4>, <0 67 4>, <0 68 4>, <0 69 4>;
> +		interrupt-names = "pin-48", "pin-49", "pin-50", "pin-51",
> +				"pin-52", "pin-53", "pin-54", "pin-55",
> +				"pin-56", "pin-57", "pin-58", "pin-59",
> +				"pin-60", "pin-61", "pin-62", "pin-63";
> +	}
> -- 
> 1.9.1
> 

^ permalink raw reply

* Re: [PATCH 04/14] dt-bindings: timer: Add Milbeaut M10V timer description
From: Rob Herring @ 2018-12-04 23:03 UTC (permalink / raw)
  To: Sugaya Taichi
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-kernel,
	linux-serial, Michael Turquette, Stephen Boyd, Mark Rutland,
	Greg Kroah-Hartman, Daniel Lezcano, Thomas Gleixner, Russell King,
	Jiri Slaby, Masami Hiramatsu, Jassi Brar
In-Reply-To: <1542589274-13878-5-git-send-email-sugaya.taichi@socionext.com>

On Mon, Nov 19, 2018 at 10:01:09AM +0900, Sugaya Taichi wrote:
> Add DT bindings document for Milbeaut M10V timer.
> 
> Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
> ---
>  .../bindings/timer/socionext,milbeaut-timer.txt         | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/timer/socionext,milbeaut-timer.txt
> 
> diff --git a/Documentation/devicetree/bindings/timer/socionext,milbeaut-timer.txt b/Documentation/devicetree/bindings/timer/socionext,milbeaut-timer.txt
> new file mode 100644
> index 0000000..ddb1b31
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/socionext,milbeaut-timer.txt
> @@ -0,0 +1,17 @@
> +Milbeaut SoCs Timer Controller
> +
> +Required properties:
> +
> +- compatible : should be "socionext,milbeaut-m10v-timer"
> +- reg : Specifies base physical address and size of the registers.

How many register ranges? Looks like 2.

> +- interrupts : The interrupt of the first timer
> +- clocks: should be "rclk"
> +
> +Example:
> +
> +timer {

timer@1e000050

> +	compatible = "socionext,milbeaut-m10v-timer";
> +	reg = <0x1e000050 0x10>, <0x1e000060 0x10>;
> +	interrupts = <0 91 4>;
> +	clocks = <&rclk>;
> +};
> -- 
> 1.9.1
> 

^ permalink raw reply

* Re: [PATCH 07/14] clock: milbeaut: Add Milbeaut M10V clock control
From: Stephen Boyd @ 2018-12-04 18:15 UTC (permalink / raw)
  To: Sugaya, Taichi, devicetree, linux-arm-kernel, linux-clk,
	linux-kernel, linux-serial
  Cc: Michael Turquette, Rob Herring, Mark Rutland, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Russell King, Jiri Slaby,
	Masami Hiramatsu, Jassi Brar
In-Reply-To: <298cd5a5-66cf-0936-405e-59bcc7c396ed@socionext.com>

Quoting Sugaya, Taichi (2018-12-04 00:26:16)
> On 2018/11/30 17:31, Stephen Boyd wrote:
> > Quoting Sugaya Taichi (2018-11-18 17:01:12)
> >> +void __init m10v_clk_mux_setup(struct device_node *node)
> >> +{
> >> +       const char *clk_name = node->name;
> >> +       struct clk_init_data init;
> >> +       const char **parent_names;
> >> +       struct m10v_mux *mcm;
> >> +       struct clk *clk;
> >> +       int i, parents;
> >> +
> >> +       if (!m10v_clk_iomap())
> >> +               return;
> >> +
> >> +       of_property_read_string(node, "clock-output-names", &clk_name);
> >> +
> >> +       parents = of_clk_get_parent_count(node);
> >> +       if (parents < 2) {
> >> +               pr_err("%s: not a mux\n", clk_name);
> > 
> > How is this possible?
> 
> When the node has more than 1 clks...
> Or I am misunderstanding your question?

This looks like code that's checking DT for correctness. We don't
typically do that in the kernel because the kernel isn't a DT validator.
That's all I'm saying. I think this comment is not useful if the driver
design is done to specify parent linkages in C code instead of DT, so
don't worry about this too much.

^ permalink raw reply

* Re: [PATCH 07/14] clock: milbeaut: Add Milbeaut M10V clock control
From: Stephen Boyd @ 2018-12-04 18:14 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: sugaya.taichi, DTML, linux-arm-kernel, linux-clk,
	Linux Kernel Mailing List, linux-serial, Michael Turquette,
	Rob Herring, Mark Rutland, Greg Kroah-Hartman, Daniel Lezcano,
	Thomas Gleixner, Russell King, Jiri Slaby, Masami Hiramatsu,
	Jassi Brar
In-Reply-To: <CAK7LNAQEeLo=PW=DygyVxVHyn5jgRP18nb0cK+u1wGNd4BUh7Q@mail.gmail.com>

Quoting Masahiro Yamada (2018-12-04 03:03:53)
> Hi Stephen,
> 
> 
> On Fri, Nov 30, 2018 at 5:31 PM Stephen Boyd <sboyd@kernel.org> wrote:
> >
> > Quoting Sugaya Taichi (2018-11-18 17:01:12)
> > > Add Milbeaut M10V clock ( including PLL ) control.
> >
> > Please give some more details here.
> >
> > >
> > > Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
> > > ---
> > >  drivers/clk/Makefile   |   1 +
> > >  drivers/clk/clk-m10v.c | 671 +++++++++++++++++++++++++++++++++++++++++++++++++
> >
> > And this is different from Uniphier? Maybe we need a socionext
> > directory under drivers/clk/.
> 
> 
> 
> This is always a difficult question,
> and I do not have a strong opinion.
> 
> 
> I am fine with moving the files to drivers/clk/socionext
> although no file would be shared.
> 
> 
> FYI
> 
> UniPhier and Milbeaut are completely different platforms
> developed/maintained by different teams.
> 
> They happen to live in the same company now
> just because Socionext merged the LSI business from Panasonic and Fujitsu.
> 
> UniPhier originates in Panasonic, while Milbeaut in Fujitsu.
> 

Thanks for the background info. I'd prefer to defer to however the dts
files are getting split up into directories. If they're all put under
arch/arm64/boot/dts/socionext/ then I would say combine the two clk
drivers into a socionext directory. Otherwise, keep them split out.

^ permalink raw reply

* [PATCH 3/3] RISC-V: Remove EARLY_PRINTK support
From: Anup Patel @ 2018-12-04 13:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Palmer Dabbelt, Albert Ou
  Cc: Atish Patra, Christoph Hellwig, Rob Herring, linux-riscv,
	linux-kernel, linux-serial, Anup Patel
In-Reply-To: <20181204135507.3706-1-anup@brainfault.org>

The EARLY_PRINTK using SBI console calls is not required
any more because we now have RISC-V SBI support in generic
earlycon framework.

Signed-off-by: Anup Patel <anup@brainfault.org>
---
 arch/riscv/Kconfig.debug  |  2 --
 arch/riscv/kernel/setup.c | 28 ----------------------------
 2 files changed, 30 deletions(-)

diff --git a/arch/riscv/Kconfig.debug b/arch/riscv/Kconfig.debug
index c5a72f17c469..e69de29bb2d1 100644
--- a/arch/riscv/Kconfig.debug
+++ b/arch/riscv/Kconfig.debug
@@ -1,2 +0,0 @@
-config EARLY_PRINTK
-	def_bool y
diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
index 2c290e6aaa6e..fc8006a042eb 100644
--- a/arch/riscv/kernel/setup.c
+++ b/arch/riscv/kernel/setup.c
@@ -35,31 +35,9 @@
 #include <asm/sections.h>
 #include <asm/pgtable.h>
 #include <asm/smp.h>
-#include <asm/sbi.h>
 #include <asm/tlbflush.h>
 #include <asm/thread_info.h>
 
-#ifdef CONFIG_EARLY_PRINTK
-static void sbi_console_write(struct console *co, const char *buf,
-			      unsigned int n)
-{
-	int i;
-
-	for (i = 0; i < n; ++i) {
-		if (buf[i] == '\n')
-			sbi_console_putchar('\r');
-		sbi_console_putchar(buf[i]);
-	}
-}
-
-struct console riscv_sbi_early_console_dev __initdata = {
-	.name	= "early",
-	.write	= sbi_console_write,
-	.flags	= CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
-	.index	= -1
-};
-#endif
-
 #ifdef CONFIG_DUMMY_CONSOLE
 struct screen_info screen_info = {
 	.orig_video_lines	= 30,
@@ -219,12 +197,6 @@ static void __init setup_bootmem(void)
 
 void __init setup_arch(char **cmdline_p)
 {
-#if defined(CONFIG_EARLY_PRINTK)
-       if (likely(early_console == NULL)) {
-               early_console = &riscv_sbi_early_console_dev;
-               register_console(early_console);
-       }
-#endif
 	*cmdline_p = boot_command_line;
 
 	parse_early_param();
-- 
2.17.1

^ permalink raw reply related

* [PATCH 2/3] RISC-V: defconfig: Enable RISC-V SBI earlycon support
From: Anup Patel @ 2018-12-04 13:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Palmer Dabbelt, Albert Ou
  Cc: Atish Patra, Christoph Hellwig, Rob Herring, linux-riscv,
	linux-kernel, linux-serial, Anup Patel
In-Reply-To: <20181204135507.3706-1-anup@brainfault.org>

This patch enables RISC-V SBI earlycon support in default defconfig
so that we can use "earlycon=sbi" in kernel parameters for early
debug prints.

Signed-off-by: Anup Patel <anup@brainfault.org>
---
 arch/riscv/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
index ef4f15df9adf..f399659d3b8d 100644
--- a/arch/riscv/configs/defconfig
+++ b/arch/riscv/configs/defconfig
@@ -46,6 +46,7 @@ CONFIG_INPUT_MOUSEDEV=y
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
 CONFIG_HVC_RISCV_SBI=y
 # CONFIG_PTP_1588_CLOCK is not set
 CONFIG_DRM=y
-- 
2.17.1

^ permalink raw reply related

* [PATCH 1/3] tty/serial: Add RISC-V SBI earlycon support
From: Anup Patel @ 2018-12-04 13:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Palmer Dabbelt, Albert Ou
  Cc: Atish Patra, Christoph Hellwig, Rob Herring, linux-riscv,
	linux-kernel, linux-serial, Anup Patel
In-Reply-To: <20181204135507.3706-1-anup@brainfault.org>

In RISC-V, the M-mode runtime firmware provide SBI calls for
debug prints. This patch adds earlycon support using RISC-V
SBI console calls. To enable it, just pass "earlycon=sbi" in
kernel parameters.

Signed-off-by: Anup Patel <anup@brainfault.org>
---
 drivers/tty/serial/Kconfig              | 12 +++++++++++
 drivers/tty/serial/Makefile             |  1 +
 drivers/tty/serial/earlycon-riscv-sbi.c | 28 +++++++++++++++++++++++++
 3 files changed, 41 insertions(+)
 create mode 100644 drivers/tty/serial/earlycon-riscv-sbi.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 32886c304641..287bb41ac814 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -85,6 +85,18 @@ config SERIAL_EARLYCON_ARM_SEMIHOST
 	  with "earlycon=smh" on the kernel command line. The console is
 	  enabled when early_param is processed.
 
+config SERIAL_EARLYCON_RISCV_SBI
+	bool "Early console using RISC-V SBI"
+	depends on RISCV
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
+	help
+	  Support for early debug console using RISC-V SBI. This enables
+	  the console before standard serial driver is probed. This is enabled
+	  with "earlycon=sbi" on the kernel command line. The console is
+	  enabled when early_param is processed.
+
 config SERIAL_SB1250_DUART
 	tristate "BCM1xxx on-chip DUART serial support"
 	depends on SIBYTE_SB1xxx_SOC=y
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index daac675612df..3ce26ce08616 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o
 
 obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
 obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o
+obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o
 
 # These Sparc drivers have to appear before others such as 8250
 # which share ttySx minor node space.  Otherwise console device
diff --git a/drivers/tty/serial/earlycon-riscv-sbi.c b/drivers/tty/serial/earlycon-riscv-sbi.c
new file mode 100644
index 000000000000..e1a551aae336
--- /dev/null
+++ b/drivers/tty/serial/earlycon-riscv-sbi.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RISC-V SBI based earlycon
+ *
+ * Copyright (C) 2018 Anup Patel <anup@brainfault.org>
+ */
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <asm/sbi.h>
+
+static void sbi_console_write(struct console *con,
+			      const char *s, unsigned int n)
+{
+	int i;
+
+	for (i = 0; i < n; ++i)
+		sbi_console_putchar(s[i]);
+}
+
+static int __init early_sbi_setup(struct earlycon_device *device,
+				  const char *opt)
+{
+	device->con->write = sbi_console_write;
+	return 0;
+}
+EARLYCON_DECLARE(sbi, early_sbi_setup);
-- 
2.17.1

^ permalink raw reply related

* [PATCH 0/3] RISC-V SBI earlycon
From: Anup Patel @ 2018-12-04 13:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Palmer Dabbelt, Albert Ou
  Cc: Atish Patra, Christoph Hellwig, Rob Herring, linux-riscv,
	linux-kernel, linux-serial, Anup Patel

This patchset adds RISC-V SBI earlycon and removes RISC-V EARLY_PRINTK.

We should use earlycon over existing EARLY_PRINTK for SBI console because:
1. It's a more generic way of implementing early console for debugging
2. Current RISC-V EARLY_PRINTK is a compile-time option whereas earlycon
   is enabled at run-time via kernel parameters.
3. To use earlycon with SBI, we have to pass "earlycon=sbi" in kernel
   parameters. If earlycon kernel parameter is not provided then kernel
   boots much faster which is very useful in real-world RISC-V deployments.

The patchset is tested on QEMU virt machine. It is based on Linux-4.20-rc5
and can be found at riscv_earlycon_v1 branch of:
https://github.com/avpatel/linux.git

Anup Patel (3):
  tty/serial: Add RISC-V SBI earlycon support
  RISC-V: defconfig: Enable RISC-V SBI earlycon support
  RISC-V: Remove EARLY_PRINTK support

 arch/riscv/Kconfig.debug                |  2 --
 arch/riscv/configs/defconfig            |  1 +
 arch/riscv/kernel/setup.c               | 28 -------------------------
 drivers/tty/serial/Kconfig              | 12 +++++++++++
 drivers/tty/serial/Makefile             |  1 +
 drivers/tty/serial/earlycon-riscv-sbi.c | 28 +++++++++++++++++++++++++
 6 files changed, 42 insertions(+), 30 deletions(-)
 create mode 100644 drivers/tty/serial/earlycon-riscv-sbi.c

-- 
2.17.1

^ permalink raw reply

* Re: [PATCH 02/14] dt-bindings: soc: milbeaut: Add Milbeaut trampoline description
From: Rob Herring @ 2018-12-04 13:32 UTC (permalink / raw)
  To: sugaya.taichi
  Cc: Stephen Boyd, devicetree,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-clk, linux-kernel@vger.kernel.org, open list:SERIAL DRIVERS,
	Michael Turquette, Mark Rutland, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Russell King, Jiri Slaby,
	Masami Hiramatsu, Jassi Brar
In-Reply-To: <90b00858-6e9e-8f7c-f6d4-b35e5daa6eee@socionext.com>

On Tue, Dec 4, 2018 at 5:30 AM Sugaya, Taichi
<sugaya.taichi@socionext.com> wrote:
>
> Hi
>
> On 2018/12/04 0:49, Rob Herring wrote:
> > On Mon, Dec 3, 2018 at 1:42 AM Sugaya, Taichi
> > <sugaya.taichi@socionext.com> wrote:
> >>
> >> Hi,
> >>
> >> On 2018/11/30 17:16, Stephen Boyd wrote:
> >>> Quoting Sugaya, Taichi (2018-11-29 04:24:51)
> >>>> On 2018/11/28 11:01, Stephen Boyd wrote:
> >>>>> Quoting Sugaya Taichi (2018-11-18 17:01:07)
> >>>>>>     create mode 100644 Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt
> >>>>>>
> >>>>>> diff --git a/Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt b/Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt
> >>>>>> new file mode 100644
> >>>>>> index 0000000..f5d906c
> >>>>>> --- /dev/null
> >>>>>> +++ b/Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt
> >>>>>> @@ -0,0 +1,12 @@
> >>>>>> +Socionext M10V SMP trampoline driver binding
> >>>>>> +
> >>>>>> +This is a driver to wait for sub-cores while boot process.
> >>>>>> +
> >>>>>> +- compatible: should be "socionext,smp-trampoline"
> >>>>>> +- reg: should be <0x4C000100 0x100>
> >>>>>> +
> >>>>>> +EXAMPLE
> >>>>>> +       trampoline: trampoline@0x4C000100 {
> >>>>> Drop the 0x part of unit addresses.
> >>>>
> >>>> Okay.
> >>>>
> >>>>
> >>>>>> +               compatible = "socionext,smp-trampoline";
> >>>>>> +               reg = <0x4C000100 0x100>;
> >>>>> Looks like a software construct, which we wouldn't want to put into DT
> >>>>> this way. DT doesn't describe drivers.
> >>>> We would like to use this node only getting the address of the
> >>>> trampoline area
> >>>> in which sub-cores wait.  (They have finished to go to this area in previous
> >>>> bootloader process.)
> >>>
> >>> Is this area part of memory, or a special SRAM? If it's part of memory,
> >>> I would expect this node to be under the reserved-memory node and
> >>> pointed to by some other node that uses this region. Could even be the
> >>> CPU nodes.
> >>
> >> Yes, 0x4C000100 is a part of memory under the reserved-memory node. So
> >> we would like to use the SRAM ( allocated 0x00000000 ) area instead.
> >> BTW, sorry, the trampoline address of this example is simply wrong.  We
> >> were going to use a part of the SRAM from the beginning.
> >>
> >>>
> >>>>
> >>>> So should we embed the constant value in source codes instead of getting
> >>>> from
> >>>> DT because the address is constant at the moment? Or is there other
> >>>> approach?
> >>>>
> >>>
> >>> If it's constant then that also works. Why does it need to come from DT
> >>> at all then?
> >>
> >> We think it is not good to embed constant value in driver codes and do
> >> not have another way...
> >> Are there better ways?
> >
> > If this is just memory, can you use the standard spin-table binding in
> > the DT spec? There are some requirements like 64-bit values even on
> > 32-bit machines (though this gets violated).
>
> The spin-table seems to be used on only 64-bit arch. Have it ever worked
> on 32-bit machine?

Yes.

> And I would like not to use it because avoid violation.

The issue now that I remember is cpu-release-addr is defined to always
be a 64-bit value while some platforms made it a 32-bit value.
'cpu-release-addr' is also used for some other enable-methods.

Rob

^ permalink raw reply

* [PATCH] tty: xilinx_uartps: Correct return value in probe
From: Rajan Vaja @ 2018-12-04 12:51 UTC (permalink / raw)
  To: gregkh, jslaby, michal.simek
  Cc: linux-serial, Rajan Vaja, linux-kernel, jollys, anirudh,
	linux-arm-kernel

Existing driver checks for alternate clock if devm_clk_get() fails
and returns error code for last clock failure. If xilinx_uartps is
called before clock driver, devm_clk_get() returns -EPROBE_DEFER.
In this case, probe should not check for alternate clock as main
clock is already present in DTS and return -EPROBE_DEFER only.

This patch fixes it by not checking for alternate clock when main
clock get returns -EPROBE_DEFER.

Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com>
---
 drivers/tty/serial/xilinx_uartps.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 57c66d2..a23baa4 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1545,27 +1545,25 @@ static int cdns_uart_probe(struct platform_device *pdev)
 	}
 
 	cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (PTR_ERR(cdns_uart_data->pclk) == -EPROBE_DEFER)
+		return PTR_ERR(cdns_uart_data->pclk);
+
 	if (IS_ERR(cdns_uart_data->pclk)) {
 		cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk");
-		if (!IS_ERR(cdns_uart_data->pclk))
-			dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n");
-	}
-	if (IS_ERR(cdns_uart_data->pclk)) {
-		dev_err(&pdev->dev, "pclk clock not found.\n");
-		rc = PTR_ERR(cdns_uart_data->pclk);
-		goto err_out_unregister_driver;
+		if (IS_ERR(cdns_uart_data->pclk))
+			return PTR_ERR(cdns_uart_data->pclk);
+		dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n");
 	}
 
 	cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk");
+	if (PTR_ERR(cdns_uart_data->uartclk) == -EPROBE_DEFER)
+		return PTR_ERR(cdns_uart_data->uartclk);
+
 	if (IS_ERR(cdns_uart_data->uartclk)) {
 		cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk");
-		if (!IS_ERR(cdns_uart_data->uartclk))
-			dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n");
-	}
-	if (IS_ERR(cdns_uart_data->uartclk)) {
-		dev_err(&pdev->dev, "uart_clk clock not found.\n");
-		rc = PTR_ERR(cdns_uart_data->uartclk);
-		goto err_out_unregister_driver;
+		if (IS_ERR(cdns_uart_data->uartclk))
+			return PTR_ERR(cdns_uart_data->uartclk);
+		dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n");
 	}
 
 	rc = clk_prepare_enable(cdns_uart_data->pclk);
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH 02/14] dt-bindings: soc: milbeaut: Add Milbeaut trampoline description
From: Sugaya, Taichi @ 2018-12-04 11:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: Stephen Boyd, devicetree,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-clk, linux-kernel@vger.kernel.org, open list:SERIAL DRIVERS,
	Michael Turquette, Mark Rutland, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Russell King, Jiri Slaby,
	Masami Hiramatsu, Jassi Brar
In-Reply-To: <CAL_Jsq+dnKht3TwCh1qB1H4pYFhQJmSN0nAayU+=PJLAvq0bbQ@mail.gmail.com>

Hi

On 2018/12/04 0:49, Rob Herring wrote:
> On Mon, Dec 3, 2018 at 1:42 AM Sugaya, Taichi
> <sugaya.taichi@socionext.com> wrote:
>>
>> Hi,
>>
>> On 2018/11/30 17:16, Stephen Boyd wrote:
>>> Quoting Sugaya, Taichi (2018-11-29 04:24:51)
>>>> On 2018/11/28 11:01, Stephen Boyd wrote:
>>>>> Quoting Sugaya Taichi (2018-11-18 17:01:07)
>>>>>>     create mode 100644 Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt
>>>>>>
>>>>>> diff --git a/Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt b/Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt
>>>>>> new file mode 100644
>>>>>> index 0000000..f5d906c
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/devicetree/bindings/soc/socionext/socionext,m10v.txt
>>>>>> @@ -0,0 +1,12 @@
>>>>>> +Socionext M10V SMP trampoline driver binding
>>>>>> +
>>>>>> +This is a driver to wait for sub-cores while boot process.
>>>>>> +
>>>>>> +- compatible: should be "socionext,smp-trampoline"
>>>>>> +- reg: should be <0x4C000100 0x100>
>>>>>> +
>>>>>> +EXAMPLE
>>>>>> +       trampoline: trampoline@0x4C000100 {
>>>>> Drop the 0x part of unit addresses.
>>>>
>>>> Okay.
>>>>
>>>>
>>>>>> +               compatible = "socionext,smp-trampoline";
>>>>>> +               reg = <0x4C000100 0x100>;
>>>>> Looks like a software construct, which we wouldn't want to put into DT
>>>>> this way. DT doesn't describe drivers.
>>>> We would like to use this node only getting the address of the
>>>> trampoline area
>>>> in which sub-cores wait.  (They have finished to go to this area in previous
>>>> bootloader process.)
>>>
>>> Is this area part of memory, or a special SRAM? If it's part of memory,
>>> I would expect this node to be under the reserved-memory node and
>>> pointed to by some other node that uses this region. Could even be the
>>> CPU nodes.
>>
>> Yes, 0x4C000100 is a part of memory under the reserved-memory node. So
>> we would like to use the SRAM ( allocated 0x00000000 ) area instead.
>> BTW, sorry, the trampoline address of this example is simply wrong.  We
>> were going to use a part of the SRAM from the beginning.
>>
>>>
>>>>
>>>> So should we embed the constant value in source codes instead of getting
>>>> from
>>>> DT because the address is constant at the moment? Or is there other
>>>> approach?
>>>>
>>>
>>> If it's constant then that also works. Why does it need to come from DT
>>> at all then?
>>
>> We think it is not good to embed constant value in driver codes and do
>> not have another way...
>> Are there better ways?
> 
> If this is just memory, can you use the standard spin-table binding in
> the DT spec? There are some requirements like 64-bit values even on
> 32-bit machines (though this gets violated).

The spin-table seems to be used on only 64-bit arch. Have it ever worked 
on 32-bit machine?
And I would like not to use it because avoid violation.

Thanks
Sugaya Taichi


> 
> Rob
> 

^ permalink raw reply

* Re: [PATCH 11/14] pinctrl: milbeaut: Add Milbeaut M10V pinctrl
From: Masahiro Yamada @ 2018-12-04 11:23 UTC (permalink / raw)
  To: sugaya.taichi
  Cc: linux-clk, DTML, linux-arm-kernel, Linux Kernel Mailing List,
	linux-serial, Michael Turquette, Stephen Boyd, Rob Herring,
	Mark Rutland, Greg Kroah-Hartman, Daniel Lezcano, Thomas Gleixner,
	Russell King, Jiri Slaby, Masami Hiramatsu, Jassi Brar
In-Reply-To: <1542589336-13925-2-git-send-email-sugaya.taichi@socionext.com>

Hi Sugaya-san

On Mon, Nov 19, 2018 at 10:01 AM Sugaya Taichi
<sugaya.taichi@socionext.com> wrote:
>
> Add Milbeaut M10V pinctrl.
> The M10V has the pins that can be used GPIOs or take multiple other
> functions.
>
> Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>


This patch was sent to:

linux-clk@vger.kernel.org,
devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org,
linux-serial@vger.kernel.org




Unfortunately, the most important ML

  linux-gpio@vger.kernel.org

was not addressed.


The pinctrl maintainer may not notice this patch.







> diff --git a/drivers/pinctrl/pinctrl-m10v.c b/drivers/pinctrl/pinctrl-m10v.c
> new file mode 100644
> index 0000000..d4ca713
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-m10v.c
> @@ -0,0 +1,765 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Socionext Ltd.
> + * Copyright (C) 2015 Linaro Ltd.
> + * Author: Jassi Brar <jaswinder.singh@linaro.org>


My company name is "Socionext Inc." instead of "Socionext Ltd."







> +static struct platform_driver m10v_pinctrl_driver = {
> +       .probe  = m10v_pinctrl_probe,
> +       .driver = {
> +               .name           = "m10v-pinctrl",
> +               .of_match_table = m10v_pmatch,
> +       },
> +};
> +
> +static int __init m10v_pinctrl_init(void)
> +{
> +       return platform_driver_register(&m10v_pinctrl_driver);
> +}
> +arch_initcall(m10v_pinctrl_init);


Can't it be builtin_platform_driver()?

Which device requires this to be arch_initcall()?





--
Best Regards
Masahiro Yamada

^ permalink raw reply

* Re: [PATCH 07/14] clock: milbeaut: Add Milbeaut M10V clock control
From: Masahiro Yamada @ 2018-12-04 11:03 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Mark Rutland, DTML, Masami Hiramatsu, sugaya.taichi,
	Greg Kroah-Hartman, Michael Turquette, Daniel Lezcano,
	Linux Kernel Mailing List, Russell King, Jassi Brar, Rob Herring,
	linux-serial, Jiri Slaby, Thomas Gleixner, linux-clk,
	linux-arm-kernel
In-Reply-To: <154356669840.88331.4455990896653868594@swboyd.mtv.corp.google.com>

Hi Stephen,


On Fri, Nov 30, 2018 at 5:31 PM Stephen Boyd <sboyd@kernel.org> wrote:
>
> Quoting Sugaya Taichi (2018-11-18 17:01:12)
> > Add Milbeaut M10V clock ( including PLL ) control.
>
> Please give some more details here.
>
> >
> > Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
> > ---
> >  drivers/clk/Makefile   |   1 +
> >  drivers/clk/clk-m10v.c | 671 +++++++++++++++++++++++++++++++++++++++++++++++++
>
> And this is different from Uniphier? Maybe we need a socionext
> directory under drivers/clk/.



This is always a difficult question,
and I do not have a strong opinion.


I am fine with moving the files to drivers/clk/socionext
although no file would be shared.


FYI

UniPhier and Milbeaut are completely different platforms
developed/maintained by different teams.

They happen to live in the same company now
just because Socionext merged the LSI business from Panasonic and Fujitsu.

UniPhier originates in Panasonic, while Milbeaut in Fujitsu.


Thanks.




--
Best Regards
Masahiro Yamada

^ permalink raw reply

* Re: [PATCH 07/14] clock: milbeaut: Add Milbeaut M10V clock control
From: Sugaya, Taichi @ 2018-12-04  8:26 UTC (permalink / raw)
  To: Stephen Boyd, devicetree, linux-arm-kernel, linux-clk,
	linux-kernel, linux-serial
  Cc: Michael Turquette, Rob Herring, Mark Rutland, Greg Kroah-Hartman,
	Daniel Lezcano, Thomas Gleixner, Russell King, Jiri Slaby,
	Masami Hiramatsu, Jassi Brar
In-Reply-To: <154356669840.88331.4455990896653868594@swboyd.mtv.corp.google.com>

Hi,

Thank you for your comments.

On 2018/11/30 17:31, Stephen Boyd wrote:
> Quoting Sugaya Taichi (2018-11-18 17:01:12)
>> Add Milbeaut M10V clock ( including PLL ) control.
> 
> Please give some more details here.

OK, add more description.

> 
>>
>> Signed-off-by: Sugaya Taichi <sugaya.taichi@socionext.com>
>> ---
>>   drivers/clk/Makefile   |   1 +
>>   drivers/clk/clk-m10v.c | 671 +++++++++++++++++++++++++++++++++++++++++++++++++
> 
> And this is different from Uniphier? Maybe we need a socionext
> directory under drivers/clk/.

Yes, M10V is a one of the Milbeaut series ( not Uniphier ).
Anyway, I will talk to Uniphier team about creating a socionext directory.

> 
>>   2 files changed, 672 insertions(+)
>>   create mode 100644 drivers/clk/clk-m10v.c
>>
>> diff --git a/drivers/clk/clk-m10v.c b/drivers/clk/clk-m10v.c
>> new file mode 100644
>> index 0000000..aa92a69
>> --- /dev/null
>> +++ b/drivers/clk/clk-m10v.c
>> @@ -0,0 +1,671 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2018 Socionext Inc.
>> + * Copyright (C) 2016 Linaro Ltd.
>> + *
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
> 
> Is this include used?

I will check and drop if they are not used.

> 
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of.h>
>> +#include <linux/spinlock.h>
>> +
>> +#define CLKSEL1                0x0
>> +#define CLKSEL(n)      (((n) - 1) * 4 + CLKSEL1)
>> +
>> +#define PLLCNT1                0x30
>> +#define PLLCNT(n)      (((n) - 1) * 4 + PLLCNT1)
>> +
>> +#define CLKSTOP1       0x54
>> +#define CLKSTOP(n)     (((n) - 1) * 4 + CLKSTOP1)
>> +
>> +#define CRSWR          0x8c
>> +#define CRRRS          0x90
>> +#define CRRSM          0x94
>> +
>> +#define to_m10v_mux(_hw)       container_of(_hw, struct m10v_mux, hw)
>> +#define to_m10v_gate(_hw)      container_of(_hw, struct m10v_gate, hw)
>> +#define to_m10v_div(_hw)       container_of(_hw, struct m10v_div, hw)
>> +#define to_m10v_pll(_hw)       container_of(_hw, struct m10v_pll, hw)
>> +
>> +static void __iomem *clk_base;
>> +static struct device_node *np_top;
>> +static DEFINE_SPINLOCK(crglock);
> 
> Please make more specific names for these global variables by prefixing
> with m10v_. Also consider getting rid of the iomem and np_top globals
> entirely and associate those with clks differently.

I got it.

> 
>> +
>> +static __init void __iomem *m10v_clk_iomap(void)
>> +{
>> +       if (clk_base)
>> +               return clk_base;
>> +
>> +       np_top = of_find_compatible_node(NULL, NULL,
>> +                       "socionext,milbeaut-m10v-clk-regs");
>> +       if (!np_top) {
>> +               pr_err("%s: CLK iomap failed!\n", __func__);
> 
> We haven't iomapped yet though.

Yes.

> 
>> +               return NULL;
>> +       }
>> +
>> +       clk_base = of_iomap(np_top, 0);
>> +       of_node_put(np_top);
> 
> Would be nicer to use platform_device APIs instead of OF ones.

OK, use platform_device APIs.

> 
>> +
>> +       return clk_base;
>> +}
>> +
>> +struct m10v_mux {
>> +       struct clk_hw hw;
>> +       const char *cname;
>> +       u32 parent;
>> +};
>> +
>> +static u8 m10v_mux_get_parent(struct clk_hw *hw)
>> +{
>> +       struct m10v_mux *mcm = to_m10v_mux(hw);
>> +       struct clk_hw *parent;
>> +       int i;
>> +
>> +       i = clk_hw_get_num_parents(hw);
>> +       while (i--) {
>> +               parent = clk_hw_get_parent_by_index(hw, i);
>> +               if (clk_hw_get_rate(parent))
>> +                       break;
>> +       }
>> +
>> +       if (i < 0) {
>> +               pr_info("%s:%s no parent?!\n",
>> +                       __func__, mcm->cname);
>> +               i = 0;
>> +       }
>> +
>> +       return i;
>> +}
>> +
>> +static int m10v_mux_set_parent(struct clk_hw *hw, u8 index)
>> +{
>> +       struct m10v_mux *mcm = to_m10v_mux(hw);
>> +
>> +       mcm->parent = index;
>> +       return 0;
>> +}
>> +
>> +static const struct clk_ops m10v_mux_ops = {
>> +       .get_parent = m10v_mux_get_parent,
>> +       .set_parent = m10v_mux_set_parent,
>> +       .determine_rate = __clk_mux_determine_rate,
>> +};
>> +
>> +void __init m10v_clk_mux_setup(struct device_node *node)
>> +{
>> +       const char *clk_name = node->name;
>> +       struct clk_init_data init;
>> +       const char **parent_names;
>> +       struct m10v_mux *mcm;
>> +       struct clk *clk;
>> +       int i, parents;
>> +
>> +       if (!m10v_clk_iomap())
>> +               return;
>> +
>> +       of_property_read_string(node, "clock-output-names", &clk_name);
>> +
>> +       parents = of_clk_get_parent_count(node);
>> +       if (parents < 2) {
>> +               pr_err("%s: not a mux\n", clk_name);
> 
> How is this possible?

When the node has more than 1 clks...
Or I am misunderstanding your question?

> 
>> +               return;
>> +       }
>> +
>> +       parent_names = kzalloc((sizeof(char *) * parents), GFP_KERNEL);
>> +       if (!parent_names)
>> +               return;
>> +
>> +       for (i = 0; i < parents; i++)
>> +               parent_names[i] = of_clk_get_parent_name(node, i);
> 
> This is of_clk_parent_fill().

OK, use it instead.

> 
>> +
>> +       mcm = kzalloc(sizeof(*mcm), GFP_KERNEL);
>> +       if (!mcm)
>> +               goto err_mcm;
>> +
>> +       init.name = clk_name;
>> +       init.ops = &m10v_mux_ops;
>> +       init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
> 
> Please don't use CLK_IS_BASIC unless you need it.

OK, confirm it.

> 
>> +       init.num_parents = parents;
>> +       init.parent_names = parent_names;
>> +
>> +       mcm->cname = clk_name;
>> +       mcm->parent = 0;
>> +       mcm->hw.init = &init;
>> +
>> +       clk = clk_register(NULL, &mcm->hw);
>> +       if (IS_ERR(clk))
>> +               goto err_clk;
>> +
>> +       of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +       return;
>> +
>> +err_clk:
>> +       kfree(mcm);
>> +err_mcm:
>> +       kfree(parent_names);
>> +}
>> +CLK_OF_DECLARE(m10v_clk_mux, "socionext,milbeaut-m10v-clk-mux",
>> +                       m10v_clk_mux_setup);
> 
> Any chance you can use a platform driver?

OK. try to use platform driver.

> 
>> +
>> +struct m10v_pll {
>> +       struct clk_hw hw;
>> +       const char *cname;
>> +       const struct clk_ops ops;
>> +       u32 offset;
>> +       u32 div, mult;
>> +       bool ro;
>> +};
>> +
>> +#define ST     1
>> +#define SEL    2
>> +
>> +static void _mpg_enable(struct clk_hw *hw, unsigned int enable)
>> +{
>> +       struct m10v_pll *mpg = to_m10v_pll(hw);
>> +       unsigned long flags;
>> +       u32 val;
>> +
>> +       if (mpg->ro) {
>> +               pr_debug("%s:%d %s: read-only\n",
>> +                        __func__, __LINE__, mpg->cname);
>> +               return;
>> +       }
>> +
>> +       spin_lock_irqsave(&crglock, flags);
>> +
>> +       val = readl(clk_base + PLLCNT(SEL));
>> +       if (enable)
>> +               val |= BIT(mpg->offset);
>> +       else
>> +               val &= ~BIT(mpg->offset);
>> +       writel(val, clk_base + PLLCNT(SEL));
>> +
>> +       spin_unlock_irqrestore(&crglock, flags);
>> +}
>> +
>> +static int mpg_enable(struct clk_hw *hw)
>> +{
>> +       _mpg_enable(hw, 1);
>> +       return 0;
>> +}
>> +
>> +static void mpg_disable(struct clk_hw *hw)
>> +{
>> +       _mpg_enable(hw, 0);
>> +}
>> +
>> +static int mpg_is_enabled(struct clk_hw *hw)
>> +{
>> +       struct m10v_pll *mpg = to_m10v_pll(hw);
>> +
>> +       return readl(clk_base + PLLCNT(SEL)) & (1 << mpg->offset);
>> +}
>> +
>> +static void _mpg_prepare(struct clk_hw *hw, unsigned int on)
>> +{
>> +       struct m10v_pll *mpg = to_m10v_pll(hw);
>> +       unsigned long flags;
>> +       u32 val;
>> +
>> +       if (mpg->ro) {
> 
> Should have different RO ops for read-only clks.

I got it.

> 
>> +               pr_debug("%s:%d %s: read-only\n",
>> +                        __func__, __LINE__, mpg->cname);
>> +               return;
>> +       }
>> +
>> +       val = readl(clk_base + PLLCNT(ST));
>> +       if (!on == !(val & BIT(mpg->offset)))
>> +               return;
>> +
>> +       /* disable */
> 
> Please remove obvious comments.

Oops, OK remove.

> 
>> +       mpg_disable(hw);
>> +
>> +       spin_lock_irqsave(&crglock, flags);
>> +
>> +       val = readl(clk_base + PLLCNT(ST));
>> +       if (on)
>> +               val |= BIT(mpg->offset);
>> +       else
>> +               val &= ~BIT(mpg->offset);
>> +       writel(val, clk_base + PLLCNT(ST));
>> +
>> +       spin_unlock_irqrestore(&crglock, flags);
>> +
>> +       udelay(on ? 200 : 10);
>> +}
>> +
>> +static int mpg_prepare(struct clk_hw *hw)
>> +{
>> +       _mpg_prepare(hw, 1);
>> +       return 0;
>> +}
>> +
>> +static void mpg_unprepare(struct clk_hw *hw)
>> +{
>> +       _mpg_prepare(hw, 0);
>> +}
>> +
>> +static int mpg_set_rate(struct clk_hw *hw, unsigned long rate,
>> +                               unsigned long prate)
>> +{
> 
> Why is this implemented then?

This is not necessary maybe. so consider whether getting rid of it.

> 
>> +       return 0;
>> +}
>> +
>> +static unsigned long mpg_recalc_rate(struct clk_hw *hw,
>> +               unsigned long prate)
>> +{
>> +       struct m10v_pll *mpg = to_m10v_pll(hw);
>> +       unsigned long long rate = prate;
>> +
>> +       if (mpg_is_enabled(hw)) {
>> +               rate = (unsigned long long)prate * mpg->mult;
>> +               do_div(rate, mpg->div);
>> +       }
>> +
>> +       return (unsigned long)rate;
>> +}
>> +
>> +static long mpg_round_rate(struct clk_hw *hw, unsigned long rate,
>> +                               unsigned long *prate)
>> +{
>> +       struct m10v_pll *mpg = to_m10v_pll(hw);
>> +       unsigned long long temp_rate = (unsigned long long)*prate * mpg->mult;
>> +
>> +       if (mpg->ro)
>> +               return mpg_recalc_rate(hw, *prate);
>> +
>> +       return do_div(temp_rate, mpg->div);
> 
> There shouldn't be round_rate implemented at all if the device is
> 'read-only' or can't change frequency because set_rate op is empty.

I understand. I will describe correctly.

> 
>> +}
>> +
> [..]
>> +
>> +static void mdc_set_div(struct m10v_div *mdc, u32 div)
>> +{
>> +       u32 off, shift, val;
>> +
>> +       off = mdc->offset / 32 * 4;
>> +       shift = mdc->offset % 32;
>> +
>> +       val = readl(clk_base + CLKSEL1 + off);
>> +       val &= ~(mdc->mask << shift);
>> +       val |= (div << shift);
>> +       writel(val, clk_base + CLKSEL1 + off);
>> +
>> +       if (mdc->waitdchreq) {
>> +               unsigned int count = 250;
>> +
>> +               writel(1, clk_base + CLKSEL(11));
>> +
>> +               do {
>> +                       udelay(1);
>> +               } while (--count && readl(clk_base + CLKSEL(11)) & 1);
> 
> Use readl_poll_timeout()?

OK. use it instead.

> 
>> +
>> +               if (!count)
>> +                       pr_err("%s:%s CLK(%d) couldn't stabilize\n",
>> +                               __func__, mdc->cname, mdc->offset);
>> +       }
>> +}
>> +
> [...]
>> +
>> +void __init m10v_clk_gate_setup(struct device_node *node)
>> +{
>> +       const char *clk_name = node->name;
>> +       struct clk_init_data init;
>> +       const char *parent_name;
>> +       struct m10v_gate *mgc;
>> +       struct clk *clk;
>> +       u32 offset;
>> +       int ret;
>> +
>> +       if (!m10v_clk_iomap())
>> +               return;
>> +
>> +       of_property_read_string(node, "clock-output-names", &clk_name);
>> +
>> +       ret = of_property_read_u32(node, "offset", &offset);
>> +       if (ret) {
>> +               pr_err("%s: missing 'offset' property\n", clk_name);
>> +               return;
>> +       }
>> +
>> +       parent_name = of_clk_get_parent_name(node, 0);
>> +
>> +       mgc = kzalloc(sizeof(*mgc), GFP_KERNEL);
>> +       if (!mgc)
>> +               return;
>> +
>> +       init.name = clk_name;
>> +       init.ops = &m10v_gate_ops;
>> +       init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
>> +       init.parent_names = &parent_name;
>> +       init.num_parents = 1;
>> +
>> +       mgc->cname = clk_name;
>> +       mgc->offset = offset;
>> +       mgc->hw.init = &init;
>> +       if (of_get_property(node, "read-only", NULL))
>> +               mgc->ro = true;
>> +
>> +       clk = clk_register(NULL, &mgc->hw);
> 
> Please use clk_hw based registration and provider APIs.

I got it. I try to describe with referring other drivers.

> 
>> +       if (IS_ERR(clk))
>> +               kfree(mgc);
>> +       else
>> +               of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +}
>> +CLK_OF_DECLARE(m10v_clk_gate, "socionext,milbeaut-m10v-clk-gate",
>> +               m10v_clk_gate_setup);
>> -- 
> 
> I suspect this driver will significantly change so I'm not reviewing
> any further until it's sent again.

I understand. I study and try to renew the driver.


> 

^ permalink raw reply

* Re: [PATCH v2 06/15] arm: dts: Add devicetree for OrangePi 2G IoT board
From: Olof Johansson @ 2018-12-03 17:53 UTC (permalink / raw)
  To: Rob Herring
  Cc: Manivannan Sadhasivam, Arnd Bergmann, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Daniel Lezcano, Greg Kroah-Hartman,
	Jiri Slaby, Andreas Färber, Linux ARM Mailing List,
	Linux Kernel Mailing List, DTML, linux-serial, Amit Kucheria,
	LinusW, zhao_steven, overseas.sales
In-Reply-To: <CAL_JsqLZY_5rZ35YSGeH7wmEZoLP1TjG+0n4BWGiUBwOFSq-Vw@mail.gmail.com>

On Mon, Dec 3, 2018 at 9:21 AM Rob Herring <robh+dt@kernel.org> wrote:
>
> On Mon, Dec 3, 2018 at 11:11 AM Olof Johansson <olof@lixom.net> wrote:
> >
> > On Mon, Dec 3, 2018 at 7:54 AM Rob Herring <robh+dt@kernel.org> wrote:
> > >
> > > On Tue, Nov 20, 2018 at 9:38 PM Manivannan Sadhasivam
> > > <manivannan.sadhasivam@linaro.org> wrote:
> > > >
> > > > Add initial devicetree support for OrangePi 2G IoT board from Xunlong.
> > > >
> > > > Signed-off-by: Andreas Färber <afaerber@suse.de>
> > > > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > > > ---
> > > >  arch/arm/boot/dts/Makefile                    |  2 +
> > > >  .../boot/dts/rda8810pl-orangepi-2g-iot.dts    | 40 +++++++++++++++++++
> > > >  2 files changed, 42 insertions(+)
> > > >  create mode 100644 arch/arm/boot/dts/rda8810pl-orangepi-2g-iot.dts
> > > >
> > > > diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> > > > index b0e966d625b9..a0fdad8f10dd 100644
> > > > --- a/arch/arm/boot/dts/Makefile
> > > > +++ b/arch/arm/boot/dts/Makefile
> > > > @@ -806,6 +806,8 @@ dtb-$(CONFIG_ARCH_QCOM) += \
> > > >         qcom-msm8974-sony-xperia-castor.dtb \
> > > >         qcom-msm8974-sony-xperia-honami.dtb \
> > > >         qcom-mdm9615-wp8548-mangoh-green.dtb
> > > > +dtb-$(CONFIG_ARCH_RDA) += \
> > > > +       rda8810pl-orangepi-2g-iot.dtb
> > > >  dtb-$(CONFIG_ARCH_REALVIEW) += \
> > >
> > > Question for Arnd/Olof: can we *please* start putting new SoC families
> > > in sub-dirs?
> >
> > I think we're at a point where it's becoming quite awkward to keep it
> > in a flat directory, yes.
> >
> > Best way to handle this is usually right before the closing of a merge
> > window, so let's do it then. It'll be a conflict-ridden mess if we try
> > to stage something in -next, so I'd rather just do it directly in our
> > tree as a one-shot thing.
>
> While I'd like to see the whole thing converted, I was only asking
> about this one platform. That should be doable now, right?

I'd rather apply this to current scheme and then move everything once,
instead of having maintainers go "Oh, I guess I need to move mine" and
we end up with an onslaught of conflicting pull requests to move
things apart.


-Olof

^ permalink raw reply


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