serial: 8250: Tolerate clock variance for max baud rate
authorJames Hogan <james.hogan@imgtec.com>
Fri, 25 Sep 2015 19:36:10 +0000 (15:36 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 4 Oct 2015 18:12:25 +0000 (19:12 +0100)
When the UART clock is set slightly under 1.8432MHz, the 8250 driver
core doesn't permit the 115200 baud rate since it calculates the maximum
frequency to pass to uart_get_baud_rate by simply dividing the uart
clock by 16 which yields a value slightly under 115200, even though the
frequency is close enough for the UART to operate reliably.

Therefore add some tolerance in the calculation of the maximum baud
rate. 1% tolerance allows for marginally slower uart clk than nominal
without introducing transmission errors.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
[pjh: Forward-port & refactor original patch; change tolerance to 1%]
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_port.c

index 554a63e4c4a00cecc76775eac9ab6f89cdeefb6e..e54de0037df43330b1ace152372ed52a2d94179f 100644 (file)
@@ -2225,6 +2225,23 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
                serial_port_out(port, 0x2, quot_frac);
 }
 
+static unsigned int
+serial8250_get_baud_rate(struct uart_port *port, struct ktermios *termios,
+                        struct ktermios *old)
+{
+       unsigned int tolerance = port->uartclk / 100;
+
+       /*
+        * Ask the core to calculate the divisor for us.
+        * Allow 1% tolerance at the upper limit so uart clks marginally
+        * slower than nominal still match standard baud rates without
+        * causing transmission errors.
+        */
+       return uart_get_baud_rate(port, termios, old,
+                                 port->uartclk / 16 / 0xffff,
+                                 (port->uartclk + tolerance) / 16);
+}
+
 void
 serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
                          struct ktermios *old)
@@ -2236,12 +2253,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 
        cval = serial8250_compute_lcr(up, termios->c_cflag);
 
-       /*
-        * Ask the core to calculate the divisor for us.
-        */
-       baud = uart_get_baud_rate(port, termios, old,
-                                 port->uartclk / 16 / 0xffff,
-                                 port->uartclk / 16);
+       baud = serial8250_get_baud_rate(port, termios, old);
        quot = serial8250_get_divisor(up, baud, &frac);
 
        /*
@@ -2834,9 +2846,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
                if (port->state->port.tty && termios.c_cflag == 0)
                        termios.c_cflag = port->state->port.tty->termios.c_cflag;
 
-               baud = uart_get_baud_rate(port, &termios, NULL,
-                                         port->uartclk / 16 / 0xffff,
-                                         port->uartclk / 16);
+               baud = serial8250_get_baud_rate(port, &termios, NULL);
                quot = serial8250_get_divisor(up, baud, &frac);
 
                serial8250_set_divisor(port, baud, quot, frac);