[PATCH 4.14 027/237] serial: imx: Only handle irqs that are actually enabled

Greg Kroah-Hartman
2020-02-27T13:53:17Z

From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

commit 437768962f754d9501e5ba4d98b1f2a89dc62028 upstream.

Handling an irq that isn't enabled can have some undesired side effects.
Some of these are mentioned in the newly introduced code comment. Some
of the irq sources already had their handling right, some don't. Handle
them all in the same consistent way.

The change for USR1_RRDY and USR1_AGTIM drops the check for
dma_is_enabled. This is correct as UCR1_RRDYEN and UCR2_ATEN are always
off if dma is enabled.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Reviewed-by: Shawn Guo <shawnguo@kernel.org>
[Backport to v4.14]
Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/tty/serial/imx.c | 53 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 39 insertions(+), 14 deletions(-)

--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -843,14 +843,42 @@ static void imx_mctrl_check(struct imx_p
static irqreturn_t imx_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned int sts;
- unsigned int sts2;
+ unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4;
irqreturn_t ret = IRQ_NONE;

- sts = readl(sport->port.membase + USR1);
- sts2 = readl(sport->port.membase + USR2);
+ usr1 = readl(sport->port.membase + USR1);
+ usr2 = readl(sport->port.membase + USR2);
+ ucr1 = readl(sport->port.membase + UCR1);
+ ucr2 = readl(sport->port.membase + UCR2);
+ ucr3 = readl(sport->port.membase + UCR3);
+ ucr4 = readl(sport->port.membase + UCR4);
+
+ /*
+ * Even if a condition is true that can trigger an irq only handle it if
+ * the respective irq source is enabled. This prevents some undesired
+ * actions, for example if a character that sits in the RX FIFO and that
+ * should be fetched via DMA is tried to be fetched using PIO. Or the
+ * receiver is currently off and so reading from URXD0 results in an
+ * exception. So just mask the (raw) status bits for disabled irqs.
+ */
+ if ((ucr1 & UCR1_RRDYEN) == 0)
+ usr1 &= ~USR1_RRDY;
+ if ((ucr2 & UCR2_ATEN) == 0)
+ usr1 &= ~USR1_AGTIM;
+ if ((ucr1 & UCR1_TXMPTYEN) == 0)
+ usr1 &= ~USR1_TRDY;
+ if ((ucr4 & UCR4_TCEN) == 0)
+ usr2 &= ~USR2_TXDC;
+ if ((ucr3 & UCR3_DTRDEN) == 0)
+ usr1 &= ~USR1_DTRD;
+ if ((ucr1 & UCR1_RTSDEN) == 0)
+ usr1 &= ~USR1_RTSD;
+ if ((ucr3 & UCR3_AWAKEN) == 0)
+ usr1 &= ~USR1_AWAKE;
+ if ((ucr4 & UCR4_OREN) == 0)
+ usr2 &= ~USR2_ORE;

- if (sts & (USR1_RRDY | USR1_AGTIM)) {
+ if (usr1 & (USR1_RRDY | USR1_AGTIM)) {
if (sport->dma_is_enabled)
imx_dma_rxint(sport);
else
@@ -858,18 +886,15 @@ static irqreturn_t imx_int(int irq, void
ret = IRQ_HANDLED;
}

- if ((sts & USR1_TRDY &&
- readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
- (sts2 & USR2_TXDC &&
- readl(sport->port.membase + UCR4) & UCR4_TCEN)) {
+ if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) {
imx_txint(irq, dev_id);
ret = IRQ_HANDLED;
}

- if (sts & USR1_DTRD) {
+ if (usr1 & USR1_DTRD) {
unsigned long flags;

- if (sts & USR1_DTRD)
+ if (usr1 & USR1_DTRD)
writel(USR1_DTRD, sport->port.membase + USR1);

spin_lock_irqsave(&sport->port.lock, flags);
@@ -879,17 +904,17 @@ static irqreturn_t imx_int(int irq, void
ret = IRQ_HANDLED;
}

- if (sts & USR1_RTSD) {
+ if (usr1 & USR1_RTSD) {
imx_rtsint(irq, dev_id);
ret = IRQ_HANDLED;
}

- if (sts & USR1_AWAKE) {
+ if (usr1 & USR1_AWAKE) {
writel(USR1_AWAKE, sport->port.membase + USR1);
ret = IRQ_HANDLED;
}

- if (sts2 & USR2_ORE) {
+ if (usr2 & USR2_ORE) {
sport->port.icount.overrun++;
writel(USR2_ORE, sport->port.membase + USR2);
ret = IRQ_HANDLED;