summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>2017-01-11 16:31:35 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-19 14:20:23 +0100
commit4831e0d9054c62c0bd134315de34e7701804707a (patch)
tree5f8c9025fe8c8ca0bf7ceb0860523e78251c34bf /drivers/tty
parent6a171b29937984a5e0bf29d6577b055998f03edb (diff)
downloadlinux-rpi-4831e0d9054c62c0bd134315de34e7701804707a.tar.gz
linux-rpi-4831e0d9054c62c0bd134315de34e7701804707a.tar.bz2
linux-rpi-4831e0d9054c62c0bd134315de34e7701804707a.zip
serial: 8250_mid: handle interrupt correctly in DMA case
Starting from Tangier B0 and continuing on Anniedale the HSU DMA interrupt line is actually shared with UART. Handling them independently is racy and quite often comes with the following traceback. irq 54: nobody cared (try booting with the "irqpoll" option) CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.9.0-rc6-edison64-86244934+ #1 Hardware name: Intel Corporation Merrifield/BODEGA BAY, BIOS 542 2015.01.21:18.19.48 ffff88003f203eb0 ffffffff8130e718 ffff880032627000 ffff88003262709c ffff88003f203ed8 ffffffff810a3960 ffff880032627000 0000000000000000 ffff880032627000 ffff88003f203f10 ffffffff810a3cc7 ffff880032627000 Call Trace: <IRQ> [<ffffffff8130e718>] dump_stack+0x4d/0x65 [<ffffffff810a3960>] __report_bad_irq+0x30/0xc0 [<ffffffff810a3cc7>] note_interrupt+0x227/0x270 [<ffffffff810a1380>] handle_irq_event_percpu+0x40/0x50 [<ffffffff810a13b7>] handle_irq_event+0x27/0x50 [<ffffffff810a42d5>] handle_fasteoi_irq+0x85/0x150 [<ffffffff8101d7fe>] handle_irq+0x6e/0x120 [<ffffffff8105b8bc>] ? _local_bh_enable+0x1c/0x50 [<ffffffff8101d0d6>] do_IRQ+0x46/0xd0 [<ffffffff818cef3f>] common_interrupt+0x7f/0x7f <EOI> [<ffffffff818cdead>] ? mwait_idle+0x7d/0x140 [<ffffffff81024c9a>] arch_cpu_idle+0xa/0x10 [<ffffffff818ce150>] default_idle_call+0x20/0x30 [<ffffffff810908fd>] cpu_startup_entry+0x16d/0x1d0 [<ffffffff818c882d>] rest_init+0x6d/0x70 [<ffffffff81f93e8f>] start_kernel+0x3e2/0x3ef [<ffffffff81f9343d>] x86_64_start_reservations+0x38/0x3a [<ffffffff81f93529>] x86_64_start_kernel+0xea/0xed handlers: [<ffffffff81411670>] serial8250_interrupt Disabling IRQ #54 Fix this by handling interrupt only in one place. The issue is discussed here: https://github.com/andy-shev/linux/issues/5 Moreover this also fixes another bug when Rx DMA returns wrong residue and we can't rely on it. Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Acked-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/8250/8250_mid.c33
1 files changed, 33 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
index ac013edf4992..6730d9eef81e 100644
--- a/drivers/tty/serial/8250/8250_mid.c
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -75,6 +75,37 @@ static int pnw_setup(struct mid8250 *mid, struct uart_port *p)
return 0;
}
+static int tng_handle_irq(struct uart_port *p)
+{
+ struct mid8250 *mid = p->private_data;
+ struct uart_8250_port *up = up_to_u8250p(p);
+ struct hsu_dma_chip *chip;
+ u32 status;
+ int ret = 0;
+ int err;
+
+ chip = pci_get_drvdata(mid->dma_dev);
+
+ /* Rx DMA */
+ err = hsu_dma_get_status(chip, mid->dma_index * 2 + 1, &status);
+ if (err > 0) {
+ serial8250_rx_dma_flush(up);
+ ret |= 1;
+ } else if (err == 0)
+ ret |= hsu_dma_do_irq(chip, mid->dma_index * 2 + 1, status);
+
+ /* Tx DMA */
+ err = hsu_dma_get_status(chip, mid->dma_index * 2, &status);
+ if (err > 0)
+ ret |= 1;
+ else if (err == 0)
+ ret |= hsu_dma_do_irq(chip, mid->dma_index * 2, status);
+
+ /* UART */
+ ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR));
+ return IRQ_RETVAL(ret);
+}
+
static int tng_setup(struct mid8250 *mid, struct uart_port *p)
{
struct pci_dev *pdev = to_pci_dev(p->dev);
@@ -90,6 +121,8 @@ static int tng_setup(struct mid8250 *mid, struct uart_port *p)
mid->dma_index = index;
mid->dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0));
+
+ p->handle_irq = tng_handle_irq;
return 0;
}