tty: serial: 8250: omap: add custom irq handling
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Mon, 29 Sep 2014 18:06:48 +0000 (20:06 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 6 Nov 2014 03:13:34 +0000 (19:13 -0800)
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>
drivers/tty/serial/8250/8250.h
drivers/tty/serial/8250/8250_core.c
drivers/tty/serial/8250/8250_omap.c

index 0dc1cb6..458ad28 100644 (file)
@@ -119,6 +119,8 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)
 }
 
 struct uart_8250_port *serial8250_get_port(int line);
+void serial8250_rpm_get(struct uart_8250_port *p);
+void serial8250_rpm_put(struct uart_8250_port *p);
 
 #if defined(__alpha__) && !defined(CONFIG_PCI)
 /*
index 39450ba..7e78f30 100644 (file)
@@ -541,20 +541,22 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p)
 }
 EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos);
 
-static void serial8250_rpm_get(struct uart_8250_port *p)
+void serial8250_rpm_get(struct uart_8250_port *p)
 {
        if (!(p->capabilities & UART_CAP_RPM))
                return;
        pm_runtime_get_sync(p->port.dev);
 }
+EXPORT_SYMBOL_GPL(serial8250_rpm_get);
 
-static void serial8250_rpm_put(struct uart_8250_port *p)
+void serial8250_rpm_put(struct uart_8250_port *p)
 {
        if (!(p->capabilities & UART_CAP_RPM))
                return;
        pm_runtime_mark_last_busy(p->port.dev);
        pm_runtime_put_autosuspend(p->port.dev);
 }
+EXPORT_SYMBOL_GPL(serial8250_rpm_put);
 
 /*
  * These two wrappers ensure that enable_runtime_pm_tx() can be called more than
index 1659858..6500547 100644 (file)
@@ -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)