arm pl011 serial: support multi-irq request
authorBing Fan <tombinfan@tencent.com>
Thu, 1 Jul 2021 01:38:32 +0000 (09:38 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 21 Jul 2021 10:39:47 +0000 (12:39 +0200)
In order to make pl011 work better, multiple interrupts are
required, such as TXIM, RXIM, RTIM, error interrupt(FE/PE/BE/OE);
at the same time, pl011 to GIC does not merge the interrupt
lines(each serial-interrupt corresponding to different GIC hardware
interrupt), so need to enable and request multiple gic interrupt
numbers in the driver.

Signed-off-by: Bing Fan <tombinfan@tencent.com>
Link: https://lore.kernel.org/r/1625103512-30182-1-git-send-email-hptsfb@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/amba-pl011.c

index d361cd8..cf6ff22 100644 (file)
@@ -1777,11 +1777,39 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
        }
 }
 
+static void pl011_release_irq(struct uart_amba_port *uap, unsigned int max_cnt)
+{
+       struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
+       int i;
+
+       for (i = 0; i < max_cnt; i++)
+               if (amba_dev->irq[i])
+                       free_irq(amba_dev->irq[i], uap);
+}
+
 static int pl011_allocate_irq(struct uart_amba_port *uap)
 {
+       int ret = 0;
+       int i;
+       unsigned int virq;
+       struct amba_device *amba_dev = container_of(uap->port.dev, struct amba_device, dev);
+
        pl011_write(uap->im, uap, REG_IMSC);
 
-       return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap);
+       for (i = 0; i < AMBA_NR_IRQS; i++) {
+               virq = amba_dev->irq[i];
+               if (virq == 0)
+                       break;
+
+               ret = request_irq(virq, pl011_int, IRQF_SHARED, dev_name(&amba_dev->dev), uap);
+               if (ret) {
+                       dev_err(uap->port.dev, "request %u interrupt failed\n", virq);
+                       pl011_release_irq(uap, i - 1);
+                       break;
+               }
+       }
+
+       return ret;
 }
 
 /*
@@ -1953,7 +1981,7 @@ static void pl011_shutdown(struct uart_port *port)
        if ((port->rs485.flags & SER_RS485_ENABLED) && uap->rs485_tx_started)
                pl011_rs485_tx_stop(uap);
 
-       free_irq(uap->port.irq, uap);
+       pl011_release_irq(uap, AMBA_NR_IRQS);
 
        pl011_disable_uart(uap);
 
@@ -1983,7 +2011,7 @@ static void sbsa_uart_shutdown(struct uart_port *port)
 
        pl011_disable_interrupts(uap);
 
-       free_irq(uap->port.irq, uap);
+       pl011_release_irq(uap, AMBA_NR_IRQS);
 
        if (uap->port.ops->flush_buffer)
                uap->port.ops->flush_buffer(port);