From: Pali Rohár <pali@xxxxxxxxxx> commit 4f532c1e25319e42996ec18a1f473fd50c8e575d upstream. Functions tty_termios_encode_baud_rate() and uart_update_timeout() should be called with the baudrate value which was set to hardware. Linux then report exact values via ioctl(TCGETS2) to userspace. Change mvebu_uart_baud_rate_set() function to return baudrate value which was set to hardware and propagate this value to above mentioned functions. With this change userspace would see precise value in termios c_ospeed field. Fixes: 68a0db1d7da2 ("serial: mvebu-uart: add function to change baudrate") Cc: stable <stable@xxxxxxxxxx> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx> Signed-off-by: Pali Rohár <pali@xxxxxxxxxx> Link: https://lore.kernel.org/r/20220628100922.10717-1-pali@xxxxxxxxxx Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- drivers/tty/serial/mvebu-uart.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -442,14 +442,14 @@ static void mvebu_uart_shutdown(struct u } } -static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) +static unsigned int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) { struct mvebu_uart *mvuart = to_mvuart(port); unsigned int d_divisor, m_divisor; u32 brdv; if (IS_ERR(mvuart->clk)) - return -PTR_ERR(mvuart->clk); + return 0; /* * The baudrate is derived from the UART clock thanks to two divisors: @@ -469,7 +469,7 @@ static int mvebu_uart_baud_rate_set(stru brdv |= d_divisor; writel(brdv, port->membase + UART_BRDV); - return 0; + return DIV_ROUND_CLOSEST(port->uartclk, d_divisor * m_divisor); } static void mvebu_uart_set_termios(struct uart_port *port, @@ -506,15 +506,11 @@ static void mvebu_uart_set_termios(struc max_baud = 230400; baud = uart_get_baud_rate(port, termios, old, min_baud, max_baud); - if (mvebu_uart_baud_rate_set(port, baud)) { - /* No clock available, baudrate cannot be changed */ - if (old) - baud = uart_get_baud_rate(port, old, NULL, - min_baud, max_baud); - } else { - tty_termios_encode_baud_rate(termios, baud, baud); - uart_update_timeout(port, termios->c_cflag, baud); - } + baud = mvebu_uart_baud_rate_set(port, baud); + + /* In case baudrate cannot be changed, report previous old value */ + if (baud == 0 && old) + baud = tty_termios_baud_rate(old); /* Only the following flag changes are supported */ if (old) { @@ -525,6 +521,11 @@ static void mvebu_uart_set_termios(struc termios->c_cflag |= CS8; } + if (baud != 0) { + tty_termios_encode_baud_rate(termios, baud, baud); + uart_update_timeout(port, termios->c_cflag, baud); + } + spin_unlock_irqrestore(&port->lock, flags); }