Linux Serial subsystem development
 help / color / mirror / Atom feed
* [PATCH AUTOSEL 4.9 013/107] serial: fsl_lpuart: clear parity enable bit when disable parity
From: Sasha Levin @ 2019-01-28 16:18 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Andy Duan, Lukas Wunner, Greg Kroah-Hartman, Sasha Levin,
	linux-serial
In-Reply-To: <20190128161947.57405-1-sashal@kernel.org>

From: Andy Duan <fugang.duan@nxp.com>

[ Upstream commit 397bd9211fe014b347ca8f95a8f4e1017bac1aeb ]

Current driver only enable parity enable bit and never clear it
when user set the termios. The fix clear the parity enable bit when
PARENB flag is not set in termios->c_cflag.

Cc: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Andy Duan <fugang.duan@nxp.com>
Reviewed-by: Fabio Estevam <festevam@gmail.com>
Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/fsl_lpuart.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index e2ec04904f54..5c471c3481bd 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1344,6 +1344,8 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				cr1 &= ~UARTCR1_PT;
 		}
+	} else {
+		cr1 &= ~UARTCR1_PE;
 	}
 
 	/* ask the core to calculate the divisor */
@@ -1487,6 +1489,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				ctrl &= ~UARTCTRL_PT;
 		}
+	} else {
+		ctrl &= ~UARTCTRL_PE;
 	}
 
 	/* ask the core to calculate the divisor */
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.14 105/170] tty: serial: samsung: Properly set flags in autoCTS mode
From: Sasha Levin @ 2019-01-28 16:10 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Beomho Seo, Marek Szyprowski, Greg Kroah-Hartman, Sasha Levin,
	linux-serial
In-Reply-To: <20190128161200.55107-1-sashal@kernel.org>

From: Beomho Seo <beomho.seo@samsung.com>

[ Upstream commit 31e933645742ee6719d37573a27cce0761dcf92b ]

Commit 391f93f2ec9f ("serial: core: Rework hw-assited flow control support")
has changed the way the autoCTS mode is handled.

According to that change, serial drivers which enable H/W autoCTS mode must
set UPSTAT_AUTOCTS to prevent the serial core from inadvertently disabling
TX. This patch adds proper handling of UPSTAT_AUTOCTS flag.

Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
[mszyprow: rephrased commit message]
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/samsung.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 57baa84ccf86..f4b8e4e17a86 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1343,11 +1343,14 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
 	wr_regl(port, S3C2410_ULCON, ulcon);
 	wr_regl(port, S3C2410_UBRDIV, quot);
 
+	port->status &= ~UPSTAT_AUTOCTS;
+
 	umcon = rd_regl(port, S3C2410_UMCON);
 	if (termios->c_cflag & CRTSCTS) {
 		umcon |= S3C2410_UMCOM_AFC;
 		/* Disable RTS when RX FIFO contains 63 bytes */
 		umcon &= ~S3C2412_UMCON_AFC_8;
+		port->status = UPSTAT_AUTOCTS;
 	} else {
 		umcon &= ~S3C2410_UMCOM_AFC;
 	}
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.14 020/170] serial: fsl_lpuart: clear parity enable bit when disable parity
From: Sasha Levin @ 2019-01-28 16:09 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Andy Duan, Lukas Wunner, Greg Kroah-Hartman, Sasha Levin,
	linux-serial
In-Reply-To: <20190128161200.55107-1-sashal@kernel.org>

From: Andy Duan <fugang.duan@nxp.com>

[ Upstream commit 397bd9211fe014b347ca8f95a8f4e1017bac1aeb ]

Current driver only enable parity enable bit and never clear it
when user set the termios. The fix clear the parity enable bit when
PARENB flag is not set in termios->c_cflag.

Cc: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Andy Duan <fugang.duan@nxp.com>
Reviewed-by: Fabio Estevam <festevam@gmail.com>
Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/fsl_lpuart.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index fd64ac2c1a74..716c33b2a11c 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1482,6 +1482,8 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				cr1 &= ~UARTCR1_PT;
 		}
+	} else {
+		cr1 &= ~UARTCR1_PE;
 	}
 
 	/* ask the core to calculate the divisor */
@@ -1694,6 +1696,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				ctrl &= ~UARTCTRL_PT;
 		}
+	} else {
+		ctrl &= ~UARTCTRL_PE;
 	}
 
 	/* ask the core to calculate the divisor */
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.19 164/258] tty: serial: samsung: Properly set flags in autoCTS mode
From: Sasha Levin @ 2019-01-28 15:57 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Beomho Seo, Marek Szyprowski, Greg Kroah-Hartman, Sasha Levin,
	linux-serial
In-Reply-To: <20190128155924.51521-1-sashal@kernel.org>

From: Beomho Seo <beomho.seo@samsung.com>

[ Upstream commit 31e933645742ee6719d37573a27cce0761dcf92b ]

Commit 391f93f2ec9f ("serial: core: Rework hw-assited flow control support")
has changed the way the autoCTS mode is handled.

According to that change, serial drivers which enable H/W autoCTS mode must
set UPSTAT_AUTOCTS to prevent the serial core from inadvertently disabling
TX. This patch adds proper handling of UPSTAT_AUTOCTS flag.

Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
[mszyprow: rephrased commit message]
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/samsung.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 2f8fa184aafa..c6058b52d5d5 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1365,11 +1365,14 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
 	wr_regl(port, S3C2410_ULCON, ulcon);
 	wr_regl(port, S3C2410_UBRDIV, quot);
 
+	port->status &= ~UPSTAT_AUTOCTS;
+
 	umcon = rd_regl(port, S3C2410_UMCON);
 	if (termios->c_cflag & CRTSCTS) {
 		umcon |= S3C2410_UMCOM_AFC;
 		/* Disable RTS when RX FIFO contains 63 bytes */
 		umcon &= ~S3C2412_UMCON_AFC_8;
+		port->status = UPSTAT_AUTOCTS;
 	} else {
 		umcon &= ~S3C2410_UMCOM_AFC;
 	}
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.19 163/258] serial: sh-sci: Resume PIO in sci_rx_interrupt() on DMA failure
From: Sasha Levin @ 2019-01-28 15:57 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Geert Uytterhoeven, Greg Kroah-Hartman, Sasha Levin, linux-serial
In-Reply-To: <20190128155924.51521-1-sashal@kernel.org>

From: Geert Uytterhoeven <geert+renesas@glider.be>

[ Upstream commit 71ab1c0336c71ace5725740f200beca9667a339f ]

On (H)SCIF, sci_submit_rx() is called in the receive interrupt handler.
Hence if DMA submission fails, the interrupt handler should resume
handling reception using PIO, else no more data is received.

Make sci_submit_rx() return an error indicator, so the receive interrupt
handler can act appropriately.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/sh-sci.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 5fdd7944b73b..c6e8ca2e5f7d 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1331,7 +1331,7 @@ static void sci_tx_dma_release(struct sci_port *s)
 	dma_release_channel(chan);
 }
 
-static void sci_submit_rx(struct sci_port *s, bool port_lock_held)
+static int sci_submit_rx(struct sci_port *s, bool port_lock_held)
 {
 	struct dma_chan *chan = s->chan_rx;
 	struct uart_port *port = &s->port;
@@ -1359,7 +1359,7 @@ static void sci_submit_rx(struct sci_port *s, bool port_lock_held)
 	s->active_rx = s->cookie_rx[0];
 
 	dma_async_issue_pending(chan);
-	return;
+	return 0;
 
 fail:
 	/* Switch to PIO */
@@ -1374,6 +1374,7 @@ static void sci_submit_rx(struct sci_port *s, bool port_lock_held)
 	sci_start_rx(port);
 	if (!port_lock_held)
 		spin_unlock_irqrestore(&port->lock, flags);
+	return -EAGAIN;
 }
 
 static void work_fn_tx(struct work_struct *work)
@@ -1668,8 +1669,10 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
 			disable_irq_nosync(irq);
 			scr |= SCSCR_RDRQE;
 		} else {
+			if (sci_submit_rx(s, false) < 0)
+				goto handle_pio;
+
 			scr &= ~SCSCR_RIE;
-			sci_submit_rx(s, false);
 		}
 		serial_port_out(port, SCSCR, scr);
 		/* Clear current interrupt */
@@ -1681,6 +1684,8 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
 
 		return IRQ_HANDLED;
 	}
+
+handle_pio:
 #endif
 
 	if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) {
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.19 162/258] serial: sh-sci: Fix locking in sci_submit_rx()
From: Sasha Levin @ 2019-01-28 15:57 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Geert Uytterhoeven, Greg Kroah-Hartman, Sasha Levin, linux-serial
In-Reply-To: <20190128155924.51521-1-sashal@kernel.org>

From: Geert Uytterhoeven <geert+renesas@glider.be>

[ Upstream commit dd1f2250da95e87cb3e612858f94b14f99445a7c ]

Some callers of sci_submit_rx() hold the port spinlock, others don't.
During fallback to PIO, the driver needs to obtain the port spinlock.
If the lock was already held, spinlock recursion is detected, causing a
deadlock: BUG: spinlock recursion on CPU#0.

Fix this by adding a flag parameter to sci_submit_rx() for the caller to
indicate the port spinlock is already held, so spinlock recursion can be
avoided.

Move the spin_lock_irqsave() up, so all DMA disable steps are protected,
which is safe as the recently introduced dmaengine_terminate_async() can
be called in atomic context.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/sh-sci.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index effba6ce0caa..5fdd7944b73b 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1331,7 +1331,7 @@ static void sci_tx_dma_release(struct sci_port *s)
 	dma_release_channel(chan);
 }
 
-static void sci_submit_rx(struct sci_port *s)
+static void sci_submit_rx(struct sci_port *s, bool port_lock_held)
 {
 	struct dma_chan *chan = s->chan_rx;
 	struct uart_port *port = &s->port;
@@ -1362,16 +1362,18 @@ static void sci_submit_rx(struct sci_port *s)
 	return;
 
 fail:
+	/* Switch to PIO */
+	if (!port_lock_held)
+		spin_lock_irqsave(&port->lock, flags);
 	if (i)
 		dmaengine_terminate_async(chan);
 	for (i = 0; i < 2; i++)
 		s->cookie_rx[i] = -EINVAL;
 	s->active_rx = -EINVAL;
-	/* Switch to PIO */
-	spin_lock_irqsave(&port->lock, flags);
 	s->chan_rx = NULL;
 	sci_start_rx(port);
-	spin_unlock_irqrestore(&port->lock, flags);
+	if (!port_lock_held)
+		spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void work_fn_tx(struct work_struct *work)
@@ -1491,7 +1493,7 @@ static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
 	}
 
 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
-		sci_submit_rx(s);
+		sci_submit_rx(s, true);
 
 	/* Direct new serial port interrupts back to CPU */
 	scr = serial_port_in(port, SCSCR);
@@ -1617,7 +1619,7 @@ static void sci_request_dma(struct uart_port *port)
 		s->chan_rx_saved = s->chan_rx = chan;
 
 		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
-			sci_submit_rx(s);
+			sci_submit_rx(s, false);
 	}
 }
 
@@ -1667,7 +1669,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
 			scr |= SCSCR_RDRQE;
 		} else {
 			scr &= ~SCSCR_RIE;
-			sci_submit_rx(s);
+			sci_submit_rx(s, false);
 		}
 		serial_port_out(port, SCSCR, scr);
 		/* Clear current interrupt */
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.19 028/258] serial: fsl_lpuart: clear parity enable bit when disable parity
From: Sasha Levin @ 2019-01-28 15:55 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Andy Duan, Lukas Wunner, Greg Kroah-Hartman, Sasha Levin,
	linux-serial
In-Reply-To: <20190128155924.51521-1-sashal@kernel.org>

From: Andy Duan <fugang.duan@nxp.com>

[ Upstream commit 397bd9211fe014b347ca8f95a8f4e1017bac1aeb ]

Current driver only enable parity enable bit and never clear it
when user set the termios. The fix clear the parity enable bit when
PARENB flag is not set in termios->c_cflag.

Cc: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Andy Duan <fugang.duan@nxp.com>
Reviewed-by: Fabio Estevam <festevam@gmail.com>
Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/fsl_lpuart.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3f8d1274fc85..7d030c2e42ff 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1477,6 +1477,8 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				cr1 &= ~UARTCR1_PT;
 		}
+	} else {
+		cr1 &= ~UARTCR1_PE;
 	}
 
 	/* ask the core to calculate the divisor */
@@ -1688,6 +1690,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				ctrl &= ~UARTCTRL_PT;
 		}
+	} else {
+		ctrl &= ~UARTCTRL_PE;
 	}
 
 	/* ask the core to calculate the divisor */
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.20 195/304] tty: serial: samsung: Properly set flags in autoCTS mode
From: Sasha Levin @ 2019-01-28 15:41 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Beomho Seo, Marek Szyprowski, Greg Kroah-Hartman, Sasha Levin,
	linux-serial
In-Reply-To: <20190128154341.47195-1-sashal@kernel.org>

From: Beomho Seo <beomho.seo@samsung.com>

[ Upstream commit 31e933645742ee6719d37573a27cce0761dcf92b ]

Commit 391f93f2ec9f ("serial: core: Rework hw-assited flow control support")
has changed the way the autoCTS mode is handled.

According to that change, serial drivers which enable H/W autoCTS mode must
set UPSTAT_AUTOCTS to prevent the serial core from inadvertently disabling
TX. This patch adds proper handling of UPSTAT_AUTOCTS flag.

Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
[mszyprow: rephrased commit message]
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/samsung.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index da1bd4bba8a9..2a49b6d876b8 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1365,11 +1365,14 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
 	wr_regl(port, S3C2410_ULCON, ulcon);
 	wr_regl(port, S3C2410_UBRDIV, quot);
 
+	port->status &= ~UPSTAT_AUTOCTS;
+
 	umcon = rd_regl(port, S3C2410_UMCON);
 	if (termios->c_cflag & CRTSCTS) {
 		umcon |= S3C2410_UMCOM_AFC;
 		/* Disable RTS when RX FIFO contains 63 bytes */
 		umcon &= ~S3C2412_UMCON_AFC_8;
+		port->status = UPSTAT_AUTOCTS;
 	} else {
 		umcon &= ~S3C2410_UMCOM_AFC;
 	}
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.20 194/304] serial: sh-sci: Resume PIO in sci_rx_interrupt() on DMA failure
From: Sasha Levin @ 2019-01-28 15:41 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Geert Uytterhoeven, Greg Kroah-Hartman, Sasha Levin, linux-serial
In-Reply-To: <20190128154341.47195-1-sashal@kernel.org>

From: Geert Uytterhoeven <geert+renesas@glider.be>

[ Upstream commit 71ab1c0336c71ace5725740f200beca9667a339f ]

On (H)SCIF, sci_submit_rx() is called in the receive interrupt handler.
Hence if DMA submission fails, the interrupt handler should resume
handling reception using PIO, else no more data is received.

Make sci_submit_rx() return an error indicator, so the receive interrupt
handler can act appropriately.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/sh-sci.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 613007d7165e..21fd3f6ad28e 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1331,7 +1331,7 @@ static void sci_tx_dma_release(struct sci_port *s)
 	dma_release_channel(chan);
 }
 
-static void sci_submit_rx(struct sci_port *s, bool port_lock_held)
+static int sci_submit_rx(struct sci_port *s, bool port_lock_held)
 {
 	struct dma_chan *chan = s->chan_rx;
 	struct uart_port *port = &s->port;
@@ -1359,7 +1359,7 @@ static void sci_submit_rx(struct sci_port *s, bool port_lock_held)
 	s->active_rx = s->cookie_rx[0];
 
 	dma_async_issue_pending(chan);
-	return;
+	return 0;
 
 fail:
 	/* Switch to PIO */
@@ -1374,6 +1374,7 @@ fail:
 	sci_start_rx(port);
 	if (!port_lock_held)
 		spin_unlock_irqrestore(&port->lock, flags);
+	return -EAGAIN;
 }
 
 static void work_fn_tx(struct work_struct *work)
@@ -1668,8 +1669,10 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
 			disable_irq_nosync(irq);
 			scr |= SCSCR_RDRQE;
 		} else {
+			if (sci_submit_rx(s, false) < 0)
+				goto handle_pio;
+
 			scr &= ~SCSCR_RIE;
-			sci_submit_rx(s, false);
 		}
 		serial_port_out(port, SCSCR, scr);
 		/* Clear current interrupt */
@@ -1681,6 +1684,8 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
 
 		return IRQ_HANDLED;
 	}
+
+handle_pio:
 #endif
 
 	if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) {
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.20 193/304] serial: sh-sci: Fix locking in sci_submit_rx()
From: Sasha Levin @ 2019-01-28 15:41 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Geert Uytterhoeven, Greg Kroah-Hartman, Sasha Levin, linux-serial
In-Reply-To: <20190128154341.47195-1-sashal@kernel.org>

From: Geert Uytterhoeven <geert+renesas@glider.be>

[ Upstream commit dd1f2250da95e87cb3e612858f94b14f99445a7c ]

Some callers of sci_submit_rx() hold the port spinlock, others don't.
During fallback to PIO, the driver needs to obtain the port spinlock.
If the lock was already held, spinlock recursion is detected, causing a
deadlock: BUG: spinlock recursion on CPU#0.

Fix this by adding a flag parameter to sci_submit_rx() for the caller to
indicate the port spinlock is already held, so spinlock recursion can be
avoided.

Move the spin_lock_irqsave() up, so all DMA disable steps are protected,
which is safe as the recently introduced dmaengine_terminate_async() can
be called in atomic context.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/sh-sci.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index cc56cb3b3eca..613007d7165e 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1331,7 +1331,7 @@ static void sci_tx_dma_release(struct sci_port *s)
 	dma_release_channel(chan);
 }
 
-static void sci_submit_rx(struct sci_port *s)
+static void sci_submit_rx(struct sci_port *s, bool port_lock_held)
 {
 	struct dma_chan *chan = s->chan_rx;
 	struct uart_port *port = &s->port;
@@ -1362,16 +1362,18 @@ static void sci_submit_rx(struct sci_port *s)
 	return;
 
 fail:
+	/* Switch to PIO */
+	if (!port_lock_held)
+		spin_lock_irqsave(&port->lock, flags);
 	if (i)
 		dmaengine_terminate_async(chan);
 	for (i = 0; i < 2; i++)
 		s->cookie_rx[i] = -EINVAL;
 	s->active_rx = -EINVAL;
-	/* Switch to PIO */
-	spin_lock_irqsave(&port->lock, flags);
 	s->chan_rx = NULL;
 	sci_start_rx(port);
-	spin_unlock_irqrestore(&port->lock, flags);
+	if (!port_lock_held)
+		spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void work_fn_tx(struct work_struct *work)
@@ -1491,7 +1493,7 @@ static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
 	}
 
 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
-		sci_submit_rx(s);
+		sci_submit_rx(s, true);
 
 	/* Direct new serial port interrupts back to CPU */
 	scr = serial_port_in(port, SCSCR);
@@ -1617,7 +1619,7 @@ static void sci_request_dma(struct uart_port *port)
 		s->chan_rx_saved = s->chan_rx = chan;
 
 		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
-			sci_submit_rx(s);
+			sci_submit_rx(s, false);
 	}
 }
 
@@ -1667,7 +1669,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
 			scr |= SCSCR_RDRQE;
 		} else {
 			scr &= ~SCSCR_RIE;
-			sci_submit_rx(s);
+			sci_submit_rx(s, false);
 		}
 		serial_port_out(port, SCSCR, scr);
 		/* Clear current interrupt */
-- 
2.19.1

^ permalink raw reply related

* [PATCH AUTOSEL 4.20 034/304] serial: fsl_lpuart: clear parity enable bit when disable parity
From: Sasha Levin @ 2019-01-28 15:39 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Andy Duan, Lukas Wunner, Greg Kroah-Hartman, Sasha Levin,
	linux-serial
In-Reply-To: <20190128154341.47195-1-sashal@kernel.org>

From: Andy Duan <fugang.duan@nxp.com>

[ Upstream commit 397bd9211fe014b347ca8f95a8f4e1017bac1aeb ]

Current driver only enable parity enable bit and never clear it
when user set the termios. The fix clear the parity enable bit when
PARENB flag is not set in termios->c_cflag.

Cc: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Andy Duan <fugang.duan@nxp.com>
Reviewed-by: Fabio Estevam <festevam@gmail.com>
Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/tty/serial/fsl_lpuart.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 00c220e4f43c..086642ea4b26 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1479,6 +1479,8 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				cr1 &= ~UARTCR1_PT;
 		}
+	} else {
+		cr1 &= ~UARTCR1_PE;
 	}
 
 	/* ask the core to calculate the divisor */
@@ -1690,6 +1692,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
 			else
 				ctrl &= ~UARTCTRL_PT;
 		}
+	} else {
+		ctrl &= ~UARTCTRL_PE;
 	}
 
 	/* ask the core to calculate the divisor */
-- 
2.19.1

^ permalink raw reply related

* [PATCH v2 2/2] Serial: Ingenic: Add X1000 suppor for the UART driver.
From: Zhou Yanjie @ 2019-01-28 13:57 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, linux-serial, devicetree, robh+dt, paul.burton,
	gregkh, jslaby, mark.rutland, syq, jiaxun.yang, 772753199
In-Reply-To: <1548683821-120924-1-git-send-email-zhouyanjie@zoho.com>

Add support for probing the 8250_ingenic driver on the
X1000 Soc from Ingenic.

Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
---
 Documentation/devicetree/bindings/serial/ingenic,uart.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/serial/ingenic,uart.txt b/Documentation/devicetree/bindings/serial/ingenic,uart.txt
index c3c6406..24ed876 100644
--- a/Documentation/devicetree/bindings/serial/ingenic,uart.txt
+++ b/Documentation/devicetree/bindings/serial/ingenic,uart.txt
@@ -6,7 +6,8 @@ Required properties:
   - "ingenic,jz4760-uart",
   - "ingenic,jz4770-uart",
   - "ingenic,jz4775-uart",
-  - "ingenic,jz4780-uart".
+  - "ingenic,jz4780-uart",
+  - "ingenic,x1000-uart".
 - reg : offset and length of the register set for the device.
 - interrupts : should contain uart interrupt.
 - clocks : phandles to the module & baud clocks.
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 1/2] Serial: Ingenic: Add X1000 suppor for the UART driver.
From: Zhou Yanjie @ 2019-01-28 13:57 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, linux-serial, devicetree, robh+dt, paul.burton,
	gregkh, jslaby, mark.rutland, syq, jiaxun.yang, 772753199
In-Reply-To: <1548683821-120924-1-git-send-email-zhouyanjie@zoho.com>

Add support for probing the 8250_ingenic driver on the
X1000 Soc from Ingenic.

Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
---
 drivers/tty/serial/8250/8250_ingenic.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c
index 15a8c8d..424c07c 100644
--- a/drivers/tty/serial/8250/8250_ingenic.c
+++ b/drivers/tty/serial/8250/8250_ingenic.c
@@ -129,22 +129,21 @@ static int __init ingenic_early_console_setup(struct earlycon_device *dev,
 	return 0;
 }
 
-EARLYCON_DECLARE(jz4740_uart, ingenic_early_console_setup);
 OF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart",
 		    ingenic_early_console_setup);
 
-EARLYCON_DECLARE(jz4770_uart, ingenic_early_console_setup);
 OF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart",
 		    ingenic_early_console_setup);
 
-EARLYCON_DECLARE(jz4775_uart, ingenic_early_console_setup);
 OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart",
 		    ingenic_early_console_setup);
 
-EARLYCON_DECLARE(jz4780_uart, ingenic_early_console_setup);
 OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
 		    ingenic_early_console_setup);
 
+OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart",
+		    ingenic_early_console_setup);
+
 static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
 {
 	int ier;
@@ -328,12 +327,18 @@ static const struct ingenic_uart_config jz4780_uart_config = {
 	.fifosize = 64,
 };
 
+static const struct ingenic_uart_config x1000_uart_config = {
+	.tx_loadsz = 32,
+	.fifosize = 64,
+};
+
 static const struct of_device_id of_match[] = {
 	{ .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
 	{ .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
 	{ .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config },
 	{ .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
 	{ .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config },
+	{ .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, of_match);
-- 
2.7.4

^ permalink raw reply related

* Serial: Ingenic: Add X1000 suppor for the UART driver.
From: Zhou Yanjie @ 2019-01-28 13:56 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, linux-serial, devicetree, robh+dt, paul.burton,
	gregkh, jslaby, mark.rutland, syq, jiaxun.yang, 772753199
In-Reply-To: <1548667176-119830-1-git-send-email-zhouyanjie@zoho.com>

v1->v2: Remove unnecessary "EARLYCON_DECLARE".

^ permalink raw reply

* Re: [PATCH 1/2] Serial: Ingenic: Add support for the X1000.
From: Zhou Yanjie @ 2019-01-28 12:44 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-mips, linux-kernel, linux-serial, devicetree, robh+dt,
	paul.burton, jslaby, mark.rutland, syq, jiaxun.yang, 772753199
In-Reply-To: <20190128093059.GA31657@kroah.com>

My fault, I will fix these in v2.

On 2019年01月28日 17:30, Greg KH wrote:
> On Mon, Jan 28, 2019 at 05:19:35PM +0800, Zhou Yanjie wrote:
>> Add support for probing the 8250_ingenic driver on the
>> X1000 Soc from Ingenic.
>>
>> Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
>> ---
>>   drivers/tty/serial/8250/8250_ingenic.c | 10 ++++++++++
>>   1 file changed, 10 insertions(+)
> You sent two different patches with the same subject: line, yet they did
> totally different things.
>
> Please fix this up and resend with a better set of subject lines.
>
> thanks,
>
> greg k-h

^ permalink raw reply

* Re: [PATCH 1/2] Serial: Ingenic: Add support for the X1000.
From: Geert Uytterhoeven @ 2019-01-28 10:53 UTC (permalink / raw)
  To: Zhou Yanjie
  Cc: linux-mips, Linux Kernel Mailing List, open list:SERIAL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Rob Herring, Paul Burton, Greg KH, Jiri Slaby, Mark Rutland, syq,
	jiaxun.yang, 772753199
In-Reply-To: <1548667176-119830-2-git-send-email-zhouyanjie@zoho.com>

Hi Zhou,

On Mon, Jan 28, 2019 at 10:23 AM Zhou Yanjie <zhouyanjie@zoho.com> wrote:
> Add support for probing the 8250_ingenic driver on the
> X1000 Soc from Ingenic.
>
> Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>

Thanks for your patch!

> --- a/drivers/tty/serial/8250/8250_ingenic.c
> +++ b/drivers/tty/serial/8250/8250_ingenic.c
> @@ -145,6 +145,10 @@ EARLYCON_DECLARE(jz4780_uart, ingenic_early_console_setup);
>  OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
>                     ingenic_early_console_setup);
>
> +EARLYCON_DECLARE(x1000_uart, ingenic_early_console_setup);
> +OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart",
> +                   ingenic_early_console_setup);

As of commit 2eaa790989e03900 ("earlycon: Use common framework for
earlycon declarations") it is no longer needer to specify both
EARLYCON_DECLARE() and OF_EARLYCON_DECLARE().

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH 1/2] Serial: Ingenic: Add support for the X1000.
From: Greg KH @ 2019-01-28  9:30 UTC (permalink / raw)
  To: Zhou Yanjie
  Cc: linux-mips, linux-kernel, linux-serial, devicetree, robh+dt,
	paul.burton, jslaby, mark.rutland, syq, jiaxun.yang, 772753199
In-Reply-To: <1548667176-119830-2-git-send-email-zhouyanjie@zoho.com>

On Mon, Jan 28, 2019 at 05:19:35PM +0800, Zhou Yanjie wrote:
> Add support for probing the 8250_ingenic driver on the
> X1000 Soc from Ingenic.
> 
> Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
> ---
>  drivers/tty/serial/8250/8250_ingenic.c | 10 ++++++++++
>  1 file changed, 10 insertions(+)

You sent two different patches with the same subject: line, yet they did
totally different things.

Please fix this up and resend with a better set of subject lines.

thanks,

greg k-h

^ permalink raw reply

* [PATCH 2/2] Serial: Ingenic: Add support for the X1000.
From: Zhou Yanjie @ 2019-01-28  9:19 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, linux-serial, devicetree, robh+dt, paul.burton,
	gregkh, jslaby, mark.rutland, syq, jiaxun.yang, 772753199
In-Reply-To: <1548667176-119830-1-git-send-email-zhouyanjie@zoho.com>

Add support for probing the 8250_ingenic driver on the
X1000 Soc from Ingenic.

Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
---
 Documentation/devicetree/bindings/serial/ingenic,uart.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/serial/ingenic,uart.txt b/Documentation/devicetree/bindings/serial/ingenic,uart.txt
index c3c6406..24ed876 100644
--- a/Documentation/devicetree/bindings/serial/ingenic,uart.txt
+++ b/Documentation/devicetree/bindings/serial/ingenic,uart.txt
@@ -6,7 +6,8 @@ Required properties:
   - "ingenic,jz4760-uart",
   - "ingenic,jz4770-uart",
   - "ingenic,jz4775-uart",
-  - "ingenic,jz4780-uart".
+  - "ingenic,jz4780-uart",
+  - "ingenic,x1000-uart".
 - reg : offset and length of the register set for the device.
 - interrupts : should contain uart interrupt.
 - clocks : phandles to the module & baud clocks.
-- 
2.7.4

^ permalink raw reply related

* [PATCH 1/2] Serial: Ingenic: Add support for the X1000.
From: Zhou Yanjie @ 2019-01-28  9:19 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, linux-serial, devicetree, robh+dt, paul.burton,
	gregkh, jslaby, mark.rutland, syq, jiaxun.yang, 772753199
In-Reply-To: <1548667176-119830-1-git-send-email-zhouyanjie@zoho.com>

Add support for probing the 8250_ingenic driver on the
X1000 Soc from Ingenic.

Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
---
 drivers/tty/serial/8250/8250_ingenic.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c
index 15a8c8d..1999e3b 100644
--- a/drivers/tty/serial/8250/8250_ingenic.c
+++ b/drivers/tty/serial/8250/8250_ingenic.c
@@ -145,6 +145,10 @@ EARLYCON_DECLARE(jz4780_uart, ingenic_early_console_setup);
 OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
 		    ingenic_early_console_setup);
 
+EARLYCON_DECLARE(x1000_uart, ingenic_early_console_setup);
+OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart",
+		    ingenic_early_console_setup);
+
 static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
 {
 	int ier;
@@ -328,12 +332,18 @@ static const struct ingenic_uart_config jz4780_uart_config = {
 	.fifosize = 64,
 };
 
+static const struct ingenic_uart_config x1000_uart_config = {
+	.tx_loadsz = 32,
+	.fifosize = 64,
+};
+
 static const struct of_device_id of_match[] = {
 	{ .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
 	{ .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
 	{ .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config },
 	{ .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
 	{ .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config },
+	{ .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, of_match);
-- 
2.7.4

^ permalink raw reply related

* Add Ingenic X1000 serial support.
From: Zhou Yanjie @ 2019-01-28  9:19 UTC (permalink / raw)
  To: linux-mips
  Cc: linux-kernel, linux-serial, devicetree, robh+dt, paul.burton,
	gregkh, jslaby, mark.rutland, syq, jiaxun.yang, 772753199

Add Ingenic X1000 serial support.

^ permalink raw reply

* RE: [PATCH] 8250_pci.c: Update NI specific devices class to multi serial
From: Guan Yung Tseng @ 2019-01-28  8:04 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Greg Kroah-Hartman, open list:SERIAL DRIVERS,
	Linux Kernel Mailing List
In-Reply-To: <CAHp75Vd+p8d87v_YEkzMLh8sxmvPYk3XeFdkpsrY_jTsC2zHCQ@mail.gmail.com>


> On Wed, Jan 23, 2019 at 5:18 PM Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
> >
> > On Mon, Jan 14, 2019 at 4:13 PM Guan Yung Tseng <guan.yung.tseng@ni.com> wrote:
> > >
> > > Modified NI devices class to PCI_CLASS_COMMUNICATION_MULTISERIAL.
> > > The reason of doing this is because all NI multi port serial cards 
> > > use PCI_CLASS_COMMUNICATION_OTHER class and thus fail the 
> > > serial_pci_is_class_communication test added in the commit 
> > > 7d8905d06405
> > > ("serial: 8250_pci: Enable device after we check black list").
> >
> > Guan, can you provide an output of the
> >
> > lspci -nk -xx -vv -s <BDF>
> >
> > of your device, where <BDF> is a PCI address in a form of bus:device.function?
>

Output of lspci before applied the patch

admin@NI-PXIe-8840Quad-Core-0308D657:~# lspci -nk -xx -vv -s 0d:0e.0
0d:0e.0 0780: 1093:70e8
        Subsystem: 1093:70e8
        Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping
- SERR+ FastB2B- DisINTx-
        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <M
Abort- >SERR- <PERR- INTx-
        Interrupt: pin A routed to IRQ 10
        Region 0: Memory at fe201000 (32-bit, non-prefetchable) [disabled] [size=4K]
00: 93 10 e8 70 40 01 80 02 00 00 80 07 10 20 00 00
10: 00 10 20 fe 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 93 10 e8 70
30: 00 00 00 00 f0 00 00 00 00 00 00 00 0a 01 00 00

admin@NI-PXIe-8840Quad-Core-0308D657:~# lspci -nk -xx -vv -s 0d:0f.0
0d:0f.0 0780: 1093:70ec
        Subsystem: 1093:70ec
        Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping
- SERR+ FastB2B- DisINTx-
        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <M
Abort- >SERR- <PERR- INTx-
        Interrupt: pin A routed to IRQ 11
        Region 0: Memory at fe200000 (32-bit, non-prefetchable) [disabled] [size=4K]
00: 93 10 ec 70 40 01 80 02 00 00 80 07 10 20 00 00
10: 00 00 20 fe 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 93 10 ec 70
30: 00 00 00 00 f0 00 00 00 00 00 00 00 0b 01 00 00

> While I'm still wondering of the above, can you also test the bellow patch if it fixes your issue?

The patch fixes the issue. The devices bind to the serial kernel driver successfully.
Below is the output of lspci -nk -xx -vv -s <BDF> for the same devices after I applied the patch.

admin@NI-PXIe-8840Quad-Core-0308D657:~# lspci -nk -xx -vv -s 0d:0e.0
0d:0e.0 0780: 1093:70e8
        Subsystem: 1093:70e8
        Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping
- SERR+ FastB2B- DisINTx-
        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <M
Abort- >SERR- <PERR- INTx-
        Interrupt: pin A routed to IRQ 17
        Region 0: Memory at fe201000 (32-bit, non-prefetchable) [size=4K]
        Kernel driver in use: serial
00: 93 10 e8 70 42 01 80 02 00 00 80 07 10 20 00 00
10: 00 10 20 fe 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 93 10 e8 70
30: 00 00 00 00 f0 00 00 00 00 00 00 00 0a 01 00 00

admin@NI-PXIe-8840Quad-Core-0308D657:~# lspci -nk -xx -vv -s 0d:0f.0
0d:0f.0 0780: 1093:70ec
        Subsystem: 1093:70ec
        Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping
- SERR+ FastB2B- DisINTx-
        Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <M
Abort- >SERR- <PERR- INTx-
        Interrupt: pin A routed to IRQ 18
        Region 0: Memory at fe200000 (32-bit, non-prefetchable) [size=4K]
        Kernel driver in use: serial
00: 93 10 ec 70 42 01 80 02 00 00 80 07 10 20 00 00
10: 00 00 20 fe 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 93 10 ec 70
30: 00 00 00 00 f0 00 00 00 00 00 00 00 0b 01 00 00




^ permalink raw reply

* Re: [PATCH v10 1/3] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support
From: Long Cheng @ 2019-01-28  3:25 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Randy Dunlap, Rob Herring, Mark Rutland, Ryder Lee, Sean Wang,
	Nicolas Boichat, Matthias Brugger, Dan Williams,
	Greg Kroah-Hartman, Jiri Slaby, Sean Wang, dmaengine, devicetree,
	linux-arm-kernel, linux-mediatek, linux-kernel, linux-serial,
	srv_heupstream, Yingjoe Chen, YT Shen, Zhenbao Liu
In-Reply-To: <1547781016-890-2-git-send-email-long.cheng@mediatek.com>

On Fri, 2019-01-18 at 11:10 +0800, Long Cheng wrote:

Hi Vinod Koul,

Just a gentle ping!

thanks.

> In DMA engine framework, add 8250 uart dma to support MediaTek uart.
> If MediaTek uart enabled(SERIAL_8250_MT6577), and want to improve
> the performance, can enable the function.
> 
> Signed-off-by: Long Cheng <long.cheng@mediatek.com>
> ---
>  drivers/dma/mediatek/Kconfig          |   11 +
>  drivers/dma/mediatek/Makefile         |    1 +
>  drivers/dma/mediatek/mtk-uart-apdma.c |  669 +++++++++++++++++++++++++++++++++
>  3 files changed, 681 insertions(+)
>  create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c
> 
> diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig
> index 680fc05..ac49eb6 100644
> --- a/drivers/dma/mediatek/Kconfig
> +++ b/drivers/dma/mediatek/Kconfig
> @@ -24,3 +24,14 @@ config MTK_CQDMA
>  
>  	  This controller provides the channels which is dedicated to
>  	  memory-to-memory transfer to offload from CPU.
> +
> +config MTK_UART_APDMA
> +	tristate "MediaTek SoCs APDMA support for UART"
> +	depends on OF && SERIAL_8250_MT6577
> +	select DMA_ENGINE
> +	select DMA_VIRTUAL_CHANNELS
> +	help
> +	  Support for the UART DMA engine found on MediaTek MTK SoCs.
> +	  When SERIAL_8250_MT6577 is enabled, and if you want to use DMA,
> +	  you can enable the config. The DMA engine can only be used
> +	  with MediaTek SoCs.
> diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile
> index 41bb381..61a6d29 100644
> --- a/drivers/dma/mediatek/Makefile
> +++ b/drivers/dma/mediatek/Makefile
> @@ -1,2 +1,3 @@
> +obj-$(CONFIG_MTK_UART_APDMA) += mtk-uart-apdma.o
>  obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
>  obj-$(CONFIG_MTK_CQDMA) += mtk-cqdma.o
> diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c
> new file mode 100644
> index 0000000..427db69
> --- /dev/null
> +++ b/drivers/dma/mediatek/mtk-uart-apdma.c
> @@ -0,0 +1,669 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MediaTek Uart APDMA 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/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_dma.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#include "../virt-dma.h"
> +
> +/* The default number of virtual channel */
> +#define MTK_UART_APDMA_NR_VCHANS	8
> +
> +#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_CLR_B	(BIT(0) | BIT(1))
> +#define VFF_TX_INT_CLR_B	0
> +#define VFF_STOP_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 VFF_RING_SIZE	0xffffU
> +/* invert this bit when wrap ring head again */
> +#define VFF_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_uart_apdmadev {
> +	struct dma_device ddev;
> +	struct clk *clk;
> +	bool support_33bits;
> +	unsigned int dma_requests;
> +	unsigned int *dma_irq;
> +};
> +
> +struct mtk_uart_apdma_desc {
> +	struct virt_dma_desc vd;
> +
> +	unsigned int avail_len;
> +};
> +
> +struct mtk_chan {
> +	struct virt_dma_chan vc;
> +	struct dma_slave_config	cfg;
> +	void __iomem *base;
> +	struct mtk_uart_apdma_desc *desc;
> +
> +	enum dma_transfer_direction dir;
> +
> +	bool requested;
> +
> +	unsigned int rx_status;
> +};
> +
> +static inline struct mtk_uart_apdmadev *
> +to_mtk_uart_apdma_dev(struct dma_device *d)
> +{
> +	return container_of(d, struct mtk_uart_apdmadev, ddev);
> +}
> +
> +static inline struct mtk_chan *to_mtk_uart_apdma_chan(struct dma_chan *c)
> +{
> +	return container_of(c, struct mtk_chan, vc.chan);
> +}
> +
> +static inline struct mtk_uart_apdma_desc *to_mtk_uart_apdma_desc
> +	(struct dma_async_tx_descriptor *t)
> +{
> +	return container_of(t, struct mtk_uart_apdma_desc, vd.tx);
> +}
> +
> +static void mtk_uart_apdma_write(struct mtk_chan *c,
> +			       unsigned int reg, unsigned int val)
> +{
> +	writel(val, c->base + reg);
> +}
> +
> +static unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg)
> +{
> +	return readl(c->base + reg);
> +}
> +
> +static void mtk_uart_apdma_desc_free(struct virt_dma_desc *vd)
> +{
> +	struct dma_chan *chan = vd->tx.chan;
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +
> +	kfree(c->desc);
> +}
> +
> +static void mtk_uart_apdma_start_tx(struct mtk_chan *c)
> +{
> +	unsigned int len, send, left, wpt, d_wpt, tmp;
> +	int ret;
> +
> +	left = mtk_uart_apdma_read(c, VFF_LEFT_SIZE);
> +	if (!left) {
> +		mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
> +		return;
> +	}
> +
> +	/* Wait 1sec for flush, can't sleep */
> +	ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
> +			tmp != VFF_FLUSH_B, 0, 1000000);
> +	if (ret)
> +		dev_warn(c->vc.chan.device->dev, "tx: fail, debug=0x%x\n",
> +			mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
> +
> +	send = min_t(unsigned int, left, c->desc->avail_len);
> +	wpt = mtk_uart_apdma_read(c, VFF_WPT);
> +	len = mtk_uart_apdma_read(c, VFF_LEN);
> +
> +	d_wpt = wpt + send;
> +	if ((d_wpt & VFF_RING_SIZE) >= len) {
> +		d_wpt = d_wpt - len;
> +		d_wpt = d_wpt ^ VFF_RING_WRAP;
> +	}
> +	mtk_uart_apdma_write(c, VFF_WPT, d_wpt);
> +
> +	c->desc->avail_len -= send;
> +
> +	mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
> +	if (mtk_uart_apdma_read(c, VFF_FLUSH) == 0U)
> +		mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
> +}
> +
> +static void mtk_uart_apdma_start_rx(struct mtk_chan *c)
> +{
> +	struct mtk_uart_apdma_desc *d = c->desc;
> +	unsigned int len, wg, rg;
> +	int cnt;
> +
> +	if ((mtk_uart_apdma_read(c, VFF_VALID_SIZE) == 0U) ||
> +		!d || !vchan_next_desc(&c->vc))
> +		return;
> +
> +	len = mtk_uart_apdma_read(c, VFF_LEN);
> +	rg = mtk_uart_apdma_read(c, VFF_RPT);
> +	wg = mtk_uart_apdma_read(c, VFF_WPT);
> +	cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE);
> +	/*
> +	 * The buffer is ring buffer. If wrap bit different,
> +	 * represents the start of the next cycle for WPT
> +	 */
> +	if ((rg ^ wg) & VFF_RING_WRAP)
> +		cnt += len;
> +
> +	c->rx_status = cnt;
> +	mtk_uart_apdma_write(c, VFF_RPT, wg);
> +
> +	list_del(&d->vd.node);
> +	vchan_cookie_complete(&d->vd);
> +}
> +
> +static irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id)
> +{
> +	struct dma_chan *chan = (struct dma_chan *)dev_id;
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +	struct mtk_uart_apdma_desc *d;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&c->vc.lock, flags);
> +	if (c->dir == DMA_DEV_TO_MEM) {
> +		mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
> +		mtk_uart_apdma_start_rx(c);
> +	} else if (c->dir == DMA_MEM_TO_DEV) {
> +		d = c->desc;
> +
> +		mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
> +
> +		if (d->avail_len != 0U) {
> +			mtk_uart_apdma_start_tx(c);
> +		} else {
> +			list_del(&d->vd.node);
> +			vchan_cookie_complete(&d->vd);
> +		}
> +	}
> +	spin_unlock_irqrestore(&c->vc.lock, flags);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan)
> +{
> +	struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device);
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +	unsigned int tmp;
> +	int ret;
> +
> +	pm_runtime_get_sync(mtkd->ddev.dev);
> +
> +	mtk_uart_apdma_write(c, VFF_ADDR, 0);
> +	mtk_uart_apdma_write(c, VFF_THRE, 0);
> +	mtk_uart_apdma_write(c, VFF_LEN, 0);
> +	mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B);
> +
> +	ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
> +	if (ret) {
> +		dev_err(chan->device->dev, "dma reset: fail, timeout\n");
> +		return ret;
> +	}
> +
> +	if (!c->requested) {
> +		c->requested = true;
> +		ret = request_irq(mtkd->dma_irq[chan->chan_id],
> +				  mtk_uart_apdma_irq_handler, IRQF_TRIGGER_NONE,
> +				  KBUILD_MODNAME, chan);
> +		if (ret < 0) {
> +			dev_err(chan->device->dev, "Can't request dma IRQ\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (mtkd->support_33bits)
> +		mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
> +
> +	return ret;
> +}
> +
> +static void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan)
> +{
> +	struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device);
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +
> +	if (c->requested) {
> +		c->requested = false;
> +		free_irq(mtkd->dma_irq[chan->chan_id], chan);
> +	}
> +
> +	tasklet_kill(&c->vc.task);
> +
> +	vchan_free_chan_resources(&c->vc);
> +
> +	pm_runtime_put_sync(mtkd->ddev.dev);
> +}
> +
> +static enum dma_status mtk_uart_apdma_tx_status(struct dma_chan *chan,
> +					 dma_cookie_t cookie,
> +					 struct dma_tx_state *txstate)
> +{
> +	struct mtk_chan *c = to_mtk_uart_apdma_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_status = mtk_uart_apdma_read(c, VFF_RPT) & VFF_RING_SIZE;
> +		dma_set_residue(txstate, c->rx_status);
> +	} else if (ret == DMA_COMPLETE && c->dir == DMA_DEV_TO_MEM) {
> +		dma_set_residue(txstate, c->rx_status);
> +	} else {
> +		dma_set_residue(txstate, 0);
> +	}
> +	spin_unlock_irqrestore(&c->vc.lock, flags);
> +
> +	return ret;
> +}
> +
> +static void mtk_uart_apdma_config_write(struct dma_chan *chan,
> +			       struct dma_slave_config *cfg,
> +			       enum dma_transfer_direction dir)
> +{
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +	struct mtk_uart_apdmadev *mtkd =
> +				to_mtk_uart_apdma_dev(c->vc.chan.device);
> +	unsigned int tmp;
> +
> +	if (mtk_uart_apdma_read(c, VFF_EN) == VFF_EN_B)
> +		return;
> +
> +	c->dir = dir;
> +
> +	if (dir == DMA_DEV_TO_MEM) {
> +		tmp = cfg->src_addr_width * 1024;
> +
> +		mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr);
> +		mtk_uart_apdma_write(c, VFF_LEN, tmp);
> +		mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(tmp));
> +		mtk_uart_apdma_write(c, VFF_INT_EN,
> +				VFF_RX_INT_EN0_B | VFF_RX_INT_EN1_B);
> +		mtk_uart_apdma_write(c, VFF_RPT, 0);
> +		mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
> +	} else if (dir == DMA_MEM_TO_DEV)	{
> +		tmp = cfg->dst_addr_width * 1024;
> +
> +		mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr);
> +		mtk_uart_apdma_write(c, VFF_LEN, tmp);
> +		mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tmp));
> +		mtk_uart_apdma_write(c, VFF_WPT, 0);
> +		mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
> +	}
> +
> +	mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B);
> +
> +	if (mtkd->support_33bits)
> +		mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B);
> +
> +	if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B)
> +		dev_err(chan->device->dev, "dir[%d] fail\n", dir);
> +}
> +
> +/*
> + * dmaengine_prep_slave_single will call the function. and sglen is 1.
> + * 8250 uart using one ring buffer, and deal with one sg.
> + */
> +static struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg
> +	(struct dma_chan *chan, struct scatterlist *sgl,
> +	unsigned int sglen, enum dma_transfer_direction dir,
> +	unsigned long tx_flags, void *context)
> +{
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +	struct mtk_uart_apdma_desc *d;
> +
> +	if (!is_slave_direction(dir))
> +		return NULL;
> +
> +	mtk_uart_apdma_config_write(chan, &c->cfg, dir);
> +
> +	/* Now allocate and setup the descriptor */
> +	d = kzalloc(sizeof(*d), GFP_ATOMIC);
> +	if (!d)
> +		return NULL;
> +
> +	/* sglen is 1 */
> +	d->avail_len = sg_dma_len(sgl);
> +
> +	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
> +}
> +
> +static void mtk_uart_apdma_issue_pending(struct dma_chan *chan)
> +{
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +	struct virt_dma_desc *vd;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&c->vc.lock, flags);
> +	if (vchan_issue_pending(&c->vc)) {
> +		vd = vchan_next_desc(&c->vc);
> +		c->desc = to_mtk_uart_apdma_desc(&vd->tx);
> +	}
> +
> +	if (c->dir == DMA_DEV_TO_MEM)
> +		mtk_uart_apdma_start_rx(c);
> +	else if (c->dir == DMA_MEM_TO_DEV)
> +		mtk_uart_apdma_start_tx(c);
> +
> +	spin_unlock_irqrestore(&c->vc.lock, flags);
> +}
> +
> +static int mtk_uart_apdma_slave_config(struct dma_chan *chan,
> +				   struct dma_slave_config *config)
> +{
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +
> +	memcpy(&c->cfg, config, sizeof(*config));
> +
> +	return 0;
> +}
> +
> +static int mtk_uart_apdma_terminate_all(struct dma_chan *chan)
> +{
> +	struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
> +	unsigned long flags;
> +	unsigned int tmp;
> +	int ret;
> +
> +	spin_lock_irqsave(&c->vc.lock, flags);
> +
> +	mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
> +	/* Wait 1sec for flush, can't sleep */
> +	ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
> +			tmp != VFF_FLUSH_B, 0, 1000000);
> +	if (ret)
> +		dev_err(c->vc.chan.device->dev, "flush: fail, debug=0x%x\n",
> +			mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
> +
> +	/* set stop as 1 -> wait until en is 0 -> set stop as 0 */
> +	mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B);
> +	ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
> +	if (ret)
> +		dev_err(c->vc.chan.device->dev, "stop: fail, debug=0x%x\n",
> +			mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
> +
> +	mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B);
> +	mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B);
> +
> +	if (c->dir == DMA_DEV_TO_MEM)
> +		mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
> +	else if (c->dir == DMA_MEM_TO_DEV)
> +		mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
> +
> +	spin_unlock_irqrestore(&c->vc.lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mtk_uart_apdma_device_pause(struct dma_chan *chan)
> +{
> +	/* just for check caps pass */
> +	return 0;
> +}
> +
> +static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd)
> +{
> +	while (!list_empty(&mtkd->ddev.channels)) {
> +		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);
> +	}
> +}
> +
> +static const struct of_device_id mtk_uart_apdma_match[] = {
> +	{ .compatible = "mediatek,mt6577-uart-dma", },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, mtk_uart_apdma_match);
> +
> +static int mtk_uart_apdma_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct mtk_uart_apdmadev *mtkd;
> +	struct resource *res;
> +	struct mtk_chan *c;
> +	int bit_mask = 32, rc;
> +	unsigned int i;
> +
> +	mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL);
> +	if (!mtkd)
> +		return -ENOMEM;
> +
> +	mtkd->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(mtkd->clk)) {
> +		dev_err(&pdev->dev, "No clock specified\n");
> +		rc = PTR_ERR(mtkd->clk);
> +		return rc;
> +	}
> +
> +	if (of_property_read_bool(np, "dma-33bits"))
> +		mtkd->support_33bits = true;
> +
> +	if (mtkd->support_33bits)
> +		bit_mask = 33;
> +
> +	rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask));
> +	if (rc)
> +		return rc;
> +
> +	dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask);
> +	mtkd->ddev.device_alloc_chan_resources =
> +				mtk_uart_apdma_alloc_chan_resources;
> +	mtkd->ddev.device_free_chan_resources =
> +				mtk_uart_apdma_free_chan_resources;
> +	mtkd->ddev.device_tx_status = mtk_uart_apdma_tx_status;
> +	mtkd->ddev.device_issue_pending = mtk_uart_apdma_issue_pending;
> +	mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg;
> +	mtkd->ddev.device_config = mtk_uart_apdma_slave_config;
> +	mtkd->ddev.device_pause = mtk_uart_apdma_device_pause;
> +	mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all;
> +	mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
> +	mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
> +	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);
> +
> +	mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS;
> +	if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) {
> +		dev_info(&pdev->dev,
> +			 "Using %u as missing dma-requests property\n",
> +			 MTK_UART_APDMA_NR_VCHANS);
> +	}
> +
> +	mtkd->dma_irq = devm_kcalloc(&pdev->dev, mtkd->dma_requests,
> +				 sizeof(*mtkd->dma_irq), GFP_KERNEL);
> +	if (!mtkd->dma_irq)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < mtkd->dma_requests; i++) {
> +		c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL);
> +		if (!c) {
> +			rc = -ENODEV;
> +			goto err_no_dma;
> +		}
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> +		if (!res) {
> +			rc = -ENODEV;
> +			goto err_no_dma;
> +		}
> +
> +		c->base = devm_ioremap_resource(&pdev->dev, res);
> +		if (IS_ERR(c->base)) {
> +			rc = PTR_ERR(c->base);
> +			goto err_no_dma;
> +		}
> +		c->requested = false;
> +		c->vc.desc_free = mtk_uart_apdma_desc_free;
> +		vchan_init(&c->vc, &mtkd->ddev);
> +
> +		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);
> +			rc = -EINVAL;
> +			goto err_no_dma;
> +		}
> +	}
> +
> +	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);
> +
> +	/* Device-tree DMA controller registration */
> +	rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd);
> +	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_uart_apdma_free(mtkd);
> +	return rc;
> +}
> +
> +static int mtk_uart_apdma_remove(struct platform_device *pdev)
> +{
> +	struct mtk_uart_apdmadev *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_uart_apdma_free(mtkd);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_uart_apdma_suspend(struct device *dev)
> +{
> +	struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
> +
> +	if (!pm_runtime_suspended(dev))
> +		clk_disable_unprepare(mtkd->clk);
> +
> +	return 0;
> +}
> +
> +static int mtk_uart_apdma_resume(struct device *dev)
> +{
> +	int ret;
> +	struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
> +
> +	if (!pm_runtime_suspended(dev)) {
> +		ret = clk_prepare_enable(mtkd->clk);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +#endif /* CONFIG_PM_SLEEP */
> +
> +#ifdef CONFIG_PM
> +static int mtk_uart_apdma_runtime_suspend(struct device *dev)
> +{
> +	struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(mtkd->clk);
> +
> +	return 0;
> +}
> +
> +static int mtk_uart_apdma_runtime_resume(struct device *dev)
> +{
> +	int ret;
> +	struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
> +
> +	ret = clk_prepare_enable(mtkd->clk);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +#endif /* CONFIG_PM */
> +
> +static const struct dev_pm_ops mtk_uart_apdma_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mtk_uart_apdma_suspend, mtk_uart_apdma_resume)
> +	SET_RUNTIME_PM_OPS(mtk_uart_apdma_runtime_suspend,
> +			   mtk_uart_apdma_runtime_resume, NULL)
> +};
> +
> +static struct platform_driver mtk_uart_apdma_driver = {
> +	.probe	= mtk_uart_apdma_probe,
> +	.remove	= mtk_uart_apdma_remove,
> +	.driver = {
> +		.name		= KBUILD_MODNAME,
> +		.pm		= &mtk_uart_apdma_pm_ops,
> +		.of_match_table = of_match_ptr(mtk_uart_apdma_match),
> +	},
> +};
> +
> +module_platform_driver(mtk_uart_apdma_driver);
> +
> +MODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver");
> +MODULE_AUTHOR("Long Cheng <long.cheng@mediatek.com>");
> +MODULE_LICENSE("GPL v2");
> +

^ permalink raw reply

* Re: [PATCH] tty: Fix WARNING in tty_set_termios
From: Al Viro @ 2019-01-26  4:14 UTC (permalink / raw)
  To: Shuah Khan
  Cc: marcel, johan.hedberg, w.d.hubbs, chris, kirk, samuel.thibault,
	gregkh, robh, jslaby, sameo, davem, arnd, nishka.dasgupta_ug18,
	m.maya.nakamura, santhameena13, zhongjiang, linux-bluetooth,
	linux-kernel, speakup, devel, linux-serial, linux-wireless,
	netdev
In-Reply-To: <20190125232905.21727-1-shuah@kernel.org>

On Fri, Jan 25, 2019 at 04:29:05PM -0700, Shuah Khan wrote:
> tty_set_termios() has the following WARMN_ON which can be triggered with a
> syscall to invoke TIOCGETD __NR_ioctl.
> 
> WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
>                 tty->driver->subtype == PTY_TYPE_MASTER);
> Reference: https://syzkaller.appspot.com/bug?id=2410d22f1d8e5984217329dd0884b01d99e3e48d
> 
> A simple change would have been to print error message instead of WARN_ON.
> However, the callers assume that tty_set_termios() always returns 0 and
> don't check return value. The complete solution is fixing all the callers
> to check error and bail out to fix the WARN_ON.
> 
> This fix changes tty_set_termios() to return error and all the callers
> to check error and bail out. The reproducer is used to reproduce the
> problem and verify the fix.

> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -321,6 +321,8 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
>  		status = tty_set_termios(tty, &ktermios);
>  		BT_DBG("Disabling hardware flow control: %s",
>  		       status ? "failed" : "success");
> +		if (status)
> +			return;

Can that ldisc end up set on pty master?  And does it make any sense there?

> diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
> index fa1672993b4c..29b51370deac 100644
> --- a/drivers/tty/serdev/serdev-ttyport.c
> +++ b/drivers/tty/serdev/serdev-ttyport.c
> @@ -136,7 +136,9 @@ static int ttyport_open(struct serdev_controller *ctrl)
>  	ktermios.c_cflag |= CRTSCTS;
>  	/* Hangups are not supported so make sure to ignore carrier detect. */
>  	ktermios.c_cflag |= CLOCAL;
> -	tty_set_termios(tty, &ktermios);
> +	ret = tty_set_termios(tty, &ktermios);

How can _that_ happen to pty master?

> diff --git a/net/nfc/nci/uart.c b/net/nfc/nci/uart.c
> index 78fe622eba65..9978c21ce34d 100644
> --- a/net/nfc/nci/uart.c
> +++ b/net/nfc/nci/uart.c
> @@ -447,6 +447,7 @@ void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl)
>  	else
>  		new_termios.c_cflag &= ~CRTSCTS;
>  
> +	/* FIXME tty_set_termios() could return error */

Ditto for this one.

IOW, I don't believe that this patch makes any sense.  If anything,
we need to prevent unconditional tty_set_termios() on the path
that *does* lead to calling it for pty.

^ permalink raw reply

* [PATCH] tty: Fix WARNING in tty_set_termios
From: Shuah Khan @ 2019-01-25 23:29 UTC (permalink / raw)
  To: marcel, johan.hedberg, w.d.hubbs, chris, kirk, samuel.thibault,
	gregkh, robh, jslaby, sameo, davem, arnd, nishka.dasgupta_ug18,
	m.maya.nakamura, santhameena13, shuah, zhongjiang, viro
  Cc: linux-bluetooth, linux-kernel, speakup, devel, linux-serial,
	linux-wireless, netdev

tty_set_termios() has the following WARMN_ON which can be triggered with a
syscall to invoke TIOCGETD __NR_ioctl.

WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
                tty->driver->subtype == PTY_TYPE_MASTER);
Reference: https://syzkaller.appspot.com/bug?id=2410d22f1d8e5984217329dd0884b01d99e3e48d

A simple change would have been to print error message instead of WARN_ON.
However, the callers assume that tty_set_termios() always returns 0 and
don't check return value. The complete solution is fixing all the callers
to check error and bail out to fix the WARN_ON.

This fix changes tty_set_termios() to return error and all the callers
to check error and bail out. The reproducer is used to reproduce the
problem and verify the fix.

Reported-by: syzbot+a950165cbb86bdd023a4@syzkaller.appspotmail.com
Signed-off-by: Shuah Khan <shuah@kernel.org>
---
 drivers/bluetooth/hci_ldisc.c       |  8 ++++++--
 drivers/staging/speakup/spk_ttyio.c |  4 +++-
 drivers/tty/serdev/serdev-ttyport.c | 20 +++++++++++++++-----
 drivers/tty/tty_ioctl.c             | 14 ++++++++++----
 net/nfc/nci/uart.c                  |  1 +
 5 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index fbf7b4df23ab..643c4c75f86d 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -321,6 +321,8 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
 		status = tty_set_termios(tty, &ktermios);
 		BT_DBG("Disabling hardware flow control: %s",
 		       status ? "failed" : "success");
+		if (status)
+			return;
 
 		/* Clear RTS to prevent the device from sending */
 		/* Most UARTs need OUT2 to enable interrupts */
@@ -369,13 +371,15 @@ void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
 {
 	struct tty_struct *tty = hu->tty;
 	struct ktermios ktermios;
+	int ret;
 
 	ktermios = tty->termios;
 	ktermios.c_cflag &= ~CBAUD;
 	tty_termios_encode_baud_rate(&ktermios, speed, speed);
 
-	/* tty_set_termios() return not checked as it is always 0 */
-	tty_set_termios(tty, &ktermios);
+	ret = tty_set_termios(tty, &ktermios);
+	if (ret)
+		return;
 
 	BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name,
 	       tty->termios.c_ispeed, tty->termios.c_ospeed);
diff --git a/drivers/staging/speakup/spk_ttyio.c b/drivers/staging/speakup/spk_ttyio.c
index c92bbd05516e..ded6f8089fc8 100644
--- a/drivers/staging/speakup/spk_ttyio.c
+++ b/drivers/staging/speakup/spk_ttyio.c
@@ -165,7 +165,9 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth *synth)
 	get_termios(tty, &tmp_termios);
 	if (!(tmp_termios.c_cflag & CRTSCTS)) {
 		tmp_termios.c_cflag |= CRTSCTS;
-		tty_set_termios(tty, &tmp_termios);
+		ret = tty_set_termios(tty, &tmp_termios);
+		if (ret)
+			return ret;
 		/*
 		 * check c_cflag to see if it's updated as tty_set_termios may not return
 		 * error even when no tty bits are changed by the request.
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index fa1672993b4c..29b51370deac 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -136,7 +136,9 @@ static int ttyport_open(struct serdev_controller *ctrl)
 	ktermios.c_cflag |= CRTSCTS;
 	/* Hangups are not supported so make sure to ignore carrier detect. */
 	ktermios.c_cflag |= CLOCAL;
-	tty_set_termios(tty, &ktermios);
+	ret = tty_set_termios(tty, &ktermios);
+	if (ret)
+		return ret;
 
 	set_bit(SERPORT_ACTIVE, &serport->flags);
 
@@ -171,12 +173,14 @@ static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigne
 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
 	struct tty_struct *tty = serport->tty;
 	struct ktermios ktermios = tty->termios;
+	int retval;
 
 	ktermios.c_cflag &= ~CBAUD;
 	tty_termios_encode_baud_rate(&ktermios, speed, speed);
 
-	/* tty_set_termios() return not checked as it is always 0 */
-	tty_set_termios(tty, &ktermios);
+	retval = tty_set_termios(tty, &ktermios);
+	if (retval)
+		return retval;
 	return ktermios.c_ospeed;
 }
 
@@ -185,13 +189,16 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable
 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
 	struct tty_struct *tty = serport->tty;
 	struct ktermios ktermios = tty->termios;
+	int retval;
 
 	if (enable)
 		ktermios.c_cflag |= CRTSCTS;
 	else
 		ktermios.c_cflag &= ~CRTSCTS;
 
-	tty_set_termios(tty, &ktermios);
+	retval = tty_set_termios(tty, &ktermios);
+	if (retval)
+		return;
 }
 
 static int ttyport_set_parity(struct serdev_controller *ctrl,
@@ -200,6 +207,7 @@ static int ttyport_set_parity(struct serdev_controller *ctrl,
 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
 	struct tty_struct *tty = serport->tty;
 	struct ktermios ktermios = tty->termios;
+	int retval;
 
 	ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
 	if (parity != SERDEV_PARITY_NONE) {
@@ -208,7 +216,9 @@ static int ttyport_set_parity(struct serdev_controller *ctrl,
 			ktermios.c_cflag |= PARODD;
 	}
 
-	tty_set_termios(tty, &ktermios);
+	retval = tty_set_termios(tty, &ktermios);
+	if (retval)
+		return retval;
 
 	if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) !=
 	    (ktermios.c_cflag & (PARENB | PARODD | CMSPAR)))
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 9245fffdbceb..93e6531573ad 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -316,8 +316,9 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
 	struct ktermios old_termios;
 	struct tty_ldisc *ld;
 
-	WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-		tty->driver->subtype == PTY_TYPE_MASTER);
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+		tty->driver->subtype == PTY_TYPE_MASTER)
+		return -EINVAL;
 	/*
 	 *	Perform the actual termios internal changes under lock.
 	 */
@@ -411,7 +412,9 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
 			return -ERESTARTSYS;
 	}
 
-	tty_set_termios(tty, &tmp_termios);
+	retval = tty_set_termios(tty, &tmp_termios);
+	if (retval)
+		return retval;
 
 	/* FIXME: Arguably if tmp_termios == tty->termios AND the
 	   actual requested termios was not tmp_termios then we may
@@ -588,7 +591,10 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
 						termios.c_ospeed);
 #endif
 	up_write(&tty->termios_rwsem);
-	tty_set_termios(tty, &termios);
+	retval = tty_set_termios(tty, &termios);
+	if (retval)
+		return retval;
+
 	return 0;
 }
 #endif
diff --git a/net/nfc/nci/uart.c b/net/nfc/nci/uart.c
index 78fe622eba65..9978c21ce34d 100644
--- a/net/nfc/nci/uart.c
+++ b/net/nfc/nci/uart.c
@@ -447,6 +447,7 @@ void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl)
 	else
 		new_termios.c_cflag &= ~CRTSCTS;
 
+	/* FIXME tty_set_termios() could return error */
 	tty_set_termios(nu->tty, &new_termios);
 }
 EXPORT_SYMBOL_GPL(nci_uart_set_config);
-- 
2.17.1

^ permalink raw reply related

* Re: [GIT PULL] TTY/Serial fixes for 5.0-rc4
From: pr-tracker-bot @ 2019-01-25 23:20 UTC (permalink / raw)
  To: Greg KH
  Cc: Linus Torvalds, Jiri Slaby, Stephen Rothwell, Andrew Morton,
	linux-kernel, linux-serial
In-Reply-To: <20190125122244.GA7566@kroah.com>

The pull request you sent on Fri, 25 Jan 2019 13:22:44 +0100:

> git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tags/tty-5.0-rc4

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/473721f9c6494c04991b9a4cb787361d941d0d7d

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.wiki.kernel.org/userdoc/prtracker

^ 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