tty: xuartps: Implement BREAK detection, add SYSRQ support
authorVlad Lungu <vlad.lungu@windriver.com>
Thu, 17 Oct 2013 21:08:06 +0000 (14:08 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 20 Oct 2013 02:47:37 +0000 (19:47 -0700)
The Cadence UART does not do break detection, even if the
datasheet says it does. This patch adds break detection in
software (tested in 8N1 mode only) and enables SYSRQ,
allowing for Break-g to enter KDB and all the other goodies.

Signed-off-by: Vlad Lungu <vlad.lungu@windriver.com>
Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/xilinx_uartps.c

index 8c074595160561bfbd1b875aeeb11e6cb88346e6..5d5557af4d22212f1e2f72ac9ace912a2d9eacd0 100644 (file)
  *
  */
 
+#if defined(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
 #include <linux/platform_device.h>
 #include <linux/serial.h>
+#include <linux/console.h>
 #include <linux/serial_core.h>
 #include <linux/slab.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
-#include <linux/console.h>
 #include <linux/clk.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 #define XUARTPS_IXR_RXEMPTY    0x00000002 /* RX FIFO empty interrupt. */
 #define XUARTPS_IXR_MASK       0x00001FFF /* Valid bit mask */
 
+/* Goes in read_status_mask for break detection as the HW doesn't do it*/
+#define XUARTPS_IXR_BRK                0x80000000
+
 /** Channel Status Register
  *
  * The channel status register (CSR) is provided to enable the control logic
@@ -171,6 +178,23 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
         */
        isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET);
 
+       /*
+        * There is no hardware break detection, so we interpret framing
+        * error with all-zeros data as a break sequence. Most of the time,
+        * there's another non-zero byte at the end of the sequence.
+        */
+
+       if (isrstatus & XUARTPS_IXR_FRAMING) {
+               while (!(xuartps_readl(XUARTPS_SR_OFFSET) &
+                                       XUARTPS_SR_RXEMPTY)) {
+                       if (!xuartps_readl(XUARTPS_FIFO_OFFSET)) {
+                               port->read_status_mask |= XUARTPS_IXR_BRK;
+                               isrstatus &= ~XUARTPS_IXR_FRAMING;
+                       }
+               }
+               xuartps_writel(XUARTPS_IXR_FRAMING, XUARTPS_ISR_OFFSET);
+       }
+
        /* drop byte with parity error if IGNPAR specified */
        if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY)
                isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT);
@@ -184,6 +208,30 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
                while ((xuartps_readl(XUARTPS_SR_OFFSET) &
                        XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) {
                        data = xuartps_readl(XUARTPS_FIFO_OFFSET);
+
+                       /* Non-NULL byte after BREAK is garbage (99%) */
+                       if (data && (port->read_status_mask &
+                                               XUARTPS_IXR_BRK)) {
+                               port->read_status_mask &= ~XUARTPS_IXR_BRK;
+                               port->icount.brk++;
+                               if (uart_handle_break(port))
+                                       continue;
+                       }
+
+                       /*
+                        * uart_handle_sysrq_char() doesn't work if
+                        * spinlocked, for some reason
+                        */
+                        if (port->sysrq) {
+                               spin_unlock(&port->lock);
+                               if (uart_handle_sysrq_char(port,
+                                                       (unsigned char)data)) {
+                                       spin_lock(&port->lock);
+                                       continue;
+                               }
+                               spin_lock(&port->lock);
+                       }
+
                        port->icount.rx++;
 
                        if (isrstatus & XUARTPS_IXR_PARITY) {