serial: 8250: Fix reporting real baudrate value in c_ospeed field

commit 32262e2e42 upstream.

In most cases it is not possible to set exact baudrate value to hardware.

So fix reporting real baudrate value which was set to hardware via c_ospeed
termios field. It can be retrieved by ioctl(TCGETS2) from userspace.

Real baudrate value is calculated from chosen hardware divisor and base
clock. It is implemented in a new function serial8250_compute_baud_rate()
which is inverse of serial8250_get_divisor() function.

With this change is fixed also UART timeout value (it is updated via
uart_update_timeout() function), which is calculated from the now fixed
baudrate value too.

Cc: stable@vger.kernel.org
Signed-off-by: Pali Rohár <pali@kernel.org>
Link: https://lore.kernel.org/r/20210927093704.19768-1-pali@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Pali Rohár
2021-09-27 11:37:04 +02:00
committed by Greg Kroah-Hartman
parent 670f1f30ed
commit 02981a9664

View File

@@ -2584,6 +2584,19 @@ static unsigned int serial8250_get_divisor(struct uart_port *port,
return serial8250_do_get_divisor(port, baud, frac);
}
static unsigned int serial8250_compute_baud_rate(struct uart_port *port,
unsigned int quot)
{
if ((port->flags & UPF_MAGIC_MULTIPLIER) && quot == 0x8001)
return port->uartclk / 4;
else if ((port->flags & UPF_MAGIC_MULTIPLIER) && quot == 0x8002)
return port->uartclk / 8;
else if (port->type == PORT_NPCM)
return DIV_ROUND_CLOSEST(port->uartclk - 2 * (quot + 2), 16 * (quot + 2));
else
return DIV_ROUND_CLOSEST(port->uartclk, 16 * quot);
}
static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
tcflag_t c_cflag)
{
@@ -2725,11 +2738,14 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk)
baud = serial8250_get_baud_rate(port, termios, NULL);
quot = serial8250_get_divisor(port, baud, &frac);
baud = serial8250_compute_baud_rate(port, quot);
serial8250_rpm_get(up);
spin_lock_irqsave(&port->lock, flags);
uart_update_timeout(port, termios->c_cflag, baud);
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
serial8250_set_divisor(port, baud, quot, frac);
serial_port_out(port, UART_LCR, up->lcr);
@@ -2763,6 +2779,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
baud = serial8250_get_baud_rate(port, termios, old);
quot = serial8250_get_divisor(port, baud, &frac);
baud = serial8250_compute_baud_rate(port, quot);
/*
* Ok, we're now changing the port state. Do it with