| From bb1201d4b38ec67bd9a871cf86b0cc10f28b15b5 Mon Sep 17 00:00:00 2001 |
| From: Jay Dolan <jay.dolan@accesio.com> |
| Date: Mon, 22 Nov 2021 14:06:04 +0200 |
| Subject: serial: 8250_pci: rewrite pericom_do_set_divisor() |
| |
| From: Jay Dolan <jay.dolan@accesio.com> |
| |
| commit bb1201d4b38ec67bd9a871cf86b0cc10f28b15b5 upstream. |
| |
| Have pericom_do_set_divisor() use the uartclk instead of a hard coded |
| value to work with different speed crystals. Tested with 14.7456 and 24 |
| MHz crystals. |
| |
| Have pericom_do_set_divisor() always calculate the divisor rather than |
| call serial8250_do_set_divisor() for rates below baud_base. |
| |
| Do not write registers or call serial8250_do_set_divisor() if valid |
| divisors could not be found. |
| |
| Fixes: 6bf4e42f1d19 ("serial: 8250: Add support for higher baud rates to Pericom chips") |
| Cc: stable <stable@vger.kernel.org> |
| Signed-off-by: Jay Dolan <jay.dolan@accesio.com> |
| Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| Link: https://lore.kernel.org/r/20211122120604.3909-3-andriy.shevchenko@linux.intel.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/tty/serial/8250/8250_pci.c | 30 +++++++++++++++++------------- |
| 1 file changed, 17 insertions(+), 13 deletions(-) |
| |
| --- a/drivers/tty/serial/8250/8250_pci.c |
| +++ b/drivers/tty/serial/8250/8250_pci.c |
| @@ -1349,29 +1349,33 @@ pericom_do_set_divisor(struct uart_port |
| { |
| int scr; |
| int lcr; |
| - int actual_baud; |
| - int tolerance; |
| |
| - for (scr = 5 ; scr <= 15 ; scr++) { |
| - actual_baud = 921600 * 16 / scr; |
| - tolerance = actual_baud / 50; |
| + for (scr = 16; scr > 4; scr--) { |
| + unsigned int maxrate = port->uartclk / scr; |
| + unsigned int divisor = max(maxrate / baud, 1U); |
| + int delta = maxrate / divisor - baud; |
| |
| - if ((baud < actual_baud + tolerance) && |
| - (baud > actual_baud - tolerance)) { |
| + if (baud > maxrate + baud / 50) |
| + continue; |
| |
| + if (delta > baud / 50) |
| + divisor++; |
| + |
| + if (divisor > 0xffff) |
| + continue; |
| + |
| + /* Update delta due to possible divisor change */ |
| + delta = maxrate / divisor - baud; |
| + if (abs(delta) < baud / 50) { |
| lcr = serial_port_in(port, UART_LCR); |
| serial_port_out(port, UART_LCR, lcr | 0x80); |
| - |
| - serial_port_out(port, UART_DLL, 1); |
| - serial_port_out(port, UART_DLM, 0); |
| + serial_port_out(port, UART_DLL, divisor & 0xff); |
| + serial_port_out(port, UART_DLM, divisor >> 8 & 0xff); |
| serial_port_out(port, 2, 16 - scr); |
| serial_port_out(port, UART_LCR, lcr); |
| return; |
| - } else if (baud > actual_baud) { |
| - break; |
| } |
| } |
| - serial8250_do_set_divisor(port, baud, quot, quot_frac); |
| } |
| static int pci_pericom_setup(struct serial_private *priv, |
| const struct pciserial_board *board, |