diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2014-09-29 22:06:48 +0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-11-06 06:13:34 +0300 |
commit | 77285243a68fec6852041d1db0d5587684b9dc48 (patch) | |
tree | 4359f53e39be746b0d2d5306a6a8e0734efa2a34 /drivers/tty/serial/8250/8250_omap.c | |
parent | 0e31c8d173ab105f161f26d222eae6cad2aebf5f (diff) | |
download | linux-77285243a68fec6852041d1db0d5587684b9dc48.tar.xz |
tty: serial: 8250: omap: add custom irq handling
We have (or will have) custom DMA callbacks in the omap driver due to
the different behaviour in the RX and TX case. To make this work
we need a few changes in the IRQ handler to invoke the rx_handler again
after the "manual" mode or retry the tx_handler again before falling
back to the manual mode.
Heikki didn't want to see the extra hacks in the generic / default irq
handler and Peter wasn't too happy about an OMAP-only IRQ handler. The
way I planned it is to use this extra IRQ routine only in DMA case. If
Peter dislike this approach then I hope Heikki doesn't block changes in
the default IRQ handler :)
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/8250/8250_omap.c')
-rw-r--r-- | drivers/tty/serial/8250/8250_omap.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 1659858e595a..6500547f8fda 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -13,6 +13,7 @@ #include <linux/serial_8250.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> +#include <linux/tty_flip.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/of.h> @@ -854,6 +855,60 @@ err: return ret; } +/* + * This is mostly serial8250_handle_irq(). We have a slightly different DMA + * hoook for RX/TX and need different logic for them in the ISR. Therefore we + * use the default routine in the non-DMA case and this one for with DMA. + */ +static int omap_8250_dma_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned char status; + unsigned long flags; + u8 iir; + int dma_err = 0; + + serial8250_rpm_get(up); + + iir = serial_port_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { + serial8250_rpm_put(up); + return 0; + } + + spin_lock_irqsave(&port->lock, flags); + + status = serial_port_in(port, UART_LSR); + + if (status & (UART_LSR_DR | UART_LSR_BI)) { + + dma_err = omap_8250_rx_dma(up, iir); + if (dma_err) { + status = serial8250_rx_chars(up, status); + omap_8250_rx_dma(up, 0); + } + } + serial8250_modem_status(up); + if (status & UART_LSR_THRE && up->dma->tx_err) { + if (uart_tx_stopped(&up->port) || + uart_circ_empty(&up->port.state->xmit)) { + up->dma->tx_err = 0; + serial8250_tx_chars(up); + } else { + /* + * try again due to an earlier failer which + * might have been resolved by now. + */ + dma_err = omap_8250_tx_dma(up); + if (dma_err) + serial8250_tx_chars(up); + } + } + + spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + return 1; +} #endif static int omap8250_probe(struct platform_device *pdev) |