summaryrefslogtreecommitdiff
path: root/drivers/tty/serial
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2021-09-27 12:37:04 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-11-18 21:16:07 +0300
commit02981a96642fbdfbeaeaa659db54f3375c54e69d (patch)
tree9a3ebc68390273140462e39f1f90c0df5b0fc6e2 /drivers/tty/serial
parent670f1f30ed706e192448a6b3f4f0af73fdc22591 (diff)
downloadlinux-02981a96642fbdfbeaeaa659db54f3375c54e69d.tar.xz
serial: 8250: Fix reporting real baudrate value in c_ospeed field
commit 32262e2e429cdb31f9e957e997d53458762931b7 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>
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r--drivers/tty/serial/8250/8250_port.c17
1 files changed, 17 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index e4dd82fd7c2a..affefe8f775d 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)
{
@@ -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