From 32262e2e429cdb31f9e957e997d53458762931b7 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 27 Sep 2021 11:37:04 +0200 Subject: serial: 8250: Fix reporting real baudrate value in c_ospeed field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Link: https://lore.kernel.org/r/20210927093704.19768-1-pali@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/tty/serial/8250/8250_port.c') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 66374704747e..dc6900b2daa8 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -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) { @@ -2714,11 +2727,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); @@ -2750,6 +2766,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 -- cgit From d02b006b29de14968ba4afa998bede0d55469e29 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Oct 2021 15:31:46 +0200 Subject: Revert "serial: 8250: Fix reporting real baudrate value in c_ospeed field" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 32262e2e429cdb31f9e957e997d53458762931b7. The commit in question claims to determine the inverse of serial8250_get_divisor() but failed to notice that some drivers override the default implementation using a get_divisor() callback. This means that the computed line-speed values can be completely wrong and results in regular TCSETS requests failing (the incorrect values would also be passed to any overridden set_divisor() callback). Similarly, it also failed to honour the old (deprecated) ASYNC_SPD_FLAGS and would break applications relying on those when re-encoding the actual line speed. There are also at least two quirks, UART_BUG_QUOT and an OMAP1510 workaround, which were happily ignored and that are now broken. Finally, even if the offending commit were to be implemented correctly, this is a new feature and not something which should be backported to stable. Cc: Pali Rohár Fixes: 32262e2e429c ("serial: 8250: Fix reporting real baudrate value in c_ospeed field") Cc: stable Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211007133146.28949-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers/tty/serial/8250/8250_port.c') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index dc6900b2daa8..66374704747e 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2584,19 +2584,6 @@ 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) { @@ -2727,14 +2714,11 @@ 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); @@ -2766,7 +2750,6 @@ 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 -- cgit From 211cde4f5817dc88ef7f8f2fa286e57fbf14c8ee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 15 Oct 2021 13:14:20 +0200 Subject: serial: 8250: fix racy uartclk update Commit 868f3ee6e452 ("serial: 8250: Add 8250 port clock update method") added a hack to support SoCs where the UART reference clock can change behind the back of the driver but failed to add the proper locking. First, make sure to take a reference to the tty struct to avoid dereferencing a NULL pointer if the clock change races with a hangup. Second, the termios semaphore must be held during the update to prevent a racing termios change. Fixes: 868f3ee6e452 ("serial: 8250: Add 8250 port clock update method") Fixes: c8dff3aa8241 ("serial: 8250: Skip uninitialized TTY port baud rate update") Cc: stable@vger.kernel.org # 5.9 Cc: Serge Semin Tested-by: Serge Semin Reviewed-by: Serge Semin Acked-by: Andy Shevchenko Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211015111422.1027-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/tty/serial/8250/8250_port.c') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 66374704747e..e4dd82fd7c2a 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2696,21 +2696,32 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port, void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) { struct uart_8250_port *up = up_to_u8250p(port); + struct tty_port *tport = &port->state->port; unsigned int baud, quot, frac = 0; struct ktermios *termios; + struct tty_struct *tty; unsigned long flags; - mutex_lock(&port->state->port.mutex); + tty = tty_port_tty_get(tport); + if (!tty) { + mutex_lock(&tport->mutex); + port->uartclk = uartclk; + mutex_unlock(&tport->mutex); + return; + } + + down_write(&tty->termios_rwsem); + mutex_lock(&tport->mutex); if (port->uartclk == uartclk) goto out_lock; port->uartclk = uartclk; - if (!tty_port_initialized(&port->state->port)) + if (!tty_port_initialized(tport)) goto out_lock; - termios = &port->state->port.tty->termios; + termios = &tty->termios; baud = serial8250_get_baud_rate(port, termios, NULL); quot = serial8250_get_divisor(port, baud, &frac); @@ -2727,7 +2738,9 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) serial8250_rpm_put(up); out_lock: - mutex_unlock(&port->state->port.mutex); + mutex_unlock(&tport->mutex); + up_write(&tty->termios_rwsem); + tty_kref_put(tty); } EXPORT_SYMBOL_GPL(serial8250_update_uartclk); -- cgit From d2248ca8d6ba867fd9b2317af730ba73284d0989 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 15 Oct 2021 13:14:21 +0200 Subject: serial: 8250: rename unlock labels Rename a couple of oddly named labels that are used to unlock before returning after what they do (rather than after the context they are used in) to improve readability. Tested-by: Serge Semin Reviewed-by: Andy Shevchenko Reviewed-by: Serge Semin Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211015111422.1027-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/tty/serial/8250/8250_port.c') diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index e4dd82fd7c2a..5775cbff8f6e 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1338,7 +1338,7 @@ static void autoconfig(struct uart_8250_port *up) up->tx_loadsz = uart_config[port->type].tx_loadsz; if (port->type == PORT_UNKNOWN) - goto out_lock; + goto out_unlock; /* * Reset the UART. @@ -1355,7 +1355,7 @@ static void autoconfig(struct uart_8250_port *up) else serial_out(up, UART_IER, 0); -out_lock: +out_unlock: spin_unlock_irqrestore(&port->lock, flags); /* @@ -2714,12 +2714,12 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) mutex_lock(&tport->mutex); if (port->uartclk == uartclk) - goto out_lock; + goto out_unlock; port->uartclk = uartclk; if (!tty_port_initialized(tport)) - goto out_lock; + goto out_unlock; termios = &tty->termios; @@ -2737,7 +2737,7 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) spin_unlock_irqrestore(&port->lock, flags); serial8250_rpm_put(up); -out_lock: +out_unlock: mutex_unlock(&tport->mutex); up_write(&tty->termios_rwsem); tty_kref_put(tty); -- cgit