@@ -265,7 +265,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
b = port->uartclk / (16 * quot);
- tty_termios_encode_baud_rate(termios, b, b);
+ uart_set_baud_rate(port, termios, b);
switch (termios->c_cflag & CSIZE) {
case CS5:
@@ -406,7 +406,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
spin_unlock_irqrestore(&port->lock, flags);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
}
static int __maybe_unused mtk8250_runtime_suspend(struct device *dev)
@@ -510,7 +510,7 @@ static void omap_8250_set_termios(struct uart_port *port,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
}
/* same as 8250 except that we may have extra flow bits set in EFR */
@@ -2878,7 +2878,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
}
EXPORT_SYMBOL(serial8250_do_set_termios);
@@ -185,7 +185,7 @@ static void altera_uart_set_termios(struct uart_port *port,
if (old)
tty_termios_copy_hw(termios, old);
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
spin_lock_irqsave(&port->lock, flags);
uart_update_timeout(port, termios->c_cflag, baud);
@@ -355,7 +355,7 @@ static void ar933x_uart_set_termios(struct uart_port *port,
spin_unlock_irqrestore(&up->port.lock, flags);
if (tty_termios_baud_rate(new))
- tty_termios_encode_baud_rate(new, baud, baud);
+ uart_set_baud_rate(port, new, baud);
}
static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
@@ -391,7 +391,7 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(new))
- tty_termios_encode_baud_rate(new, baud, baud);
+ uart_set_baud_rate(port, new, baud);
uart_update_timeout(port, new->c_cflag, baud);
@@ -599,7 +599,7 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,
baud = 9600;
bflag = DZ_B9600;
}
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(uport, termios, baud);
}
cflag |= bflag;
@@ -1729,8 +1729,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
tdiv64 = sport->port.uartclk;
tdiv64 *= num;
do_div(tdiv64, denom * 16 * div);
- tty_termios_encode_baud_rate(termios,
- (speed_t)tdiv64, (speed_t)tdiv64);
+ uart_set_baud_rate(port, termios, (speed_t)tdiv64);
num -= 1;
denom -= 1;
@@ -502,7 +502,7 @@ lqasc_set_termios(struct uart_port *port,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(new))
- tty_termios_encode_baud_rate(new, baud, baud);
+ uart_set_baud_rate(port, new, baud);
uart_update_timeout(port, cflag, baud);
}
@@ -530,7 +530,7 @@ static void serial_lpc32xx_set_termios(struct uart_port *port,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
}
static const char *serial_lpc32xx_type(struct uart_port *port)
@@ -713,7 +713,7 @@ static void men_z135_set_termios(struct uart_port *port,
spin_lock_irq(&port->lock);
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
bd_reg = uart_freq / (4 * baud);
iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG);
@@ -383,7 +383,7 @@ mps2_uart_set_termios(struct uart_port *port, struct ktermios *termios,
spin_unlock_irqrestore(&port->lock, flags);
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
}
static const char *mps2_uart_type(struct uart_port *port)
@@ -1262,7 +1262,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
baud = uart_get_baud_rate(port, termios, old, 300, 4000000);
baud = msm_set_baud_rate(port, baud, &flags);
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
/* calculate parity */
mr = msm_read(port, UART_MR2);
@@ -515,7 +515,7 @@ static void mvebu_uart_set_termios(struct uart_port *port,
baud = uart_get_baud_rate(port, old, NULL,
min_baud, max_baud);
} else {
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
uart_update_timeout(port, termios->c_cflag, baud);
}
@@ -387,7 +387,7 @@ static void owl_uart_set_termios(struct uart_port *port,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
port->read_status_mask |= OWL_UART_STAT_RXER;
if (termios->c_iflag & INPCK)
@@ -1412,7 +1412,7 @@ static void pch_uart_set_termios(struct uart_port *port,
pch_uart_set_mctrl(&priv->port, priv->port.mctrl);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
out:
spin_unlock(&port->lock);
@@ -596,7 +596,7 @@ static void pic32_uart_set_termios(struct uart_port *port,
uart_update_timeout(port, new->c_cflag, baud);
if (tty_termios_baud_rate(new))
- tty_termios_encode_baud_rate(new, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
/* enable uart */
pic32_uart_en_and_unmask(port);
@@ -318,7 +318,7 @@ static void rda_uart_set_termios(struct uart_port *port,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
/* update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, baud);
@@ -382,7 +382,7 @@ static void rp2_uart_set_termios(struct uart_port *port,
baud_div = uart_get_divisor(port, baud);
if (tty_termios_baud_rate(new))
- tty_termios_encode_baud_rate(new, baud, baud);
+ uart_set_baud_rate(port, new, baud);
spin_lock_irqsave(&port->lock, flags);
@@ -714,7 +714,7 @@ static void sccnxp_set_termios(struct uart_port *port,
/* Report actual baudrate back to core */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
/* Enable RX & TX */
sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
@@ -1360,7 +1360,7 @@ static void tegra_uart_set_termios(struct uart_port *u,
return;
}
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(u, termios, baud);
spin_lock_irqsave(&u->lock, flags);
/* Flow control */
@@ -477,6 +477,57 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
EXPORT_SYMBOL(uart_get_baud_rate);
+void
+uart_set_baud_rate(struct uart_port *port, struct ktermios *termios, unsigned int baud)
+{
+ upf_t flags = port->flags & UPF_SPD_MASK;
+ unsigned int close = baud / 50;
+ unsigned int altbaud = 0;
+
+ switch (flags) {
+ case UPF_SPD_HI:
+ altbaud = 57600;
+ break;
+ case UPF_SPD_VHI:
+ altbaud = 115200;
+ break;
+ case UPF_SPD_SHI:
+ altbaud = 230400;
+ break;
+ case UPF_SPD_WARP:
+ altbaud = 460800;
+ break;
+ }
+
+ /*
+ * UPF_SPD_* port flags are in use when B38400 is set in termios.
+ * Let termios baudrate set to B38400 value when new baudrate is
+ * in 2% tolerance (same tolerance as in tty_termios_encode_baud_rate).
+ * For UPF_SPD_CUST flag it is required to be this function called with
+ * baud = 38400 and then real baudrate depends only on custom_divisor.
+ */
+ if (tty_termios_baud_rate(termios) == 38400) {
+ if (altbaud && baud - close >= altbaud && baud + close <= altbaud) {
+ altbaud = baud;
+ baud = 38400;
+ } else if (flags == UPF_SPD_CUST && baud == 38400) {
+ /* See uart_update_timeout() about this calculation */
+ altbaud = DIV_ROUND_CLOSEST(port->uartclk, 16 * port->custom_divisor);
+ }
+ }
+
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
+ /*
+ * If UPF_SPD_* port flags are active and in use then store into
+ * TCGETS2 c_*speed fields real baudrate.
+ */
+ if (baud == 38400 && altbaud)
+ termios->c_ispeed = termios->c_ospeed = altbaud;
+}
+
+EXPORT_SYMBOL(uart_set_baud_rate);
+
/**
* uart_get_divisor - return uart clock divisor
* @port: uart_port structure describing the port.
@@ -867,7 +867,7 @@ static void sprd_set_termios(struct uart_port *port,
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
}
static const char *sprd_type(struct uart_port *port)
@@ -294,7 +294,7 @@ static void timbuart_set_termios(struct uart_port *port,
up initially */
if (old)
tty_termios_copy_hw(termios, old);
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
spin_lock_irqsave(&port->lock, flags);
iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE);
@@ -369,7 +369,7 @@ static void vt8500_set_termios(struct uart_port *port,
baud = uart_get_baud_rate(port, termios, old, 900, 921600);
baud = vt8500_set_baud_rate(port, baud);
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
/* calculate parity */
lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR);
@@ -714,7 +714,7 @@ static void cdns_uart_set_termios(struct uart_port *port,
baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud);
baud = cdns_uart_set_baud_rate(port, baud);
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, baud, baud);
+ uart_set_baud_rate(port, termios, baud);
/* Update the per-port timeout. */
uart_update_timeout(port, termios->c_cflag, baud);
@@ -330,6 +330,8 @@ void uart_update_timeout(struct uart_port *port, unsigned int cflag,
unsigned int uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
struct ktermios *old, unsigned int min,
unsigned int max);
+void uart_set_baud_rate(struct uart_port *port, struct ktermios *termios,
+ unsigned int baud);
unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud);
/* Base timer interval for polling */
Most serial drivers do not handle UPF_SPD_* flags correctly. They use uart_get_baud_rate() and uart_get_divisor() functions for retrieving baud rate and divisor which correctly handle UPF_SPD_* flags and set correct value to HW. But drivers do not propagate correct value to termios structure as they call just tty_termios_encode_baud_rate() function which completely ignores these UPF_SPD_* flags. So termios structure reported to userspace does not match to UPF_SPD_* flags which were used for configuring HW and also does not match ASYNC_SDP_* flags stored in serial_struct which are reported to userspace. Fix this issue by introducing a new function uart_set_baud_rate() which is wrapper around tty_termios_encode_baud_rate() and which handles those UPF_SPD_* flags correctly. Replace all calls of tty_termios_encode_baud_rate() function which take an argument from uart_get_baud_rate() function by this new function uart_set_baud_rate(). This ensures that serial drivers which are using uart_get_baud_rate() will correctly handle UPF_SPD_* flags. Signed-off-by: Pali Rohár <pali@kernel.org> --- drivers/tty/serial/21285.c | 2 +- drivers/tty/serial/8250/8250_mtk.c | 2 +- drivers/tty/serial/8250/8250_omap.c | 2 +- drivers/tty/serial/8250/8250_port.c | 2 +- drivers/tty/serial/altera_uart.c | 2 +- drivers/tty/serial/ar933x_uart.c | 2 +- drivers/tty/serial/arc_uart.c | 2 +- drivers/tty/serial/dz.c | 2 +- drivers/tty/serial/imx.c | 3 +- drivers/tty/serial/lantiq.c | 2 +- drivers/tty/serial/lpc32xx_hs.c | 2 +- drivers/tty/serial/men_z135_uart.c | 2 +- drivers/tty/serial/mps2-uart.c | 2 +- drivers/tty/serial/msm_serial.c | 2 +- drivers/tty/serial/mvebu-uart.c | 2 +- drivers/tty/serial/owl-uart.c | 2 +- drivers/tty/serial/pch_uart.c | 2 +- drivers/tty/serial/pic32_uart.c | 2 +- drivers/tty/serial/rda-uart.c | 2 +- drivers/tty/serial/rp2.c | 2 +- drivers/tty/serial/sccnxp.c | 2 +- drivers/tty/serial/serial-tegra.c | 2 +- drivers/tty/serial/serial_core.c | 51 +++++++++++++++++++++++++++++ drivers/tty/serial/sprd_serial.c | 2 +- drivers/tty/serial/timbuart.c | 2 +- drivers/tty/serial/vt8500_serial.c | 2 +- drivers/tty/serial/xilinx_uartps.c | 2 +- include/linux/serial_core.h | 2 ++ 28 files changed, 79 insertions(+), 27 deletions(-)