1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- From patchwork Fri Jul 13 11:32:42 2018
- Content-Type: text/plain; charset="utf-8"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Subject: serial8250 on tegra hsuart: recover from spurious interrupts due to
- tegra2 silicon bug
- X-Patchwork-Submitter: "David R. Piegdon" <[email protected]>
- X-Patchwork-Id: 943440
- Message-Id: <[email protected]>
- To: [email protected]
- Date: Fri, 13 Jul 2018 11:32:42 +0000
- From: "David R. Piegdon" <[email protected]>
- List-Id: <linux-tegra.vger.kernel.org>
- Hi,
- a while back I sent a few mails regarding spurious interrupts in the
- UARTA (hsuart) block of the Tegra2 SoC, when using the 8250 driver for
- it instead of the hsuart driver. After going down a pretty deep
- debugging/testing hole, I think I found a patch that fixes the issue. So
- far testing in a reboot-cycle suggests that the error frequency dropped
- from >3% of all reboots to at least <0.05% of all reboots. Tests
- continue to run over the weekend.
- The patch below already is a second iteration; the first did not reset
- the MCR or contain the lines below '// clear interrupts'. This resulted
- in no more spurious interrupts, but in a few % of spurious interrupts
- that were recovered the UART block did not receive any characters any
- more. So further resetting was required to fully reacquire operational
- state of the UART block.
- I'd love any comments/suggestions on this!
- Cheers,
- David
- --- a/drivers/tty/serial/8250/8250_core.c
- +++ b/drivers/tty/serial/8250/8250_core.c
- @@ -136,6 +136,38 @@ static irqreturn_t serial8250_interrupt(
- "serial8250: too much work for irq%d\n", irq);
- break;
- }
- +
- +#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- + if (!handled && (port->type == PORT_TEGRA)) {
- + /*
- + * Fix Tegra 2 CPU silicon bug where sometimes
- + * "TX holding register empty" interrupts result in a
- + * bad (metastable?) state in Tegras HSUART IP core.
- + * Only way to recover seems to be to reset all
- + * interrupts as well as the TX queue and the MCR.
- + * But we don't want to loose any outgoing characters,
- + * so only do it if the RX and TX queues are empty.
- + */
- + unsigned char lsr = port->serial_in(port, UART_LSR);
- + const unsigned char fifo_empty_mask =
- + (UART_LSR_TEMT | UART_LSR_THRE);
- + if (((lsr & (UART_LSR_DR | fifo_empty_mask)) ==
- + fifo_empty_mask)) {
- + port->serial_out(port, UART_IER, 0);
- + port->serial_out(port, UART_MCR, 0);
- + serial8250_clear_and_reinit_fifos(up);
- + port->serial_out(port, UART_MCR, up->mcr);
- + port->serial_out(port, UART_IER, up->ier);
- + // clear interrupts
- + serial_port_in(port, UART_LSR);
- + serial_port_in(port, UART_RX);
- + serial_port_in(port, UART_IIR);
- + serial_port_in(port, UART_MSR);
- + up->lsr_saved_flags = 0;
- + up->msr_saved_flags = 0;
- + }
- + }
- +#endif
- } while (l != end);
-
- spin_unlock(&i->lock);
|