serial: 8250: Set Altera 16550 TX FIFO Threshold
authorThor Thayer <tthayer@opensource.altera.com>
Thu, 22 Sep 2016 19:56:16 +0000 (14:56 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 27 Sep 2016 10:55:50 +0000 (12:55 +0200)
The Altera 16550 soft IP UART requires 2 additional registers for
TX FIFO threshold support. These 2 registers enable the TX FIFO
Low Watermark and set the TX FIFO Low Watermark.
Set the TX FIFO threshold to the FIFO size - tx_loadsz.

Signed-off-by: Thor Thayer <tthayer@opensource.altera.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_port.c
include/uapi/linux/serial_reg.h

index 28d29fa..1bfb6fd 100644 (file)
@@ -1870,6 +1870,30 @@ static int exar_handle_irq(struct uart_port *port)
        return ret;
 }
 
+/*
+ * Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP
+ * have a programmable TX threshold that triggers the THRE interrupt in
+ * the IIR register. In this case, the THRE interrupt indicates the FIFO
+ * has space available. Load it up with tx_loadsz bytes.
+ */
+static int serial8250_tx_threshold_handle_irq(struct uart_port *port)
+{
+       unsigned long flags;
+       unsigned int iir = serial_port_in(port, UART_IIR);
+
+       /* TX Threshold IRQ triggered so load up FIFO */
+       if ((iir & UART_IIR_ID) == UART_IIR_THRI) {
+               struct uart_8250_port *up = up_to_u8250p(port);
+
+               spin_lock_irqsave(&port->lock, flags);
+               serial8250_tx_chars(up);
+               spin_unlock_irqrestore(&port->lock, flags);
+       }
+
+       iir = serial_port_in(port, UART_IIR);
+       return serial8250_handle_irq(port, iir);
+}
+
 static unsigned int serial8250_tx_empty(struct uart_port *port)
 {
        struct uart_8250_port *up = up_to_u8250p(port);
@@ -2159,6 +2183,25 @@ int serial8250_do_startup(struct uart_port *port)
                serial_port_out(port, UART_LCR, 0);
        }
 
+       /*
+        * For the Altera 16550 variants, set TX threshold trigger level.
+        */
+       if (((port->type == PORT_ALTR_16550_F32) ||
+            (port->type == PORT_ALTR_16550_F64) ||
+            (port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) {
+               /* Bounds checking of TX threshold (valid 0 to fifosize-2) */
+               if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) {
+                       pr_err("ttyS%d TX FIFO Threshold errors, skipping\n",
+                              serial_index(port));
+               } else {
+                       serial_port_out(port, UART_ALTR_AFR,
+                                       UART_ALTR_EN_TXFIFO_LW);
+                       serial_port_out(port, UART_ALTR_TX_LOW,
+                                       port->fifosize - up->tx_loadsz);
+                       port->handle_irq = serial8250_tx_threshold_handle_irq;
+               }
+       }
+
        if (port->irq) {
                unsigned char iir1;
                /*
index 1e5ac4e..b4c0484 100644 (file)
 #define UART_EXAR_TXTRG                0x0a    /* Tx FIFO trigger level write-only */
 #define UART_EXAR_RXTRG                0x0b    /* Rx FIFO trigger level write-only */
 
+/*
+ * These are definitions for the Altera ALTR_16550_F32/F64/F128
+ * Normalized from 0x100 to 0x40 because of shift by 2 (32 bit regs).
+ */
+#define UART_ALTR_AFR          0x40    /* Additional Features Register */
+#define UART_ALTR_EN_TXFIFO_LW 0x01    /* Enable the TX FIFO Low Watermark */
+#define UART_ALTR_TX_LOW       0x41    /* Tx FIFO Low Watermark */
+
 #endif /* _LINUX_SERIAL_REG_H */