From patchwork Mon Mar 21 16:30:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Pali_Roh=C3=A1r?= X-Patchwork-Id: 553710 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 43D77C433F5 for ; Mon, 21 Mar 2022 16:31:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241777AbiCUQcn (ORCPT ); Mon, 21 Mar 2022 12:32:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60684 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347366AbiCUQcf (ORCPT ); Mon, 21 Mar 2022 12:32:35 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5ECC4F7F5C; Mon, 21 Mar 2022 09:31:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id EE6BC6135B; Mon, 21 Mar 2022 16:31:09 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3218DC340F0; Mon, 21 Mar 2022 16:31:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1647880269; bh=G0Iu+15P7J/9+ldVRS0Kh3E5nSoQYqiC6yNgO0mYsEI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pTgGMiUOnY9NUs9Paagrn3RqiWs3DMFROmXDzrmjp2mFPLDMuvl3ii3I9iu1SerXt +07UC/3W+PlrohAX6bdBg7l2105J3TrzDhL4mQ2Exar1y1loJhL3ewsTNRKPyaqCzw 5Lve8Zl9+L0MwDENJOFUB4jwOvhQFqv9tog8YZizwfB678fPIdj+Fjlui7SJ+5huqc M7Ne8y+jhNudkxj81RdQIA13wJPUs4vRNPma2aw6VO9apynGJgtiS7e0csbEP0rMyp QpjFShIauPSoKuKGwz2Dau6V7egM2fH8oZaAIu6M+02biOcDcV/OdwscuTLravWi7M J1JsrsVCYvjaA== Received: by pali.im (Postfix) id 5D6F6C77; Mon, 21 Mar 2022 17:31:06 +0100 (CET) From: =?utf-8?q?Pali_Roh=C3=A1r?= To: Greg Kroah-Hartman , Jiri Slaby , Johan Hovold , =?utf-8?q?Marek_Beh=C3=BAn?= Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/3] serial: core: Document why UPF_SPD_CUST is not handled in uart_get_baud_rate() Date: Mon, 21 Mar 2022 17:30:53 +0100 Message-Id: <20220321163055.4058-2-pali@kernel.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20220321163055.4058-1-pali@kernel.org> References: <20220321163055.4058-1-pali@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org Switch in uart_get_baud_rate() function is missing case for UPF_SPD_CUST flag. It is not obvious why it is missing here, so add comments explaining how deprecated UPF_SPD_CUST flag is handled and how drivers should call uart_get_baud_rate() and uart_get_divisor() functions. Signed-off-by: Pali Rohár --- drivers/tty/serial/serial_core.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 1e738f265eea..d8fc2616d62b 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -364,7 +364,17 @@ EXPORT_SYMBOL(uart_update_timeout); * * Decode the termios structure into a numeric baud rate, * taking account of the magic 38400 baud rate (with spd_* - * flags), and mapping the %B0 rate to 9600 baud. + * flags, except cust), and mapping the %B0 rate to 9600 baud. + * + * UPF_SPD_CUST flag is not handled in this function as it applies + * to the custom divisor. When UPF_SPD_CUST flag is active and in + * use then this function returns value 38400 and not the correct + * baud rate. + * + * Drivers should call uart_get_divisor() function with baud rate + * returned from this function to calculate clock divisor. Function + * uart_get_divisor() then handles UPF_SPD_CUST flag with magic + * baud rate value 38400 and returns the correct custom divisor. * * If the new baud rate is invalid, try the old termios setting. * If it's still invalid, we try 9600 baud. @@ -396,6 +406,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, case UPF_SPD_WARP: altbaud = 460800; break; + /* Flag UPF_SPD_CUST is not handed here, see description why. */ default: altbaud = 38400; break; @@ -462,6 +473,8 @@ EXPORT_SYMBOL(uart_get_baud_rate); * @baud: desired baud rate * * Calculate the uart clock divisor for the port. + * + * Handles also special case when UPF_SPD_CUST flag is in use. */ unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud) From patchwork Mon Mar 21 16:30:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Pali_Roh=C3=A1r?= X-Patchwork-Id: 553436 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 31806C433F5 for ; Mon, 21 Mar 2022 16:31:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351183AbiCUQco (ORCPT ); Mon, 21 Mar 2022 12:32:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35186 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1351187AbiCUQci (ORCPT ); Mon, 21 Mar 2022 12:32:38 -0400 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 619BAFCBC8; Mon, 21 Mar 2022 09:31:12 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 022D0B81891; Mon, 21 Mar 2022 16:31:11 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5B930C340F2; Mon, 21 Mar 2022 16:31:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1647880269; bh=wG7sX/hncO57KXrCuRz1PGNOImxeZapEiK9Kq930zNI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=l2a2zvqi5KkgK3ug6zMo8M2KiEfY5iBXVJmdAz8Pcp93J2Oy/A8Ejt/6R4KzhMcB6 vwjt7X995yqZBAjMQIGW8rTaFdUbNUL7oynCUFXlh6ibNytu/A92a3LWSjsOUQGAAf 82lVEbPF7kO+YtjsKq1nTKfYVPh/6b1yg9Skon4ARLESadww1SzWBCF7/Zz/QGs1AS mi/Y2bUniHpiNBf8s2zDBhhn+0l7lbgZJWdHo0FxS7DLwk0on96mdRuPFYI1FjWBN9 TtL354F3lUX3MWCsamOCVjODvT13yhoRcCsivK9Qo1+u8VofhdczroAkH5238/yB4C IGqQ1D2lk073Q== Received: by pali.im (Postfix) id D0A93EEB; Mon, 21 Mar 2022 17:31:06 +0100 (CET) From: =?utf-8?q?Pali_Roh=C3=A1r?= To: Greg Kroah-Hartman , Jiri Slaby , Johan Hovold , =?utf-8?q?Marek_Beh=C3=BAn?= Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/3] serial: core: Fix function uart_update_timeout() to handle UPF_SPD_CUST flag Date: Mon, 21 Mar 2022 17:30:54 +0100 Message-Id: <20220321163055.4058-3-pali@kernel.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20220321163055.4058-1-pali@kernel.org> References: <20220321163055.4058-1-pali@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org Function uart_update_timeout() currently handles UPF_SPD_HI, UPF_SPD_VHI, UPF_SPD_SHI and UPF_SPD_WARP flags thanks to how uart_get_baud_rate() works and how should be used. uart_get_baud_rate() already translates these 4 special flags to real baud rate value and therefore uart_update_timeout() is called with the correct baud rate argument. What is not handled in this function is the last remaining flag UPF_SPD_CUST. It is because uart_get_baud_rate() returns hardcoded value 38400 when UPF_SPD_CUST is set and in use. Implement support for UPF_SPD_CUST in uart_update_timeout(), so port->timeout is calculated also when UPF_SPD_CUST flag is set. For calculation use base uart clock and custom divisor. This calculation does not have to be precise for all UART hardware but this the best approximation which can be done. It is for sure better than hardcoded baud rate value 38400. Signed-off-by: Pali Rohár --- drivers/tty/serial/serial_core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index d8fc2616d62b..34e085a038fe 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -343,6 +343,16 @@ uart_update_timeout(struct uart_port *port, unsigned int cflag, { unsigned int size; + /* + * Old custom speed handling. See function uart_get_divisor() + * and description in uart_get_baud_rate() function. + * This calculation does not have to be precise for all UART + * hardware but it is the best approximation which we can do here + * as we do not know the exact baud rate. + */ + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) + baud = DIV_ROUND_CLOSEST(port->uartclk, 16 * port->custom_divisor); + size = tty_get_frame_size(cflag) * port->fifosize; /* From patchwork Mon Mar 21 16:30:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Pali_Roh=C3=A1r?= X-Patchwork-Id: 553709 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5DBB1C433FE for ; Mon, 21 Mar 2022 16:31:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350022AbiCUQco (ORCPT ); Mon, 21 Mar 2022 12:32:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34896 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1351165AbiCUQch (ORCPT ); Mon, 21 Mar 2022 12:32:37 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 24C16F957F; Mon, 21 Mar 2022 09:31:11 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id B0EE5612F5; Mon, 21 Mar 2022 16:31:10 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id C6539C340F9; Mon, 21 Mar 2022 16:31:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1647880270; bh=oLh0/VBSUCblDOhHVjzy1tQ+2cNVbgrdWCBKnnqb0CA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ydb+/oIJa5h1SawT5gGwwGDXMJkjDhxO2MexA84GMrIXHOEzS9u42UOjShgm41ucT aODIvGXw4SgXQ0lomAIL/BCqJybUUPfCaZmTR4bHXHFQliNF8teUPltpHuREG2ET4k TKJXWXbGnVihiSMS6XFHdlGWCWkNnodHHz6/KJaI0Kvv4aMcy8QoxKROpKqR2B8zLI zblcWL5X/4CapDP65ycLpZapJ5GQi7QKTLBWbRAjZ8ZeNQSBRVTjMhKPuCHz3RWXBr bkG1sEMLCMXy2ZwP91SBbYyPWR14Q7dAoHK8WjfKunp3icTWSQmK8kfo+3m6JBmI+R PcL2FI0MbxxFQ== Received: by pali.im (Postfix) id 46142EF8; Mon, 21 Mar 2022 17:31:07 +0100 (CET) From: =?utf-8?q?Pali_Roh=C3=A1r?= To: Greg Kroah-Hartman , Jiri Slaby , Johan Hovold , =?utf-8?q?Marek_Beh=C3=BAn?= Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/3] serial: Fix support for UPF_SPD_* flags in serial drivers Date: Mon, 21 Mar 2022 17:30:55 +0100 Message-Id: <20220321163055.4058-4-pali@kernel.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20220321163055.4058-1-pali@kernel.org> References: <20220321163055.4058-1-pali@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org 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 --- 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(-) diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c index 09baef4ccc39..4e4dbd58c581 100644 --- a/drivers/tty/serial/21285.c +++ b/drivers/tty/serial/21285.c @@ -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: diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index fb65dc601b23..4a46a7f039d0 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -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) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 73e5f1dbd075..96c1df9ffd25 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -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 */ diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 5775cbff8f6e..05ca0152b932 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -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); diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 7c5f4e966b59..ec352d53662a 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -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); diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 4379ca4842ae..48e8127d0e9d 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -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) diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 596217d10d5c..cc2a06832699 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -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); diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index e9edabc5a211..8084966ba9f9 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -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; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 90f82e6c54e4..b56344b46459 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -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; diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 497b334bc845..38b2f7fa80ff 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -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); } diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index b199d7859961..e6f9166be0c7 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -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) diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 9acae5f8fc32..e3a83bca9991 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -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); diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c index 587b42f754cb..117a7afcd088 100644 --- a/drivers/tty/serial/mps2-uart.c +++ b/drivers/tty/serial/mps2-uart.c @@ -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) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index fcef7a961430..789dfeb286bf 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -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); diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index ab226da75f7b..7bd9579855fa 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -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); } diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index 91f1eb0058d7..8d70cdbd4a73 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -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) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index f0351e6f0ef6..53f2e9c09d8d 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -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); diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index 0a12fb11e698..286045456a1c 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -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); diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index d550d8fa2fab..c08c597cda86 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -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); diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 6689d8add8f7..a12e9a97905b 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -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); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 10cc16a71f26..141dc0ebb688 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -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); diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 45e2e4109acd..876aa57dbcd0 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -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 */ diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 34e085a038fe..113db02be87e 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -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. diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 9a7ae6384edf..17b0cadd31e6 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -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) diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 08941eabe7b1..c41377d218e7 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -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); diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index e15b2bf69904..2e5d9b52e478 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -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); diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index d5e243908d9f..300108c9ba7e 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -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); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index c58cc142d23f..a0f736be5645 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -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 */