linux-serial.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs
@ 2020-03-19 11:03 Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 1/6] serial: 8250: 8250_omap: Terminate DMA before pushing data on RX timeout Vignesh Raghavendra
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Vignesh Raghavendra @ 2020-03-19 11:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Vignesh Raghavendra, linux-serial, linux-omap, linux-kernel

This series add DMA support for UARTs on AM654 and J721e SoCs. These
UARTs are modified version of older generation UARTs on OMAP SoCs.

With new version of IP, it is possible to support longer DMA bursts per
request. There are also differences wrt DMA integration.

First 4 patches update driver to work with AM654 DMA differences. Patch
5 implements errata workaround and Patch 6 adds the actual support for
DMA.


Peter Ujfalusi (1):
  serial: 8250: 8250_omap: Move locking out from __dma_rx_do_complete()

Vignesh Raghavendra (5):
  serial: 8250: 8250_omap: Terminate DMA before pushing data on RX
    timeout
  serial: 8250: 8250_omap: Account for data in flight during DMA
    teardown
  serial: 8250: 8250_omap: Extend driver data to pass FIFO trigger info
  serial: 8250: 8250_omap: Work around errata causing spurious IRQs with
    DMA
  serial: 8250: 8250_omap: Add DMA support for UARTs on K3 SoCs

 drivers/tty/serial/8250/8250_omap.c | 222 ++++++++++++++++++++++------
 1 file changed, 176 insertions(+), 46 deletions(-)

-- 
2.25.2


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/6] serial: 8250: 8250_omap: Terminate DMA before pushing data on RX timeout
  2020-03-19 11:03 [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs Vignesh Raghavendra
@ 2020-03-19 11:03 ` Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 2/6] serial: 8250: 8250_omap: Account for data in flight during DMA teardown Vignesh Raghavendra
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Vignesh Raghavendra @ 2020-03-19 11:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Vignesh Raghavendra, linux-serial, linux-omap, linux-kernel

Terminate and flush DMA internal buffers, before pushing RX data to
higher layer. Otherwise, this will lead to data corruption, as driver
would end up pushing stale buffer data to higher layer while actual data
is still stuck inside DMA hardware and has yet not arrived at the
memory.
While at that, replace deprecated dmaengine_terminate_all() with
dmaengine_terminate_async().

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/tty/serial/8250/8250_omap.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index f04139923da0..65b6d165df67 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -755,7 +755,10 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
 	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
 
 	count = dma->rx_size - state.residue;
-
+	if (count < dma->rx_size)
+		dmaengine_terminate_async(dma->rxchan);
+	if (!count)
+		goto unlock;
 	ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
 
 	p->port.icount.rx += ret;
@@ -817,7 +820,6 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
 	spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
 
 	__dma_rx_do_complete(p);
-	dmaengine_terminate_all(dma->rxchan);
 }
 
 static int omap_8250_rx_dma(struct uart_8250_port *p)
-- 
2.25.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/6] serial: 8250: 8250_omap: Account for data in flight during DMA teardown
  2020-03-19 11:03 [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 1/6] serial: 8250: 8250_omap: Terminate DMA before pushing data on RX timeout Vignesh Raghavendra
@ 2020-03-19 11:03 ` Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 3/6] serial: 8250: 8250_omap: Move locking out from __dma_rx_do_complete() Vignesh Raghavendra
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Vignesh Raghavendra @ 2020-03-19 11:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Vignesh Raghavendra, linux-serial, linux-omap, linux-kernel

Take into account data stuck in DMA internal buffers before pushing data
to higher layer. dma_tx_state has "in_flight_bytes" member that provides
this information.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/tty/serial/8250/8250_omap.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 65b6d165df67..be2b7d374e60 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -741,6 +741,8 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
 	struct omap8250_priv	*priv = p->port.private_data;
 	struct uart_8250_dma    *dma = p->dma;
 	struct tty_port         *tty_port = &p->port.state->port;
+	struct dma_chan		*rxchan = dma->rxchan;
+	dma_cookie_t		cookie;
 	struct dma_tx_state     state;
 	int                     count;
 	unsigned long		flags;
@@ -751,12 +753,29 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
 	if (!dma->rx_running)
 		goto unlock;
 
+	cookie = dma->rx_cookie;
 	dma->rx_running = 0;
-	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+	dmaengine_tx_status(rxchan, cookie, &state);
 
-	count = dma->rx_size - state.residue;
-	if (count < dma->rx_size)
-		dmaengine_terminate_async(dma->rxchan);
+	count = dma->rx_size - state.residue + state.in_flight_bytes;
+	if (count < dma->rx_size) {
+		dmaengine_terminate_async(rxchan);
+
+		/*
+		 * Poll for teardown to complete which guarantees in
+		 * flight data is drained.
+		 */
+		if (state.in_flight_bytes) {
+			int poll_count = 25;
+
+			while (dmaengine_tx_status(rxchan, cookie, NULL) &&
+			       poll_count--)
+				cpu_relax();
+
+			if (!poll_count)
+				dev_err(p->port.dev, "teardown incomplete\n");
+		}
+	}
 	if (!count)
 		goto unlock;
 	ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
-- 
2.25.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/6] serial: 8250: 8250_omap: Move locking out from __dma_rx_do_complete()
  2020-03-19 11:03 [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 1/6] serial: 8250: 8250_omap: Terminate DMA before pushing data on RX timeout Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 2/6] serial: 8250: 8250_omap: Account for data in flight during DMA teardown Vignesh Raghavendra
@ 2020-03-19 11:03 ` Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 4/6] serial: 8250: 8250_omap: Extend driver data to pass FIFO trigger info Vignesh Raghavendra
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Vignesh Raghavendra @ 2020-03-19 11:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Vignesh Raghavendra, linux-serial, linux-omap, linux-kernel

From: Peter Ujfalusi <peter.ujfalusi@ti.com>

Caller functions of __dma_rx_do_complete() already hold rx_dma_lock.
Therefore move locking out of the function to avoid need to release and
reacquire lock.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/tty/serial/8250/8250_omap.c | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index be2b7d374e60..aef0535b62a4 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -736,22 +736,19 @@ static void omap_8250_unthrottle(struct uart_port *port)
 #ifdef CONFIG_SERIAL_8250_DMA
 static int omap_8250_rx_dma(struct uart_8250_port *p);
 
+/* Must be called while priv->rx_dma_lock is held */
 static void __dma_rx_do_complete(struct uart_8250_port *p)
 {
-	struct omap8250_priv	*priv = p->port.private_data;
 	struct uart_8250_dma    *dma = p->dma;
 	struct tty_port         *tty_port = &p->port.state->port;
 	struct dma_chan		*rxchan = dma->rxchan;
 	dma_cookie_t		cookie;
 	struct dma_tx_state     state;
 	int                     count;
-	unsigned long		flags;
 	int			ret;
 
-	spin_lock_irqsave(&priv->rx_dma_lock, flags);
-
 	if (!dma->rx_running)
-		goto unlock;
+		goto out;
 
 	cookie = dma->rx_cookie;
 	dma->rx_running = 0;
@@ -777,13 +774,12 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
 		}
 	}
 	if (!count)
-		goto unlock;
+		goto out;
 	ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
 
 	p->port.icount.rx += ret;
 	p->port.icount.buf_overrun += count - ret;
-unlock:
-	spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
+out:
 
 	tty_flip_buffer_push(tty_port);
 }
@@ -836,9 +832,8 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
 		if (WARN_ON_ONCE(ret))
 			priv->rx_dma_broken = true;
 	}
-	spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
-
 	__dma_rx_do_complete(p);
+	spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
 }
 
 static int omap_8250_rx_dma(struct uart_8250_port *p)
-- 
2.25.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/6] serial: 8250: 8250_omap: Extend driver data to pass FIFO trigger info
  2020-03-19 11:03 [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs Vignesh Raghavendra
                   ` (2 preceding siblings ...)
  2020-03-19 11:03 ` [PATCH 3/6] serial: 8250: 8250_omap: Move locking out from __dma_rx_do_complete() Vignesh Raghavendra
@ 2020-03-19 11:03 ` Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 5/6] serial: 8250: 8250_omap: Work around errata causing spurious IRQs with DMA Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 6/6] serial: 8250: 8250_omap: Add DMA support for UARTs on K3 SoCs Vignesh Raghavendra
  5 siblings, 0 replies; 7+ messages in thread
From: Vignesh Raghavendra @ 2020-03-19 11:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Vignesh Raghavendra, linux-serial, linux-omap, linux-kernel

Although same 8250 compliant UART IP is reused across different SoC,
their integration wrt DMA varies greatly across SoCs. Therefore,
different SoC may need to use different FIFO trigger level for DMA
event and DMA configuration parameters. Provide a way to pass this
information via driver data. This is required to support UART DMA on
AM654/J721e SoCs.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/tty/serial/8250/8250_omap.c | 82 +++++++++++++++++++++--------
 1 file changed, 61 insertions(+), 21 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index aef0535b62a4..207759dfbff2 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -105,6 +105,8 @@ struct omap8250_priv {
 	u8 delayed_restore;
 	u16 quot;
 
+	u8 tx_trigger;
+	u8 rx_trigger;
 	bool is_suspending;
 	int wakeirq;
 	int wakeups_enabled;
@@ -118,6 +120,17 @@ struct omap8250_priv {
 	bool throttled;
 };
 
+struct omap8250_dma_params {
+	u32 rx_size;
+	u8 rx_trigger;
+	u8 tx_trigger;
+};
+
+struct omap8250_platdata {
+	struct omap8250_dma_params *dma_params;
+	u8 habit;
+};
+
 #ifdef CONFIG_SERIAL_8250_DMA
 static void omap_8250_rx_dma_flush(struct uart_8250_port *p);
 #else
@@ -295,8 +308,8 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
 	serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_RESTORE(16) |
 			OMAP_UART_TCR_HALT(52));
 	serial_out(up, UART_TI752_TLR,
-		   TRIGGER_TLR_MASK(TX_TRIGGER) << UART_TI752_TLR_TX |
-		   TRIGGER_TLR_MASK(RX_TRIGGER) << UART_TI752_TLR_RX);
+		   TRIGGER_TLR_MASK(priv->tx_trigger) << UART_TI752_TLR_TX |
+		   TRIGGER_TLR_MASK(priv->rx_trigger) << UART_TI752_TLR_RX);
 
 	serial_out(up, UART_LCR, 0);
 
@@ -435,8 +448,8 @@ static void omap_8250_set_termios(struct uart_port *port,
 	 * This is because threshold and trigger values are the same.
 	 */
 	up->fcr = UART_FCR_ENABLE_FIFO;
-	up->fcr |= TRIGGER_FCR_MASK(TX_TRIGGER) << OMAP_UART_FCR_TX_TRIG;
-	up->fcr |= TRIGGER_FCR_MASK(RX_TRIGGER) << OMAP_UART_FCR_RX_TRIG;
+	up->fcr |= TRIGGER_FCR_MASK(priv->tx_trigger) << OMAP_UART_FCR_TX_TRIG;
+	up->fcr |= TRIGGER_FCR_MASK(priv->rx_trigger) << OMAP_UART_FCR_RX_TRIG;
 
 	priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY |
 		OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
@@ -1092,18 +1105,30 @@ static int omap8250_no_handle_irq(struct uart_port *port)
 	return 0;
 }
 
-static const u8 omap4_habit = UART_ERRATA_CLOCK_DISABLE;
-static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
-static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE;
+static struct omap8250_dma_params am33xx_dma = {
+	.rx_size = RX_TRIGGER,
+	.rx_trigger = RX_TRIGGER,
+	.tx_trigger = TX_TRIGGER,
+};
+
+static struct omap8250_platdata am33xx_platdata = {
+	.dma_params	= &am33xx_dma,
+	.habit		= OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE,
+};
+
+static struct omap8250_platdata omap4_platdata = {
+	.dma_params	= &am33xx_dma,
+	.habit		= UART_ERRATA_CLOCK_DISABLE,
+};
 
 static const struct of_device_id omap8250_dt_ids[] = {
 	{ .compatible = "ti,am654-uart" },
 	{ .compatible = "ti,omap2-uart" },
 	{ .compatible = "ti,omap3-uart" },
-	{ .compatible = "ti,omap4-uart", .data = &omap4_habit, },
-	{ .compatible = "ti,am3352-uart", .data = &am3352_habit, },
-	{ .compatible = "ti,am4372-uart", .data = &am3352_habit, },
-	{ .compatible = "ti,dra742-uart", .data = &dra742_habit, },
+	{ .compatible = "ti,omap4-uart", .data = &omap4_platdata, },
+	{ .compatible = "ti,am3352-uart", .data = &am33xx_platdata, },
+	{ .compatible = "ti,am4372-uart", .data = &am33xx_platdata, },
+	{ .compatible = "ti,dra742-uart", .data = &omap4_platdata, },
 	{},
 };
 MODULE_DEVICE_TABLE(of, omap8250_dt_ids);
@@ -1114,10 +1139,10 @@ static int omap8250_probe(struct platform_device *pdev)
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	struct device_node *np = pdev->dev.of_node;
 	struct omap8250_priv *priv;
+	const struct omap8250_platdata *pdata;
 	struct uart_8250_port up;
 	int ret;
 	void __iomem *membase;
-	const struct of_device_id *id;
 
 	if (!regs || !irq) {
 		dev_err(&pdev->dev, "missing registers or irq\n");
@@ -1198,9 +1223,9 @@ static int omap8250_probe(struct platform_device *pdev)
 
 	priv->wakeirq = irq_of_parse_and_map(np, 1);
 
-	id = of_match_device(of_match_ptr(omap8250_dt_ids), &pdev->dev);
-	if (id && id->data)
-		priv->habit |= *(u8 *)id->data;
+	pdata = of_device_get_match_data(&pdev->dev);
+	if (pdata)
+		priv->habit |= pdata->habit;
 
 	if (!up.port.uartclk) {
 		up.port.uartclk = DEFAULT_CLK_SPEED;
@@ -1236,6 +1261,8 @@ static int omap8250_probe(struct platform_device *pdev)
 
 	omap_serial_fill_features_erratas(&up, priv);
 	up.port.handle_irq = omap8250_no_handle_irq;
+	priv->rx_trigger = RX_TRIGGER;
+	priv->tx_trigger = TX_TRIGGER;
 #ifdef CONFIG_SERIAL_8250_DMA
 	/*
 	 * Oh DMA support. If there are no DMA properties in the DT then
@@ -1247,13 +1274,26 @@ static int omap8250_probe(struct platform_device *pdev)
 	 */
 	ret = of_property_count_strings(np, "dma-names");
 	if (ret == 2) {
+		struct omap8250_dma_params *dma_params = NULL;
+
 		up.dma = &priv->omap8250_dma;
-		priv->omap8250_dma.fn = the_no_dma_filter_fn;
-		priv->omap8250_dma.tx_dma = omap_8250_tx_dma;
-		priv->omap8250_dma.rx_dma = omap_8250_rx_dma;
-		priv->omap8250_dma.rx_size = RX_TRIGGER;
-		priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER;
-		priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER;
+		up.dma->fn = the_no_dma_filter_fn;
+		up.dma->tx_dma = omap_8250_tx_dma;
+		up.dma->rx_dma = omap_8250_rx_dma;
+		if (pdata)
+			dma_params = pdata->dma_params;
+
+		if (dma_params) {
+			up.dma->rx_size = dma_params->rx_size;
+			up.dma->rxconf.src_maxburst = dma_params->rx_trigger;
+			up.dma->txconf.dst_maxburst = dma_params->tx_trigger;
+			priv->rx_trigger = dma_params->rx_trigger;
+			priv->tx_trigger = dma_params->tx_trigger;
+		} else {
+			up.dma->rx_size = RX_TRIGGER;
+			up.dma->rxconf.src_maxburst = RX_TRIGGER;
+			up.dma->txconf.dst_maxburst = TX_TRIGGER;
+		}
 	}
 #endif
 	ret = serial8250_register_8250_port(&up);
-- 
2.25.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/6] serial: 8250: 8250_omap: Work around errata causing spurious IRQs with DMA
  2020-03-19 11:03 [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs Vignesh Raghavendra
                   ` (3 preceding siblings ...)
  2020-03-19 11:03 ` [PATCH 4/6] serial: 8250: 8250_omap: Extend driver data to pass FIFO trigger info Vignesh Raghavendra
@ 2020-03-19 11:03 ` Vignesh Raghavendra
  2020-03-19 11:03 ` [PATCH 6/6] serial: 8250: 8250_omap: Add DMA support for UARTs on K3 SoCs Vignesh Raghavendra
  5 siblings, 0 replies; 7+ messages in thread
From: Vignesh Raghavendra @ 2020-03-19 11:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Vignesh Raghavendra, linux-serial, linux-omap, linux-kernel

As per Advisory 27 of AM437x Silicon errata document, Spurious UART
interrupts may occur when DMA mode (FCR.DMA_MODE) is enabled. The
Interrupt Controller flags that a UART interrupt has occurred; however,
the associated IT_PENDING bit remains set to 1, indicating that no
interrupt is pending. Acknowledge the spurious interrupts for every
occurrence as workaround.

Errata is applicable to all TI SoCs with this IP.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/tty/serial/8250/8250_omap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 207759dfbff2..92508aaa961c 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1051,7 +1051,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
 	iir = serial_port_in(port, UART_IIR);
 	if (iir & UART_IIR_NO_INT) {
 		serial8250_rpm_put(up);
-		return 0;
+		return IRQ_HANDLED;
 	}
 
 	spin_lock_irqsave(&port->lock, flags);
-- 
2.25.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 6/6] serial: 8250: 8250_omap: Add DMA support for UARTs on K3 SoCs
  2020-03-19 11:03 [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs Vignesh Raghavendra
                   ` (4 preceding siblings ...)
  2020-03-19 11:03 ` [PATCH 5/6] serial: 8250: 8250_omap: Work around errata causing spurious IRQs with DMA Vignesh Raghavendra
@ 2020-03-19 11:03 ` Vignesh Raghavendra
  5 siblings, 0 replies; 7+ messages in thread
From: Vignesh Raghavendra @ 2020-03-19 11:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Vignesh Raghavendra, linux-serial, linux-omap, linux-kernel

UART on K3 SoCs has configurable RX timeout behavior (controlled via
EFR2) and better DMA integration. This allows to transfer as larger
amount data per DMA transfer compared to older SoCs.  Add support for
the same.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/tty/serial/8250/8250_omap.c | 98 +++++++++++++++++++++++++----
 1 file changed, 86 insertions(+), 12 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 92508aaa961c..2b63db08d87e 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -40,6 +40,7 @@
  * The same errata is applicable to AM335x and DRA7x processors too.
  */
 #define UART_ERRATA_CLOCK_DISABLE	(1 << 3)
+#define	UART_HAS_EFR2			BIT(4)
 
 #define OMAP_UART_FCR_RX_TRIG		6
 #define OMAP_UART_FCR_TX_TRIG		4
@@ -93,6 +94,10 @@
 #define OMAP_UART_REV_52 0x0502
 #define OMAP_UART_REV_63 0x0603
 
+/* Enhanced features register 2 */
+#define UART_OMAP_EFR2			0x23
+#define UART_OMAP_EFR2_TIMEOUT_BEHAVE	BIT(6)
+
 struct omap8250_priv {
 	int line;
 	u8 habit;
@@ -664,7 +669,7 @@ static int omap_8250_startup(struct uart_port *port)
 		priv->wer |= OMAP_UART_TX_WAKEUP_EN;
 	serial_out(up, UART_OMAP_WER, priv->wer);
 
-	if (up->dma)
+	if (up->dma && !(priv->habit & UART_HAS_EFR2))
 		up->dma->rx_dma(up);
 
 	pm_runtime_mark_last_busy(port->dev);
@@ -689,6 +694,8 @@ static void omap_8250_shutdown(struct uart_port *port)
 	pm_runtime_get_sync(port->dev);
 
 	serial_out(up, UART_OMAP_WER, 0);
+	if (priv->habit & UART_HAS_EFR2)
+		serial_out(up, UART_OMAP_EFR2, 0x0);
 
 	up->ier = 0;
 	serial_out(up, UART_IER, 0);
@@ -818,8 +825,12 @@ static void __dma_rx_complete(void *param)
 		return;
 	}
 	__dma_rx_do_complete(p);
-	if (!priv->throttled)
-		omap_8250_rx_dma(p);
+	if (!priv->throttled) {
+		p->ier |= UART_IER_RLSI | UART_IER_RDI;
+		serial_out(p, UART_IER, p->ier);
+		if (!(priv->habit & UART_HAS_EFR2))
+			omap_8250_rx_dma(p);
+	}
 
 	spin_unlock_irqrestore(&p->port.lock, flags);
 }
@@ -862,8 +873,20 @@ static int omap_8250_rx_dma(struct uart_8250_port *p)
 
 	spin_lock_irqsave(&priv->rx_dma_lock, flags);
 
-	if (dma->rx_running)
+	if (dma->rx_running) {
+		enum dma_status state;
+
+		state = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, NULL);
+		if (state == DMA_COMPLETE) {
+			/*
+			 * Disable RX interrupts to allow RX DMA completion
+			 * callback to run.
+			 */
+			p->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
+			serial_out(p, UART_IER, p->ier);
+		}
 		goto out;
+	}
 
 	desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
 					   dma->rx_size, DMA_DEV_TO_MEM,
@@ -1034,6 +1057,46 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
 	return omap_8250_rx_dma(up);
 }
 
+static unsigned char omap_8250_handle_rx_dma(struct uart_8250_port *up,
+					     u8 iir, unsigned char status)
+{
+	if ((status & (UART_LSR_DR | UART_LSR_BI)) &&
+	    (iir & UART_IIR_RDI)) {
+		if (handle_rx_dma(up, iir)) {
+			status = serial8250_rx_chars(up, status);
+			omap_8250_rx_dma(up);
+		}
+	}
+
+	return status;
+}
+
+static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir,
+				     unsigned char status)
+{
+	/*
+	 * Queue a new transfer if FIFO has data.
+	 */
+	if ((status & (UART_LSR_DR | UART_LSR_BI)) &&
+	    (up->ier & UART_IER_RDI)) {
+		omap_8250_rx_dma(up);
+		serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE);
+	} else if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
+		/*
+		 * Disable RX timeout, read IIR to clear
+		 * current timeout condition, clear EFR2 to
+		 * periodic timeouts, re-enable interrupts.
+		 */
+		up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
+		serial_out(up, UART_IER, up->ier);
+		omap_8250_rx_dma_flush(up);
+		serial_in(up, UART_IIR);
+		serial_out(up, UART_OMAP_EFR2, 0x0);
+		up->ier |= UART_IER_RLSI | UART_IER_RDI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
 /*
  * This is mostly serial8250_handle_irq(). We have a slightly different DMA
  * hoook for RX/TX and need different logic for them in the ISR. Therefore we
@@ -1042,6 +1105,7 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
 static int omap_8250_dma_handle_irq(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
+	struct omap8250_priv *priv = up->port.private_data;
 	unsigned char status;
 	unsigned long flags;
 	u8 iir;
@@ -1058,12 +1122,11 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
 
 	status = serial_port_in(port, UART_LSR);
 
-	if (status & (UART_LSR_DR | UART_LSR_BI)) {
-		if (handle_rx_dma(up, iir)) {
-			status = serial8250_rx_chars(up, status);
-			omap_8250_rx_dma(up);
-		}
-	}
+	if (priv->habit & UART_HAS_EFR2)
+		am654_8250_handle_rx_dma(up, iir, status);
+	else
+		status = omap_8250_handle_rx_dma(up, iir, status);
+
 	serial8250_modem_status(up);
 	if (status & UART_LSR_THRE && up->dma->tx_err) {
 		if (uart_tx_stopped(&up->port) ||
@@ -1105,12 +1168,23 @@ static int omap8250_no_handle_irq(struct uart_port *port)
 	return 0;
 }
 
+static struct omap8250_dma_params am654_dma = {
+	.rx_size = SZ_2K,
+	.rx_trigger = 1,
+	.tx_trigger = TX_TRIGGER,
+};
+
 static struct omap8250_dma_params am33xx_dma = {
 	.rx_size = RX_TRIGGER,
 	.rx_trigger = RX_TRIGGER,
 	.tx_trigger = TX_TRIGGER,
 };
 
+static struct omap8250_platdata am654_platdata = {
+	.dma_params	= &am654_dma,
+	.habit		= UART_HAS_EFR2,
+};
+
 static struct omap8250_platdata am33xx_platdata = {
 	.dma_params	= &am33xx_dma,
 	.habit		= OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE,
@@ -1122,7 +1196,7 @@ static struct omap8250_platdata omap4_platdata = {
 };
 
 static const struct of_device_id omap8250_dt_ids[] = {
-	{ .compatible = "ti,am654-uart" },
+	{ .compatible = "ti,am654-uart", .data = &am654_platdata, },
 	{ .compatible = "ti,omap2-uart" },
 	{ .compatible = "ti,omap3-uart" },
 	{ .compatible = "ti,omap4-uart", .data = &omap4_platdata, },
@@ -1491,7 +1565,7 @@ static int omap8250_runtime_resume(struct device *dev)
 	if (omap8250_lost_context(up))
 		omap8250_restore_regs(up);
 
-	if (up->dma && up->dma->rxchan)
+	if (up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2))
 		omap_8250_rx_dma(up);
 
 	priv->latency = priv->calc_latency;
-- 
2.25.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-03-19 11:03 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-19 11:03 [PATCH 0/6] 8250_omap: Add DMA for AM654/J721e type UARTs Vignesh Raghavendra
2020-03-19 11:03 ` [PATCH 1/6] serial: 8250: 8250_omap: Terminate DMA before pushing data on RX timeout Vignesh Raghavendra
2020-03-19 11:03 ` [PATCH 2/6] serial: 8250: 8250_omap: Account for data in flight during DMA teardown Vignesh Raghavendra
2020-03-19 11:03 ` [PATCH 3/6] serial: 8250: 8250_omap: Move locking out from __dma_rx_do_complete() Vignesh Raghavendra
2020-03-19 11:03 ` [PATCH 4/6] serial: 8250: 8250_omap: Extend driver data to pass FIFO trigger info Vignesh Raghavendra
2020-03-19 11:03 ` [PATCH 5/6] serial: 8250: 8250_omap: Work around errata causing spurious IRQs with DMA Vignesh Raghavendra
2020-03-19 11:03 ` [PATCH 6/6] serial: 8250: 8250_omap: Add DMA support for UARTs on K3 SoCs Vignesh Raghavendra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).