serial: omap: improve RS-485 performance
authorPhilippe Proulx <philippe.proulx@savoirfairelinux.com>
Wed, 23 Oct 2013 22:49:58 +0000 (18:49 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 29 Oct 2013 23:34:11 +0000 (16:34 -0700)
If RS-485 is enabled, make the OMAP UART fire THR interrupts when both
TX FIFO and TX shift register are empty instead of polling the
equivalent status bit. This removes the burst of interrupt requests
seen at every end of transmission.

Also: the comment said that the TX FIFO trigger level was set at 16
characters when it's 32 in reality.

Signed-off-by: Philippe Proulx <philippe.proulx@savoirfairelinux.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/omap-serial.c

index b69393f2817d4c2ef75d4ef9dd1426ff7f32bd5b..e42eb1e5a21a97883eae4fa0f8e4d76e5e12b50a 100644 (file)
@@ -298,21 +298,22 @@ static void serial_omap_enable_ms(struct uart_port *port)
 static void serial_omap_stop_tx(struct uart_port *port)
 {
        struct uart_omap_port *up = to_uart_omap_port(port);
-       struct circ_buf *xmit = &up->port.state->xmit;
        int res;
 
        pm_runtime_get_sync(up->dev);
 
-       /* handle rs485 */
+       /* Handle RS-485 */
        if (up->rs485.flags & SER_RS485_ENABLED) {
-               /* do nothing if current tx not yet completed */
-               res = serial_in(up, UART_LSR) & UART_LSR_TEMT;
-               if (!res)
-                       return;
-
-               /* if there's no more data to send, turn off rts */
-               if (uart_circ_empty(xmit)) {
-                       /* if rts not already disabled */
+               if (up->scr & OMAP_UART_SCR_TX_EMPTY) {
+                       /* THR interrupt is fired when both TX FIFO and TX
+                        * shift register are empty. This means there's nothing
+                        * left to transmit now, so make sure the THR interrupt
+                        * is fired when TX FIFO is below the trigger level,
+                        * disable THR interrupts and toggle the RS-485 GPIO
+                        * data direction pin if needed.
+                        */
+                       up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+                       serial_out(up, UART_OMAP_SCR, up->scr);
                        res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
                        if (gpio_get_value(up->rts_gpio) != res) {
                                if (up->rs485.delay_rts_after_send > 0) {
@@ -320,6 +321,18 @@ static void serial_omap_stop_tx(struct uart_port *port)
                                }
                                gpio_set_value(up->rts_gpio, res);
                        }
+               } else {
+                       /* We're asked to stop, but there's still stuff in the
+                        * UART FIFO, so make sure the THR interrupt is fired
+                        * when both TX FIFO and TX shift register are empty.
+                        * The next THR interrupt (if no transmission is started
+                        * in the meantime) will indicate the end of a
+                        * transmission. Therefore we _don't_ disable THR
+                        * interrupts in this situation.
+                        */
+                       up->scr |= OMAP_UART_SCR_TX_EMPTY;
+                       serial_out(up, UART_OMAP_SCR, up->scr);
+                       return;
                }
        }
 
@@ -399,8 +412,12 @@ static void serial_omap_start_tx(struct uart_port *port)
 
        pm_runtime_get_sync(up->dev);
 
-       /* handle rs485 */
+       /* Handle RS-485 */
        if (up->rs485.flags & SER_RS485_ENABLED) {
+               /* Fire THR interrupts when FIFO is below trigger level */
+               up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+               serial_out(up, UART_OMAP_SCR, up->scr);
+
                /* if rts not already enabled */
                res = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0;
                if (gpio_get_value(up->rts_gpio) != res) {
@@ -969,7 +986,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
         */
 
        /* Set receive FIFO threshold to 16 characters and
-        * transmit FIFO threshold to 16 spaces
+        * transmit FIFO threshold to 32 spaces
         */
        up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
        up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK;
@@ -1375,6 +1392,15 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
        up->ier = mode;
        serial_out(up, UART_IER, up->ier);
 
+       /* If RS-485 is disabled, make sure the THR interrupt is fired when
+        * TX FIFO is below the trigger level.
+        */
+       if (!(up->rs485.flags & SER_RS485_ENABLED) &&
+           (up->scr & OMAP_UART_SCR_TX_EMPTY)) {
+               up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+               serial_out(up, UART_OMAP_SCR, up->scr);
+       }
+
        spin_unlock_irqrestore(&up->port.lock, flags);
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);