From patchwork Fri Jun 19 20:02:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11614985 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8232790 for ; Fri, 19 Jun 2020 20:03:25 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5FB4020FC3 for ; Fri, 19 Jun 2020 20:03:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="Lk6YDDSI" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 5FB4020FC3 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=baikalelectronics.ru Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=bFTnZpuAqfAtbbgNvZiHsB6FAyBR4p1ORV9HduIeKyI=; b=Lk6YDDSIWZoXxg tA4ZE3FXrQhWFGvwjpjTdv+4s+CySBVnTovfKDuBwE8GhKfgZYsrYVcma3Mc2lB3NuRH7y7j6CynJ +fTZ8iMfyH51A01NLzewtg7TFUFI+0cddUoT4f0VShoWgmQ7sHYw8f13amjQ+CF/8PM5wOACvRba8 wphStcCc6h6URGRSDSKvWe042oLqL601QH2t19X+W9mZKdgiSDQZKqajoPOcTuuiCYD/FYHAttj+M nfMs5+BpM7l526ViXAvzma+jS8EbXxV8Qs+AIFK7ZvLmI0uNKg/Ef6daciMYUCJwKkm1x09VZIF3r uWdtdZJ6/vR+LrI8tOag==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jmNES-0006Bh-IK; Fri, 19 Jun 2020 20:03:24 +0000 Received: from mx.baikalchip.com ([94.125.187.42] helo=mail.baikalelectronics.ru) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jmNEI-00063C-W9 for linux-arm-kernel@lists.infradead.org; Fri, 19 Jun 2020 20:03:16 +0000 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 1E8548040A6B; Fri, 19 Jun 2020 20:03:12 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id nzL3mHnictu1; Fri, 19 Jun 2020 23:03:09 +0300 (MSK) From: Serge Semin To: Thomas Bogendoerfer , Greg Kroah-Hartman , Jiri Slaby Subject: [PATCH v7 1/3] serial: 8250: Add 8250 port clock update method Date: Fri, 19 Jun 2020 23:02:49 +0300 Message-ID: <20200619200251.9066-2-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20200619200251.9066-1-Sergey.Semin@baikalelectronics.ru> References: <20200619200251.9066-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200619_130315_390619_4370620E X-CRM114-Status: GOOD ( 12.27 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-kernel@vger.kernel.org, Arnd Bergmann , linux-mips@vger.kernel.org, Russell King , Maxime Ripard , Alexey Malahov , Serge Semin , Serge Semin , linux-serial@vger.kernel.org, Andy Shevchenko , Will Deacon , linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Some platforms can be designed in a way so the UART port reference clock might be asynchronously changed at some point. In Baikal-T1 SoC this may happen due to the reference clock being shared between two UART ports, on the Allwinner SoC the reference clock is derived from the CPU clock, so any CPU frequency change should get to be known/reflected by/in the UART controller as well. But it's not enough to just update the uart_port->uartclk field of the corresponding UART port, the 8250 controller reference clock divisor should be altered so to preserve current baud rate setting. All of these things is done in a coherent way by calling the serial8250_update_uartclk() method provided in this patch. Though note that it isn't supposed to be called from within the UART port callbacks because the locks using to the protect the UART port data are already taken in there. Signed-off-by: Serge Semin Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Andy Shevchenko Cc: Maxime Ripard Cc: Will Deacon Cc: Russell King Cc: linux-mips@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org --- Changelog v4: - Export serial8250_update_uartclk() symbol for GPL modules only. Changelog v7: - Wake the device up on the serial port divider update. --- drivers/tty/serial/8250/8250_port.c | 40 +++++++++++++++++++++++++++++ include/linux/serial_8250.h | 2 ++ 2 files changed, 42 insertions(+) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 4d83c85a7389..7bfa0f7d9d60 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2628,6 +2628,46 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port, (port->uartclk + tolerance) / 16); } +/* + * Note in order to avoid the tty port mutex deadlock don't use the next method + * within the uart port callbacks. Primarily it's supposed to be utilized to + * handle a sudden reference clock rate change. + */ +void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int baud, quot, frac = 0; + struct ktermios *termios; + unsigned long flags; + + mutex_lock(&port->state->port.mutex); + + if (port->uartclk == uartclk) + goto out_lock; + + port->uartclk = uartclk; + termios = &port->state->port.tty->termios; + + baud = serial8250_get_baud_rate(port, termios, NULL); + quot = serial8250_get_divisor(port, baud, &frac); + + serial8250_rpm_get(up); + spin_lock_irqsave(&port->lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + + serial8250_set_divisor(port, baud, quot, frac); + serial_port_out(port, UART_LCR, up->lcr); + serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS); + + spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + +out_lock: + mutex_unlock(&port->state->port.mutex); +} +EXPORT_SYMBOL_GPL(serial8250_update_uartclk); + void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 6545f8cfc8fa..2b70f736b091 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -155,6 +155,8 @@ extern int early_serial_setup(struct uart_port *port); extern int early_serial8250_setup(struct earlycon_device *device, const char *options); +extern void serial8250_update_uartclk(struct uart_port *port, + unsigned int uartclk); extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); extern void serial8250_do_set_ldisc(struct uart_port *port, From patchwork Fri Jun 19 20:02:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11614987 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B4CD3618 for ; Fri, 19 Jun 2020 20:03:25 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 936A320FC3 for ; Fri, 19 Jun 2020 20:03:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="NYdV7rjX" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 936A320FC3 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=baikalelectronics.ru Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=4b7oCZ1mBMcAVDBg9TxEGpyoled7KzEmVH1uPIyvF7M=; b=NYdV7rjXKQZQYX xUfJ8AXdQuzkmRPe93TbRiqcqePeC0haVMWt36wea12g8ncnSK4BN82PuDiLMITBP6ANWJ2pEi0HU M/a0+XSfz8nmevg6zzs+CDKn4tO2JLOdnVVRpshcpZRwB53j8l73AQ/XKAKP2vO6ed9Ey7cc1YR0j PusVa95kpMxx3pCigpu5Pmo6fDIAE6N2gJfAEqBfifd3nprkg81FO0N1iHG5gRAbJIdB3wM/u6TF6 a9Au7Si5pfbqJidXL8fhHrwMt0JxBu+p3i/P30V2C8PyKe+2EX3isqdF40Ta0Tqn4AAPzWUILGbNn HXzWzzaXRyKoysvGlpSw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jmNEM-00064l-B1; Fri, 19 Jun 2020 20:03:18 +0000 Received: from ns2.chip.baikal.ru ([94.125.187.42] helo=mail.baikalelectronics.ru) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jmNEI-00063D-W8 for linux-arm-kernel@lists.infradead.org; Fri, 19 Jun 2020 20:03:16 +0000 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id C3BBB80045E4; Fri, 19 Jun 2020 20:03:12 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id fHwk4qyxZKn9; Fri, 19 Jun 2020 23:03:12 +0300 (MSK) From: Serge Semin To: Thomas Bogendoerfer , Greg Kroah-Hartman , Jiri Slaby , Andy Shevchenko Subject: [PATCH v7 2/3] serial: 8250_dw: Simplify the ref clock rate setting procedure Date: Fri, 19 Jun 2020 23:02:50 +0300 Message-ID: <20200619200251.9066-3-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20200619200251.9066-1-Sergey.Semin@baikalelectronics.ru> References: <20200619200251.9066-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200619_130315_207749_A7DCAE37 X-CRM114-Status: GOOD ( 10.53 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Arnd Bergmann , linux-kernel@vger.kernel.org, Russell King , Maxime Ripard , Alexey Malahov , Serge Semin , Serge Semin , linux-serial@vger.kernel.org, linux-mips@vger.kernel.org, Will Deacon , linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Really instead of twice checking the clk_round_rate() return value we could do it once, and if it isn't error the clock rate can be changed. By doing so we decrease a number of ret-value tests and remove a weird goto-based construction implemented in the dw8250_set_termios() method. Signed-off-by: Serge Semin Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Andy Shevchenko Cc: Maxime Ripard Cc: Will Deacon Cc: Russell King Cc: linux-mips@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org --- Changelog v3: - This is a new patch. --- drivers/tty/serial/8250/8250_dw.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index aab3cccc6789..12866083731d 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -282,20 +282,13 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, clk_disable_unprepare(d->clk); rate = clk_round_rate(d->clk, baud * 16); - if (rate < 0) - ret = rate; - else if (rate == 0) - ret = -ENOENT; - else + if (rate > 0) { ret = clk_set_rate(d->clk, rate); + if (!ret) + p->uartclk = rate; + } clk_prepare_enable(d->clk); - if (ret) - goto out; - - p->uartclk = rate; - -out: p->status &= ~UPSTAT_AUTOCTS; if (termios->c_cflag & CRTSCTS) p->status |= UPSTAT_AUTOCTS; From patchwork Fri Jun 19 20:02:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 11614997 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8D8D6618 for ; Fri, 19 Jun 2020 20:04:06 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 46B2320C09 for ; Fri, 19 Jun 2020 20:04:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="lCf9aQV1" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 46B2320C09 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=baikalelectronics.ru Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=uV6J1J/hVtJscLdAV4sRZnXsZ4NS02Zp2Q8xfvkZvgY=; b=lCf9aQV1JBIPRT /pJVnyUAmaItWMZxKoaTVYs7UgkGxmD6JFL1UGm+WDAT9GMEPlh/IkH+xE9bO1w6ciznezas0lglm W71zCXN36hOUf83FSsjryow6S6JC/BmFzpYI0BalcsaTEmkoiY3GrNzuwUiyhKHpIVQEx13wEJMPi aRa8IrCvA0txydvJMIZtNqgyqK5go2rh8fIGN9iDUOJVIvTtg2z/ToZAVYI6xDdrPpK/LmXUivotU XHSgqhIcJTnFgX+4OMfA/onUsEXCnFs4K6cSLMNdKb1bNk18ABsEk2YD6zKAh/4gtlZb7kFDUJrw3 ZZbCYBbY67yIib6TNS1w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jmNF6-0006rc-N6; Fri, 19 Jun 2020 20:04:04 +0000 Received: from mx.baikalchip.com ([94.125.187.42] helo=mail.baikalelectronics.ru) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jmNEJ-00063Q-9D for linux-arm-kernel@lists.infradead.org; Fri, 19 Jun 2020 20:03:18 +0000 Received: from localhost (unknown [127.0.0.1]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 9C70E8030866; Fri, 19 Jun 2020 20:03:14 +0000 (UTC) X-Virus-Scanned: amavisd-new at baikalelectronics.ru Received: from mail.baikalelectronics.ru ([127.0.0.1]) by localhost (mail.baikalelectronics.ru [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Q4yMHA-hPdvJ; Fri, 19 Jun 2020 23:03:12 +0300 (MSK) From: Serge Semin To: Thomas Bogendoerfer , Greg Kroah-Hartman , Jiri Slaby , Andy Shevchenko Subject: [PATCH v7 3/3] serial: 8250_dw: Fix common clocks usage race condition Date: Fri, 19 Jun 2020 23:02:51 +0300 Message-ID: <20200619200251.9066-4-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20200619200251.9066-1-Sergey.Semin@baikalelectronics.ru> References: <20200619200251.9066-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200619_130315_679029_F0BEB534 X-CRM114-Status: GOOD ( 24.30 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Arnd Bergmann , linux-kernel@vger.kernel.org, Russell King , Maxime Ripard , Alexey Malahov , Serge Semin , Serge Semin , linux-serial@vger.kernel.org, linux-mips@vger.kernel.org, Will Deacon , linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org The race condition may happen if the UART reference clock is shared with some other device (on Baikal-T1 SoC it's another DW UART port). In this case if that device changes the clock rate while serial console is using it the DW 8250 UART port might not only end up with an invalid uartclk value saved, but may also experience a distorted output data since baud-clock could have been changed. In order to fix this lets at least try to adjust the 8250 port setting like UART clock rate in case if the reference clock rate change is discovered. The driver will call the new method to update 8250 UART port clock rate settings. It's done by means of the clock event notifier registered at the port startup and unregistered in the shutdown callback method. Note 1. In order to avoid deadlocks we had to execute the UART port update method in a dedicated deferred work. This is due to (in my opinion redundant) the clock update implemented in the dw8250_set_termios() method. Note 2. Before the ref clock is manually changed by the custom set_termios() function we swap the port uartclk value with new rate adjusted to be suitable for the requested baud. It is necessary in order to effectively disable a functionality of the ref clock events handler for the current UART port, since uartclk update will be done a bit further in the generic serial8250_do_set_termios() function. Signed-off-by: Serge Semin Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Andy Shevchenko Cc: Maxime Ripard Cc: Will Deacon Cc: Russell King Cc: linux-mips@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org --- Changelog v2: - Move exclusive ref clock lock/unlock precudures to the 8250 port startup/shutdown methods. - The changelog message has also been slightly modified due to the alteration. - Remove Alexey' SoB tag. - Cc someone from ARM who might be concerned regarding this change. - Cc someone from Clocks Framework to get their comments on this patch. Changelog v3: - Refactor the original patch to adjust the UART port divisor instead of requesting an exclusive ref clock utilization. Changelog v5: - Refactor dw8250_clk_work_cb() function cheking the clk_get_rate() return value for being erroneous and exit if it is. - Don't update p->uartclk on the port startup. It will be updated later in the same procedure at the set_termios() function being invoked by the serial_core anyway. --- drivers/tty/serial/8250/8250_dw.c | 105 +++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 12866083731d..fa59c026270f 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -43,6 +45,8 @@ struct dw8250_data { int msr_mask_off; struct clk *clk; struct clk *pclk; + struct notifier_block clk_notifier; + struct work_struct clk_work; struct reset_control *rst; unsigned int skip_autocfg:1; @@ -54,6 +58,16 @@ static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) return container_of(data, struct dw8250_data, data); } +static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb) +{ + return container_of(nb, struct dw8250_data, clk_notifier); +} + +static inline struct dw8250_data *work_to_dw8250_data(struct work_struct *work) +{ + return container_of(work, struct dw8250_data, clk_work); +} + static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) { struct dw8250_data *d = to_dw8250_data(p->private_data); @@ -260,6 +274,46 @@ static int dw8250_handle_irq(struct uart_port *p) return 0; } +static void dw8250_clk_work_cb(struct work_struct *work) +{ + struct dw8250_data *d = work_to_dw8250_data(work); + struct uart_8250_port *up; + unsigned long rate; + + rate = clk_get_rate(d->clk); + if (rate <= 0) + return; + + up = serial8250_get_port(d->data.line); + + serial8250_update_uartclk(&up->port, rate); +} + +static int dw8250_clk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct dw8250_data *d = clk_to_dw8250_data(nb); + + /* + * We have no choice but to defer the uartclk update due to two + * deadlocks. First one is caused by a recursive mutex lock which + * happens when clk_set_rate() is called from dw8250_set_termios(). + * Second deadlock is more tricky and is caused by an inverted order of + * the clk and tty-port mutexes lock. It happens if clock rate change + * is requested asynchronously while set_termios() is executed between + * tty-port mutex lock and clk_set_rate() function invocation and + * vise-versa. Anyway if we didn't have the reference clock alteration + * in the dw8250_set_termios() method we wouldn't have needed this + * deferred event handling complication. + */ + if (event == POST_RATE_CHANGE) { + queue_work(system_unbound_wq, &d->clk_work); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + static void dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) { @@ -283,9 +337,16 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, clk_disable_unprepare(d->clk); rate = clk_round_rate(d->clk, baud * 16); if (rate > 0) { - ret = clk_set_rate(d->clk, rate); - if (!ret) - p->uartclk = rate; + /* + * Premilinary set the uartclk to the new clock rate so the + * clock update event handler caused by the clk_set_rate() + * calling wouldn't actually update the UART divisor since + * we about to do this anyway. + */ + swap(p->uartclk, rate); + ret = clk_set_rate(d->clk, p->uartclk); + if (ret) + swap(p->uartclk, rate); } clk_prepare_enable(d->clk); @@ -312,6 +373,39 @@ static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) serial8250_do_set_ldisc(p, termios); } +static int dw8250_startup(struct uart_port *p) +{ + struct dw8250_data *d = to_dw8250_data(p->private_data); + int ret; + + /* + * Some platforms may provide a reference clock shared between several + * devices. In this case before using the serial port first we have to + * make sure that any clock state change is known to the UART port at + * least post factum. + */ + if (d->clk) { + ret = clk_notifier_register(d->clk, &d->clk_notifier); + if (ret) + dev_warn(p->dev, "Failed to set the clock notifier\n"); + } + + return serial8250_do_startup(p); +} + +static void dw8250_shutdown(struct uart_port *p) +{ + struct dw8250_data *d = to_dw8250_data(p->private_data); + + serial8250_do_shutdown(p); + + if (d->clk) { + clk_notifier_unregister(d->clk, &d->clk_notifier); + + flush_work(&d->clk_work); + } +} + /* * dw8250_fallback_dma_filter will prevent the UART from getting just any free * channel on platforms that have DMA engines, but don't have any channels @@ -407,6 +501,8 @@ static int dw8250_probe(struct platform_device *pdev) p->serial_out = dw8250_serial_out; p->set_ldisc = dw8250_set_ldisc; p->set_termios = dw8250_set_termios; + p->startup = dw8250_startup; + p->shutdown = dw8250_shutdown; p->membase = devm_ioremap(dev, regs->start, resource_size(regs)); if (!p->membase) @@ -468,6 +564,9 @@ static int dw8250_probe(struct platform_device *pdev) if (IS_ERR(data->clk)) return PTR_ERR(data->clk); + INIT_WORK(&data->clk_work, dw8250_clk_work_cb); + data->clk_notifier.notifier_call = dw8250_clk_notifier_cb; + err = clk_prepare_enable(data->clk); if (err) dev_warn(dev, "could not enable optional baudclk: %d\n", err);