serial: 8250_pci: rewrite pericom_do_set_divisor()
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: 6bf4e42f1d
("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>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
50b06889c8
commit
f9802d7049
@@ -1349,29 +1349,33 @@ pericom_do_set_divisor(struct uart_port *port, unsigned int baud,
|
|||||||
{
|
{
|
||||||
int scr;
|
int scr;
|
||||||
int lcr;
|
int lcr;
|
||||||
int actual_baud;
|
|
||||||
int tolerance;
|
|
||||||
|
|
||||||
for (scr = 5 ; scr <= 15 ; scr++) {
|
for (scr = 16; scr > 4; scr--) {
|
||||||
actual_baud = 921600 * 16 / scr;
|
unsigned int maxrate = port->uartclk / scr;
|
||||||
tolerance = actual_baud / 50;
|
unsigned int divisor = max(maxrate / baud, 1U);
|
||||||
|
int delta = maxrate / divisor - baud;
|
||||||
|
|
||||||
if ((baud < actual_baud + tolerance) &&
|
if (baud > maxrate + baud / 50)
|
||||||
(baud > actual_baud - tolerance)) {
|
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);
|
lcr = serial_port_in(port, UART_LCR);
|
||||||
serial_port_out(port, UART_LCR, lcr | 0x80);
|
serial_port_out(port, UART_LCR, lcr | 0x80);
|
||||||
|
serial_port_out(port, UART_DLL, divisor & 0xff);
|
||||||
serial_port_out(port, UART_DLL, 1);
|
serial_port_out(port, UART_DLM, divisor >> 8 & 0xff);
|
||||||
serial_port_out(port, UART_DLM, 0);
|
|
||||||
serial_port_out(port, 2, 16 - scr);
|
serial_port_out(port, 2, 16 - scr);
|
||||||
serial_port_out(port, UART_LCR, lcr);
|
serial_port_out(port, UART_LCR, lcr);
|
||||||
return;
|
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,
|
static int pci_pericom_setup(struct serial_private *priv,
|
||||||
const struct pciserial_board *board,
|
const struct pciserial_board *board,
|
||||||
|
Reference in New Issue
Block a user