From patchwork Fri Feb 11 19:12:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 12743774 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 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 9F21AC433F5 for ; Fri, 11 Feb 2022 19:14:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=Q48pLBTpIrlejqj+tFw6bF0PM/9dtacAgHLAo001NvA=; b=3frRgLeQOxDpSD 5uyEedcSMTv0miI6b6mc9X8XYelWdZOhfYOM4JaS+DvYB9zSf9HhzgQ9DlV1429ca4eyYMdGmVzaV eSldonmDHOE5vkfi63gFIq3cd1CEQTvD19fDEj71rPNB/HBlZoQ3l13XI5L3LJ5HZ9ZpT74+b+vHE vxU8Rr2nqdBAj/2fRyclxGgHgqH9UCor1cpn3TjGogB5WTleSCMhzSbobrjwt7ojWjVJjq6deAxhs riSg0qw6l2NgzJbxMl1Q4Bo5B7t2sV5Mkh0N0wwiXg5ftfDOqTSqXyfGn6dMiDmQqRop5XZej90WI tTU6QAdpKVj1L79JZYig==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLl-008Wfs-9Q; Fri, 11 Feb 2022 19:12:57 +0000 Received: from dfw.source.kernel.org ([139.178.84.217]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLa-008WdK-74 for linux-arm-kernel@lists.infradead.org; Fri, 11 Feb 2022 19:12:47 +0000 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 C898A61F39; Fri, 11 Feb 2022 19:12:45 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3C94BC340E9; Fri, 11 Feb 2022 19:12:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1644606765; bh=/kRr5IqKtgAB6PuFxk7QjfVSXaEzJJ5qpIyJSbD3iEA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Wj+9Wb+CZtHszzgJ+ck+1SkW0c4aQ8Pox7l5ywg2DmQlaPfXOUTSKbuMBNIcTXCRo W8KQvmDXlQO7fHSBE9l2XXC1MWvVBjZ4c21RMHWnretX0Q71qtKTQbL7szMxgUMzNQ 4gUYfcwuMiu6LFY/ZGMS529OY9aUnc0FR5Gn7B25FywTlLAkzQzu6Nca6+jt5YUiOZ DER+Sat5I1j9eecxwZL1cu+JHVn9s13aY8tTHZeZ2dXAg9lZwdJCAyi5Vbbyu0o4Kt CemlHDyDk4afLe7Hfn1s6Y+Mmr6B/F3rEQE9HiP19pYeRw53iyUPRqAhw67vofN6aI sV6GCYN9loMew== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Stephen Boyd , Michael Turquette , Greg Kroah-Hartman , Gregory Clement Cc: =?utf-8?q?Pali_Roh=C3=A1r?= , linux-clk@vger.kernel.org, linux-serial@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH v8 1/6] math64: New DIV_U64_ROUND_CLOSEST helper Date: Fri, 11 Feb 2022 20:12:33 +0100 Message-Id: <20220211191238.2142-2-kabel@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220211191238.2142-1-kabel@kernel.org> References: <20220211191238.2142-1-kabel@kernel.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220211_111246_314858_E1FAABC1 X-CRM114-Status: GOOD ( 10.19 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Pali Rohár Provide DIV_U64_ROUND_CLOSEST helper which uses div_u64 to perform division rounded to the closest integer using unsigned 64bit dividend and unsigned 32bit divisor. Signed-off-by: Pali Rohár Reviewed-by: Marek Behún Signed-off-by: Marek Behún --- Changes since v7: - added Marek's Reviewed-by tag --- include/linux/math64.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/linux/math64.h b/include/linux/math64.h index 2928f03d6d46..a14f40de1dca 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -300,6 +300,19 @@ u64 mul_u64_u64_div_u64(u64 a, u64 mul, u64 div); #define DIV64_U64_ROUND_CLOSEST(dividend, divisor) \ ({ u64 _tmp = (divisor); div64_u64((dividend) + _tmp / 2, _tmp); }) +/* + * DIV_U64_ROUND_CLOSEST - unsigned 64bit divide with 32bit divisor rounded to nearest integer + * @dividend: unsigned 64bit dividend + * @divisor: unsigned 32bit divisor + * + * Divide unsigned 64bit dividend by unsigned 32bit divisor + * and round to closest integer. + * + * Return: dividend / divisor rounded to nearest integer + */ +#define DIV_U64_ROUND_CLOSEST(dividend, divisor) \ + ({ u32 _tmp = (divisor); div_u64((u64)(dividend) + _tmp / 2, _tmp); }) + /* * DIV_S64_ROUND_CLOSEST - signed 64bit divide with 32bit divisor rounded to nearest integer * @dividend: signed 64bit dividend From patchwork Fri Feb 11 19:12:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 12743777 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 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 182BFC433EF for ; Fri, 11 Feb 2022 19:14:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=LntlV2pmexXKPnatWcWG1K3dnnAihgiutESzPf5A7Wk=; b=cdkdRaAqPLz3/5 H2qCycDd0TlRslRiGRiTLX8JIwl9HaywG9pbMsZw+ybQLbk4zJNiYHaljDZ9tyGdVxzcjgCzj6zVE n1XlSotb9fUHf7oQ7/4ompCCR9fshN70ijfJcQLpc6Ft86Po4HC2misGgFf3ccuLDQnvsLlb2qcIL f5Kz3ZADl4r3+E047CZR1I39/yV0byqICj9h/GOkCNhkJFCsJDi0Qx0pkT4hmCoso6hWSD5aOk3PF v8eP6R7PU5isGKG0ehSQP+QkKXgfVJnoBUG3sKnBkE/w31q4fW+jWMNCW686OWkXgA1vubWMd5kM8 mFo0/krnkQy8qbl7WcjQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbMO-008Wuy-9F; Fri, 11 Feb 2022 19:13:36 +0000 Received: from dfw.source.kernel.org ([2604:1380:4641:c500::1]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLr-008Wdq-6m for linux-arm-kernel@lists.infradead.org; Fri, 11 Feb 2022 19:13:04 +0000 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 1100861F24; Fri, 11 Feb 2022 19:12:48 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id AAD40C340F2; Fri, 11 Feb 2022 19:12:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1644606767; bh=kaArX3mep8T1juUfVoTJSgqg1e77nuBpM1rAZZLiL5Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CGFg1aPBiw7lgHDhG+PiaowWYKoeM663d3ZC/qLhY/OXxHiLBxvuTY/WdqTQDft8L PXtWKCVQVOJ0mee3t/3NtPRN0qgpjhCDOw4nric1R9xjMcZQZX9e2pYxsvme0Fm+FR JxOtbqft01eG3Kli5M28K7xMZ9BBv2kBphPQ997hSpYQFK7w1yAQML2dSZbwydHApR Ymz6s9g/Dldlfsa7HC60qqjCvaJmRxhQF4uHEdDGH+ldD8e8vSfTc58J6uxoC9cAWv ZBWTdyzF05/lXrDzmRLsTJZ5pb6jVOooWIaxT2r68RsE0RLKZvmNmiBNilMurVbu62 uoRHZiCTu/yAg== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Stephen Boyd , Michael Turquette , Greg Kroah-Hartman , Gregory Clement Cc: =?utf-8?q?Pali_Roh=C3=A1r?= , linux-clk@vger.kernel.org, linux-serial@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , =?utf-8?q?Mare?= =?utf-8?q?k_Beh=C3=BAn?= Subject: [PATCH v8 2/6] dt-bindings: mvebu-uart: document DT bindings for marvell, armada-3700-uart-clock Date: Fri, 11 Feb 2022 20:12:34 +0100 Message-Id: <20220211191238.2142-3-kabel@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220211191238.2142-1-kabel@kernel.org> References: <20220211191238.2142-1-kabel@kernel.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220211_111303_363511_0BBA7361 X-CRM114-Status: GOOD ( 15.64 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Pali Rohár Add DT bindings documentation for device nodes with compatible string "marvell,armada-3700-uart-clock". Signed-off-by: Pali Rohár Reviewed-by: Rob Herring Reviewed-by: Marek Behún Signed-off-by: Marek Behún --- Changes since v7: - added Reviewed-by tags - changed commit message a little ("This change adds" -> "Add") - fixed lint errors in yaml binding file --- .../clock/marvell,armada-3700-uart-clock.yaml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/marvell,armada-3700-uart-clock.yaml diff --git a/Documentation/devicetree/bindings/clock/marvell,armada-3700-uart-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,armada-3700-uart-clock.yaml new file mode 100644 index 000000000000..175f5c8f2bc5 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,armada-3700-uart-clock.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/marvell,armada-3700-uart-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# +title: Marvell Armada 3720 UART clocks + +maintainers: + - Pali Rohár + +properties: + compatible: + const: marvell,armada-3700-uart-clock + + reg: + items: + - description: UART Clock Control Register + - description: UART 2 Baud Rate Divisor Register + + clocks: + description: | + List of parent clocks suitable for UART from following set: + "TBG-A-P", "TBG-B-P", "TBG-A-S", "TBG-B-S", "xtal" + UART clock can use one from this set and when more are provided + then kernel would choose and configure the most suitable one. + It is suggest to specify at least one TBG clock to achieve + baudrates above 230400 and also to specify clock which bootloader + used for UART (most probably xtal) for smooth boot log on UART. + + clock-names: + items: + - const: TBG-A-P + - const: TBG-B-P + - const: TBG-A-S + - const: TBG-B-S + - const: xtal + minItems: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + +additionalProperties: false + +examples: + - | + uartclk: clock-controller@12010 { + compatible = "marvell,armada-3700-uart-clock"; + reg = <0x12010 0x4>, <0x12210 0x4>; + clocks = <&tbg 0>, <&tbg 1>, <&tbg 2>, <&tbg 3>, <&xtalclk>; + clock-names = "TBG-A-P", "TBG-B-P", "TBG-A-S", "TBG-B-S", "xtal"; + #clock-cells = <1>; + }; From patchwork Fri Feb 11 19:12:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 12743779 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 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 smtp.lore.kernel.org (Postfix) with ESMTPS id D9E93C433F5 for ; Fri, 11 Feb 2022 19:15:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=jXb2P9STP0XpS6KOAQo8KSb8R20iOHAwW4o/YCjn4Jk=; b=xgNxQTJvVCGhlu wmEX0bLNsq/OYzDFm4kFyASwR6osz3SFQ/9cNLfZ0dpcuSYFwWfGscgd3CQIKuPoYwJHakNpVEWyP ZMwQcA5q9pg68HtvTIbcuvUNIy8GegfbQK+dIQAlnpGETG7nYo2k2PHDN9SefXq4QABu6hX/CoKGf hMgJNtlfRbuxWjfbFbTkyCYaaCrGyxV88LR21Gbm0ShHLYuKRN8nGDs8TwOh9BENI0/0NTyF83uth gmqaTgqX7nm7Z/pfCFfFb0jf8XlxBujrIWFs0sQDivvBmjovxujBaJ9/AG+PqbX/djPhDguTUwoJ/ /zZaI4FIeiJxwHME6MOQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbN3-008XFd-Gm; Fri, 11 Feb 2022 19:14:17 +0000 Received: from dfw.source.kernel.org ([2604:1380:4641:c500::1]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLr-008We9-6n for linux-arm-kernel@lists.infradead.org; Fri, 11 Feb 2022 19:13:08 +0000 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 AA89561F35; Fri, 11 Feb 2022 19:12:50 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4FC07C340F0; Fri, 11 Feb 2022 19:12:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1644606770; bh=/CHCxS/xFV985M7WTg7mDvHkZaKIjAXfsV3msKFVdgo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dDtEYva4tcV4zI1gSUcRQDln7MD3ZUpkvlBTiedMMpBQcgH2iJhWzlK8+lI9MXl4b qR9mfnLLRP1arT2xAcHKnUMDxN9RFha+hgdEG+iUvy9PJwZAIIvmuqd2vUGgCwPEWR g7aZqLOHr94IRrCaLYiGhKOw3kM9KkY1StrrWO7CHvS7b8rM+cV+VfKVCa5eaa/C7I 76bhcfvCGY/0NYK0G6Uxcqj1qH/u1P+tAlhSlRiAvxk51fNpRy3FeTTIF4ZCkhne7L SdtHIBfxvW7mzegqY6c7wIJ/RkVZUnMGtezyt900adZQf194RdIjboLsmy9YCR4/wM yrVqn27YXO/dQ== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Stephen Boyd , Michael Turquette , Greg Kroah-Hartman , Gregory Clement Cc: =?utf-8?q?Pali_Roh=C3=A1r?= , linux-clk@vger.kernel.org, linux-serial@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH v8 3/6] serial: mvebu-uart: implement UART clock driver for configuring UART base clock Date: Fri, 11 Feb 2022 20:12:35 +0100 Message-Id: <20220211191238.2142-4-kabel@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220211191238.2142-1-kabel@kernel.org> References: <20220211191238.2142-1-kabel@kernel.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220211_111303_441555_1ECC5822 X-CRM114-Status: GOOD ( 42.54 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Pali Rohár Implement a new device driver for controlling UART clocks on Marvell Armada 3700 SoC. This device driver is loaded for devices which match the compatible string "marvell,armada-3700-uart-clock". There are more pitfalls related to UART clocks: - both UARTs use same parent clock source (which can be xtal or one of the TBG clocks), - if a TBG clock is used as the parent clock, there are two additional divisors that can both be configured to divide the rate by 1, 2, ... 6, but these divisors are again shared between the two UART controllers on the SOC, - the configuration of the parent clock source and divisors is done in the address space of the first UART controller, UART1. Clocks can be gated separately for UART1 and UART2, but this setting also lives in the address space of UART1, - Marvell's Functional Specification for Armada 3720 document has the clock gating bits swapped, so the one described to gate UART1 clock actually gates UART2 and vice versa, - each UART has it's own "special divisor", and this uses the parent clock described above. These divisors are configure in each UART's address space separately. Thus the driver for UART2 controller needs to have access to UART1 address space, since UART1 address space contains some bits exclusive for UART2 and also some bits which are shared between UART1 and UART2. Also, during boot, when early console is active on one of the UARTs, and we want to switch parent clock from xtal (default) to TBG (to be more flexible with baudrates), the driver changing UART clocks also needs to be able to change the "special divisor", so that the baudrate of earlycon is not changed when swtiching to normal console. Thus the clock driver also needs to be able to access UART2 register space, for UART2's "special divisor". For these reasons, this new UART clock driver does not use ioremap_resource(), but only ioremap() to prevent resource conflicts between UART clock driver and UART driver. === We need to share only two 32-bit registers between the UART driver and the UART clock driver: - UART Clock Control - UART 2 Baud Rate Divisor Access to these two registers are protected by one spinlock to prevent any conflicts. Access is required only during probing, when changing baudrate or during suspend/resume. Hardware can be configured to use one of following clocks as UART parent clock: TBG-A-P, TBG-B-P, TBG-A-S, TBG-B-S, xtal. Not every clock is usable for higher buadrates. Any subset can be specified in the device-tree and the driver will choose the best one which also still supports the mandatory baudrate of 9600 Bd. For smooth boot log output it is needed to specify clock used by early console, otherwise garbage would be printed on UART during probe of UART clock driver and transitioning from early console to normal console. We are implementing this to be able to configure TBG clock as UART parent clock, which is required to be able to achieve higher baudrates than 230400 Bd. We achieve this by referencing this new UART clock device node in UART's device node. UART clock device driver automatically chooses the best clock source for UART driver. Until now, UART's device-tree node needed to reference one of the static clocks (xtal or one of the TBGs) as parent clock in the `clocks` phandle - the parent clock which was configured before booting the kernel. If bootloader changed UART's parent clock, it needed to change the `clocks` phandle in DTB correspondingly before booting. From now on both the old mechanism (xtal or TBG referenced as parent clock in `clocks` phandle) and the new one (UART clock referenced in the `clocks` phandle) are supported, to provide full backward compatibility with existing DTS files, full backward compatibility with existing boot loaders, and to provide new features (runtime clock configuration to allow higher baudrates than 230400 Bd). New features are available only with new DTS files. There was also a discussion about how the UART node and the clock-controller node could be wrapped together in a new binding [1, 2]. As explained there, this is not possible if we want to keep backwards compatibility with existing bootloaders, and thus we are doing this by putting the UART clock-controller node inside the UART1 node. [1] https://lore.kernel.org/linux-serial/20220120000651.in7s6nazif5qjkme@pali/ [2] https://lore.kernel.org/linux-serial/20220125204006.A6D09C340E0@smtp.kernel.org/ Signed-off-by: Pali Rohár Reviewed-by: Marek Behún Signed-off-by: Marek Behún --- Changes since v7: - commit message was rewritten - changed comments a little - fixed indentation at some places - added Marek's Reviewed-by tag --- drivers/tty/serial/Kconfig | 1 + drivers/tty/serial/mvebu-uart.c | 521 +++++++++++++++++++++++++++++++- 2 files changed, 520 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 0e5ccb25bdb1..9bc1325dd7ba 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1445,6 +1445,7 @@ config SERIAL_STM32_CONSOLE config SERIAL_MVEBU_UART bool "Marvell EBU serial port support" depends on ARCH_MVEBU || COMPILE_TEST + depends on COMMON_CLK select SERIAL_CORE help This driver is for Marvell EBU SoC's UART. If you have a machine diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index ab226da75f7b..56278b29f5f5 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -8,12 +8,14 @@ */ #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -68,8 +70,31 @@ #define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR \ | STAT_PAR_ERR | STAT_OVR_ERR) +/* + * Marvell Armada 3700 Functional Specifications describes that bit 21 of UART + * Clock Control register controls UART1 and bit 20 controls UART2. But in + * reality bit 21 controls UART2 and bit 20 controls UART1. This seems to be an + * error in Marvell's documentation. Hence following CLK_DIS macros are swapped. + */ + #define UART_BRDV 0x10 +/* These bits are located in UART1 address space and control UART2 */ +#define UART2_CLK_DIS BIT(21) +/* These bits are located in UART1 address space and control UART1 */ +#define UART1_CLK_DIS BIT(20) +/* These bits are located in UART1 address space and control both UARTs */ +#define CLK_NO_XTAL BIT(19) +#define CLK_TBG_DIV1_SHIFT 15 +#define CLK_TBG_DIV1_MASK 0x7 +#define CLK_TBG_DIV1_MAX 6 +#define CLK_TBG_DIV2_SHIFT 12 +#define CLK_TBG_DIV2_MASK 0x7 +#define CLK_TBG_DIV2_MAX 6 +#define CLK_TBG_SEL_SHIFT 10 +#define CLK_TBG_SEL_MASK 0x3 +/* These bits are located in both UARTs address space */ #define BRDV_BAUD_MASK 0x3FF +#define BRDV_BAUD_MAX BRDV_BAUD_MASK #define UART_OSAMP 0x14 #define OSAMP_DEFAULT_DIVISOR 16 @@ -153,6 +178,8 @@ static struct mvebu_uart *to_mvuart(struct uart_port *port) static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS]; +static DEFINE_SPINLOCK(mvebu_uart_lock); + /* Core UART Driver Operations */ static unsigned int mvebu_uart_tx_empty(struct uart_port *port) { @@ -445,6 +472,7 @@ static void mvebu_uart_shutdown(struct uart_port *port) static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) { unsigned int d_divisor, m_divisor; + unsigned long flags; u32 brdv, osamp; if (!port->uartclk) @@ -463,10 +491,12 @@ static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) m_divisor = OSAMP_DEFAULT_DIVISOR; d_divisor = DIV_ROUND_CLOSEST(port->uartclk, baud * m_divisor); + spin_lock_irqsave(&mvebu_uart_lock, flags); brdv = readl(port->membase + UART_BRDV); brdv &= ~BRDV_BAUD_MASK; brdv |= d_divisor; writel(brdv, port->membase + UART_BRDV); + spin_unlock_irqrestore(&mvebu_uart_lock, flags); osamp = readl(port->membase + UART_OSAMP); osamp &= ~OSAMP_DIVISORS_MASK; @@ -762,6 +792,7 @@ static int mvebu_uart_suspend(struct device *dev) { struct mvebu_uart *mvuart = dev_get_drvdata(dev); struct uart_port *port = mvuart->port; + unsigned long flags; uart_suspend_port(&mvebu_uart_driver, port); @@ -770,7 +801,9 @@ static int mvebu_uart_suspend(struct device *dev) mvuart->pm_regs.ctrl = readl(port->membase + UART_CTRL(port)); mvuart->pm_regs.intr = readl(port->membase + UART_INTR(port)); mvuart->pm_regs.stat = readl(port->membase + UART_STAT); + spin_lock_irqsave(&mvebu_uart_lock, flags); mvuart->pm_regs.brdv = readl(port->membase + UART_BRDV); + spin_unlock_irqrestore(&mvebu_uart_lock, flags); mvuart->pm_regs.osamp = readl(port->membase + UART_OSAMP); device_set_wakeup_enable(dev, true); @@ -782,13 +815,16 @@ static int mvebu_uart_resume(struct device *dev) { struct mvebu_uart *mvuart = dev_get_drvdata(dev); struct uart_port *port = mvuart->port; + unsigned long flags; writel(mvuart->pm_regs.rbr, port->membase + UART_RBR(port)); writel(mvuart->pm_regs.tsh, port->membase + UART_TSH(port)); writel(mvuart->pm_regs.ctrl, port->membase + UART_CTRL(port)); writel(mvuart->pm_regs.intr, port->membase + UART_INTR(port)); writel(mvuart->pm_regs.stat, port->membase + UART_STAT); + spin_lock_irqsave(&mvebu_uart_lock, flags); writel(mvuart->pm_regs.brdv, port->membase + UART_BRDV); + spin_unlock_irqrestore(&mvebu_uart_lock, flags); writel(mvuart->pm_regs.osamp, port->membase + UART_OSAMP); uart_resume_port(&mvebu_uart_driver, port); @@ -972,6 +1008,478 @@ static struct platform_driver mvebu_uart_platform_driver = { }, }; +/* This code is based on clk-fixed-factor.c driver and modified. */ + +struct mvebu_uart_clock { + struct clk_hw clk_hw; + int clock_idx; + u32 pm_context_reg1; + u32 pm_context_reg2; +}; + +struct mvebu_uart_clock_base { + struct mvebu_uart_clock clocks[2]; + unsigned int parent_rates[5]; + int parent_idx; + unsigned int div; + void __iomem *reg1; + void __iomem *reg2; + bool configured; +}; + +#define PARENT_CLOCK_XTAL 4 + +#define to_uart_clock(hw) container_of(hw, struct mvebu_uart_clock, clk_hw) +#define to_uart_clock_base(uart_clock) container_of(uart_clock, \ + struct mvebu_uart_clock_base, clocks[uart_clock->clock_idx]) + +static int mvebu_uart_clock_prepare(struct clk_hw *hw) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + unsigned int prev_clock_idx, prev_clock_rate, prev_d1d2; + unsigned int parent_clock_idx, parent_clock_rate; + unsigned long flags; + unsigned int d1, d2; + u64 divisor; + u32 val; + + /* + * This function just reconfigures UART Clock Control register (located + * in UART1 address space which controls both UART1 and UART2) to + * selected UART base clock and recalculates current UART1/UART2 + * divisors in their address spaces, so that final baudrate will not be + * changed by switching UART parent clock. This is required for + * otherwise kernel's boot log stops working - we need to ensure that + * UART baudrate does not change during this setup. It is a one time + * operation, it will execute only once and set `configured` to true, + * and be skipped on subsequent calls. Because this UART Clock Control + * register (UART_BRDV) is shared between UART1 baudrate function, + * UART1 clock selector and UART2 clock selector, every access to + * UART_BRDV (reg1) needs to be protected by a lock. + */ + + spin_lock_irqsave(&mvebu_uart_lock, flags); + + if (uart_clock_base->configured) { + spin_unlock_irqrestore(&mvebu_uart_lock, flags); + return 0; + } + + parent_clock_idx = uart_clock_base->parent_idx; + parent_clock_rate = uart_clock_base->parent_rates[parent_clock_idx]; + + val = readl(uart_clock_base->reg1); + + if (uart_clock_base->div > CLK_TBG_DIV1_MAX) { + d1 = CLK_TBG_DIV1_MAX; + d2 = uart_clock_base->div / CLK_TBG_DIV1_MAX; + } else { + d1 = uart_clock_base->div; + d2 = 1; + } + + if (val & CLK_NO_XTAL) { + prev_clock_idx = (val >> CLK_TBG_SEL_SHIFT) & CLK_TBG_SEL_MASK; + prev_d1d2 = ((val >> CLK_TBG_DIV1_SHIFT) & CLK_TBG_DIV1_MASK) * + ((val >> CLK_TBG_DIV2_SHIFT) & CLK_TBG_DIV2_MASK); + } else { + prev_clock_idx = PARENT_CLOCK_XTAL; + prev_d1d2 = 1; + } + + /* Note that uart_clock_base->parent_rates[i] may not be available */ + prev_clock_rate = uart_clock_base->parent_rates[prev_clock_idx]; + + /* Recalculate UART1 divisor so UART1 baudrate does not change */ + if (prev_clock_rate) { + divisor = DIV_U64_ROUND_CLOSEST((u64)(val & BRDV_BAUD_MASK) * + parent_clock_rate * prev_d1d2, + prev_clock_rate * d1 * d2); + if (divisor < 1) + divisor = 1; + else if (divisor > BRDV_BAUD_MAX) + divisor = BRDV_BAUD_MAX; + val = (val & ~BRDV_BAUD_MASK) | divisor; + } + + if (parent_clock_idx != PARENT_CLOCK_XTAL) { + /* Do not use XTAL, select TBG clock and TBG d1 * d2 divisors */ + val |= CLK_NO_XTAL; + val &= ~(CLK_TBG_DIV1_MASK << CLK_TBG_DIV1_SHIFT); + val |= d1 << CLK_TBG_DIV1_SHIFT; + val &= ~(CLK_TBG_DIV2_MASK << CLK_TBG_DIV2_SHIFT); + val |= d2 << CLK_TBG_DIV2_SHIFT; + val &= ~(CLK_TBG_SEL_MASK << CLK_TBG_SEL_SHIFT); + val |= parent_clock_idx << CLK_TBG_SEL_SHIFT; + } else { + /* Use XTAL, TBG bits are then ignored */ + val &= ~CLK_NO_XTAL; + } + + writel(val, uart_clock_base->reg1); + + /* Recalculate UART2 divisor so UART2 baudrate does not change */ + if (prev_clock_rate) { + val = readl(uart_clock_base->reg2); + divisor = DIV_U64_ROUND_CLOSEST((u64)(val & BRDV_BAUD_MASK) * + parent_clock_rate * prev_d1d2, + prev_clock_rate * d1 * d2); + if (divisor < 1) + divisor = 1; + else if (divisor > BRDV_BAUD_MAX) + divisor = BRDV_BAUD_MAX; + val = (val & ~BRDV_BAUD_MASK) | divisor; + writel(val, uart_clock_base->reg2); + } + + uart_clock_base->configured = true; + + spin_unlock_irqrestore(&mvebu_uart_lock, flags); + + return 0; +} + +static int mvebu_uart_clock_enable(struct clk_hw *hw) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&mvebu_uart_lock, flags); + + val = readl(uart_clock_base->reg1); + + if (uart_clock->clock_idx == 0) + val &= ~UART1_CLK_DIS; + else + val &= ~UART2_CLK_DIS; + + writel(val, uart_clock_base->reg1); + + spin_unlock_irqrestore(&mvebu_uart_lock, flags); + + return 0; +} + +static void mvebu_uart_clock_disable(struct clk_hw *hw) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&mvebu_uart_lock, flags); + + val = readl(uart_clock_base->reg1); + + if (uart_clock->clock_idx == 0) + val |= UART1_CLK_DIS; + else + val |= UART2_CLK_DIS; + + writel(val, uart_clock_base->reg1); + + spin_unlock_irqrestore(&mvebu_uart_lock, flags); +} + +static int mvebu_uart_clock_is_enabled(struct clk_hw *hw) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + u32 val; + + val = readl(uart_clock_base->reg1); + + if (uart_clock->clock_idx == 0) + return !(val & UART1_CLK_DIS); + else + return !(val & UART2_CLK_DIS); +} + +static int mvebu_uart_clock_save_context(struct clk_hw *hw) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + unsigned long flags; + + spin_lock_irqsave(&mvebu_uart_lock, flags); + uart_clock->pm_context_reg1 = readl(uart_clock_base->reg1); + uart_clock->pm_context_reg2 = readl(uart_clock_base->reg2); + spin_unlock_irqrestore(&mvebu_uart_lock, flags); + + return 0; +} + +static void mvebu_uart_clock_restore_context(struct clk_hw *hw) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + unsigned long flags; + + spin_lock_irqsave(&mvebu_uart_lock, flags); + writel(uart_clock->pm_context_reg1, uart_clock_base->reg1); + writel(uart_clock->pm_context_reg2, uart_clock_base->reg2); + spin_unlock_irqrestore(&mvebu_uart_lock, flags); +} + +static unsigned long mvebu_uart_clock_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + + return parent_rate / uart_clock_base->div; +} + +static long mvebu_uart_clock_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct mvebu_uart_clock *uart_clock = to_uart_clock(hw); + struct mvebu_uart_clock_base *uart_clock_base = + to_uart_clock_base(uart_clock); + + return *parent_rate / uart_clock_base->div; +} + +static int mvebu_uart_clock_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + /* + * We must report success but we can do so unconditionally because + * mvebu_uart_clock_round_rate returns values that ensure this call is a + * nop. + */ + + return 0; +} + +static const struct clk_ops mvebu_uart_clock_ops = { + .prepare = mvebu_uart_clock_prepare, + .enable = mvebu_uart_clock_enable, + .disable = mvebu_uart_clock_disable, + .is_enabled = mvebu_uart_clock_is_enabled, + .save_context = mvebu_uart_clock_save_context, + .restore_context = mvebu_uart_clock_restore_context, + .round_rate = mvebu_uart_clock_round_rate, + .set_rate = mvebu_uart_clock_set_rate, + .recalc_rate = mvebu_uart_clock_recalc_rate, +}; + +static int mvebu_uart_clock_register(struct device *dev, + struct mvebu_uart_clock *uart_clock, + const char *name, + const char *parent_name) +{ + struct clk_init_data init = { }; + + uart_clock->clk_hw.init = &init; + + init.name = name; + init.ops = &mvebu_uart_clock_ops; + init.flags = 0; + init.num_parents = 1; + init.parent_names = &parent_name; + + return devm_clk_hw_register(dev, &uart_clock->clk_hw); +} + +static int mvebu_uart_clock_probe(struct platform_device *pdev) +{ + static const char *const uart_clk_names[] = { "uart_1", "uart_2" }; + static const char *const parent_clk_names[] = { "TBG-A-P", "TBG-B-P", + "TBG-A-S", "TBG-B-S", + "xtal" }; + struct clk *parent_clks[ARRAY_SIZE(parent_clk_names)]; + struct mvebu_uart_clock_base *uart_clock_base; + struct clk_hw_onecell_data *hw_clk_data; + struct device *dev = &pdev->dev; + int i, parent_clk_idx, ret; + unsigned long div, rate; + struct resource *res; + unsigned int d1, d2; + + BUILD_BUG_ON(ARRAY_SIZE(uart_clk_names) != + ARRAY_SIZE(uart_clock_base->clocks)); + BUILD_BUG_ON(ARRAY_SIZE(parent_clk_names) != + ARRAY_SIZE(uart_clock_base->parent_rates)); + + uart_clock_base = devm_kzalloc(dev, + sizeof(*uart_clock_base), + GFP_KERNEL); + if (!uart_clock_base) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Couldn't get first register\n"); + return -ENOENT; + } + + /* + * UART Clock Control register (reg1 / UART_BRDV) is in the address + * space of UART1 (standard UART variant), controls parent clock and + * dividers for both UART1 and UART2 and is supplied via DT as the first + * resource. Therefore use ioremap() rather than ioremap_resource() to + * avoid conflicts with UART1 driver. Access to UART_BRDV is protected + * by a lock shared between clock and UART driver. + */ + uart_clock_base->reg1 = devm_ioremap(dev, res->start, + resource_size(res)); + if (IS_ERR(uart_clock_base->reg1)) + return PTR_ERR(uart_clock_base->reg1); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "Couldn't get second register\n"); + return -ENOENT; + } + + /* + * UART 2 Baud Rate Divisor register (reg2 / UART_BRDV) is in address + * space of UART2 (extended UART variant), controls only one UART2 + * specific divider and is supplied via DT as second resource. + * Therefore use ioremap() rather than ioremap_resource() to avoid + * conflicts with UART2 driver. Access to UART_BRDV is protected by a + * by lock shared between clock and UART driver. + */ + uart_clock_base->reg2 = devm_ioremap(dev, res->start, + resource_size(res)); + if (IS_ERR(uart_clock_base->reg2)) + return PTR_ERR(uart_clock_base->reg2); + + hw_clk_data = devm_kzalloc(dev, + struct_size(hw_clk_data, hws, + ARRAY_SIZE(uart_clk_names)), + GFP_KERNEL); + if (!hw_clk_data) + return -ENOMEM; + + hw_clk_data->num = ARRAY_SIZE(uart_clk_names); + for (i = 0; i < ARRAY_SIZE(uart_clk_names); i++) { + hw_clk_data->hws[i] = &uart_clock_base->clocks[i].clk_hw; + uart_clock_base->clocks[i].clock_idx = i; + } + + parent_clk_idx = -1; + + for (i = 0; i < ARRAY_SIZE(parent_clk_names); i++) { + parent_clks[i] = devm_clk_get(dev, parent_clk_names[i]); + if (IS_ERR(parent_clks[i])) { + if (PTR_ERR(parent_clks[i]) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_warn(dev, "Couldn't get the parent clock %s: %ld\n", + parent_clk_names[i], PTR_ERR(parent_clks[i])); + continue; + } + + ret = clk_prepare_enable(parent_clks[i]); + if (ret) { + dev_warn(dev, "Couldn't enable parent clock %s: %d\n", + parent_clk_names[i], ret); + continue; + } + rate = clk_get_rate(parent_clks[i]); + uart_clock_base->parent_rates[i] = rate; + + if (i != PARENT_CLOCK_XTAL) { + /* + * Calculate the smallest TBG d1 and d2 divisors that + * still can provide 9600 baudrate. + */ + d1 = DIV_ROUND_UP(rate, 9600 * OSAMP_DEFAULT_DIVISOR * + BRDV_BAUD_MAX); + if (d1 < 1) + d1 = 1; + else if (d1 > CLK_TBG_DIV1_MAX) + d1 = CLK_TBG_DIV1_MAX; + + d2 = DIV_ROUND_UP(rate, 9600 * OSAMP_DEFAULT_DIVISOR * + BRDV_BAUD_MAX * d1); + if (d2 < 1) + d2 = 1; + else if (d2 > CLK_TBG_DIV2_MAX) + d2 = CLK_TBG_DIV2_MAX; + } else { + /* + * When UART clock uses XTAL clock as a source then it + * is not possible to use d1 and d2 divisors. + */ + d1 = d2 = 1; + } + + /* Skip clock source which cannot provide 9600 baudrate */ + if (rate > 9600 * OSAMP_DEFAULT_DIVISOR * BRDV_BAUD_MAX * d1 * + d2) + continue; + + /* + * Choose TBG clock source with the smallest divisors. Use XTAL + * clock source only in case TBG is not available as XTAL cannot + * be used for baudrates higher than 230400. + */ + if (parent_clk_idx == -1 || + (i != PARENT_CLOCK_XTAL && div > d1 * d2)) { + parent_clk_idx = i; + div = d1 * d2; + } + } + + for (i = 0; i < ARRAY_SIZE(parent_clk_names); i++) { + if (i == parent_clk_idx || IS_ERR(parent_clks[i])) + continue; + clk_disable_unprepare(parent_clks[i]); + devm_clk_put(dev, parent_clks[i]); + } + + if (parent_clk_idx == -1) { + dev_err(dev, "No usable parent clock\n"); + return -ENOENT; + } + + uart_clock_base->parent_idx = parent_clk_idx; + uart_clock_base->div = div; + + dev_notice(dev, "Using parent clock %s as base UART clock\n", + __clk_get_name(parent_clks[parent_clk_idx])); + + for (i = 0; i < ARRAY_SIZE(uart_clk_names); i++) { + ret = mvebu_uart_clock_register(dev, + &uart_clock_base->clocks[i], + uart_clk_names[i], + __clk_get_name(parent_clks[parent_clk_idx])); + if (ret) { + dev_err(dev, "Can't register UART clock %d: %d\n", + i, ret); + return ret; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + hw_clk_data); +} + +static const struct of_device_id mvebu_uart_clock_of_match[] = { + { .compatible = "marvell,armada-3700-uart-clock", }, + { } +}; + +static struct platform_driver mvebu_uart_clock_platform_driver = { + .probe = mvebu_uart_clock_probe, + .driver = { + .name = "mvebu-uart-clock", + .of_match_table = mvebu_uart_clock_of_match, + }, +}; + static int __init mvebu_uart_init(void) { int ret; @@ -980,10 +1488,19 @@ static int __init mvebu_uart_init(void) if (ret) return ret; + ret = platform_driver_register(&mvebu_uart_clock_platform_driver); + if (ret) { + uart_unregister_driver(&mvebu_uart_driver); + return ret; + } + ret = platform_driver_register(&mvebu_uart_platform_driver); - if (ret) + if (ret) { + platform_driver_unregister(&mvebu_uart_clock_platform_driver); uart_unregister_driver(&mvebu_uart_driver); + return ret; + } - return ret; + return 0; } arch_initcall(mvebu_uart_init); From patchwork Fri Feb 11 19:12:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 12743776 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 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 7E066C433EF for ; Fri, 11 Feb 2022 19:14:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=wHnFhO8YoQt4/qKzYY1HfOK9wpzppNRJx8NzpAPfxQs=; b=qLAdcE6++SsiT4 U6JPSWCOM+kdeg4yTI/bSU8cyeXfH735sEMLWnldm0+4c9sTBHtcrw6rVsBCKnk6G6BUCmtkJ5OT+ ejD8bFD2QylyriD13yGLutD2t8LZDcDb7A+5dxITbpCNNMjC5qs+/myH/B+UsMHmyxl3VjvQcidoU cf+2AfHhB/wY5Hq6sy+J9ZX1lBYewjc5F0YmicXVldDVl8gBL+DK3WWPb4CXB516hIK/pFdQHtFFL Qaigf7xKX+6yByYZsM/7tKmCJmsvqB0e1rYg7FdxyyQ9DCzcUgl5leukhiTA01Tp6az93EVBcq8gF K5yvjp2FzQHKRR4yz4Aw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbMB-008WqU-KN; Fri, 11 Feb 2022 19:13:23 +0000 Received: from dfw.source.kernel.org ([2604:1380:4641:c500::1]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLr-008Wf0-6o for linux-arm-kernel@lists.infradead.org; Fri, 11 Feb 2022 19:13:04 +0000 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 B4C2D61F3F; Fri, 11 Feb 2022 19:12:53 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id EA818C340F2; Fri, 11 Feb 2022 19:12:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1644606773; bh=z4QJvv5jQOMSoBELgUEfc+t5uAq/ZocWJT4vzhJHC9k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mxR3FEwiw1/IApZN1xx2o1TK2U5T8LLeDp/nMfkHN5Rt7kvQ0Xvy8O+MMaXiDqJ2w aJmZQRzxAgICQrM17BgxiDE1GSDHy3H3ht7MaCXryHOnHF0HzKh7kEm2lHLq4oUFCg 21LxHUB53Uqm/orGNYQD0s5cXZomoIrurhEQZB2eUrfEnCJ6vsNp0b3kvfQ8BYeshb PYJt9QQMOl9ZSlDMSyIwJWvc0pKvPmYDQ1kF7qQH7C3zJhGvTf3zdDN1XiXhV0M7Wy IKNMRtztTz5/7oA6WO09ad3Nv3iawWzzuoHMg9VfF7tdoAhEwhwKcJ1+7TUrKbdrw3 tYF1Fu7dtqFfg== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Stephen Boyd , Michael Turquette , Greg Kroah-Hartman , Gregory Clement Cc: =?utf-8?q?Pali_Roh=C3=A1r?= , linux-clk@vger.kernel.org, linux-serial@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , =?utf-8?q?Mare?= =?utf-8?q?k_Beh=C3=BAn?= Subject: [PATCH v8 4/6] dt-bindings: mvebu-uart: update information about UART clock Date: Fri, 11 Feb 2022 20:12:36 +0100 Message-Id: <20220211191238.2142-5-kabel@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220211191238.2142-1-kabel@kernel.org> References: <20220211191238.2142-1-kabel@kernel.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220211_111303_325116_B3AB3957 X-CRM114-Status: GOOD ( 14.27 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Pali Rohár Device "marvell,armada-3700-uart" should use "marvell,armada-3700-uart-clock" compatible clock. Signed-off-by: Pali Rohár Reviewed-by: Rob Herring Reviewed-by: Marek Behún Signed-off-by: Marek Behún --- Changes since v7: - added Reviewed-by tags --- Documentation/devicetree/bindings/serial/mvebu-uart.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/mvebu-uart.txt b/Documentation/devicetree/bindings/serial/mvebu-uart.txt index 2d0dbdf32d1d..a062bbca532c 100644 --- a/Documentation/devicetree/bindings/serial/mvebu-uart.txt +++ b/Documentation/devicetree/bindings/serial/mvebu-uart.txt @@ -14,7 +14,10 @@ Required properties: is provided (possible only with the "marvell,armada-3700-uart" compatible string for backward compatibility), it will only work if the baudrate was initialized by the bootloader and no baudrate - change will then be possible. + change will then be possible. When provided it should be UART1-clk + for standard variant of UART and UART2-clk for extended variant + of UART. TBG clock (with UART TBG divisors d1=d2=1) or xtal clock + should not be used and are supported only for backward compatibility. - interrupts: - Must contain three elements for the standard variant of the IP (marvell,armada-3700-uart): "uart-sum", "uart-tx" and "uart-rx", @@ -34,7 +37,7 @@ Example: uart0: serial@12000 { compatible = "marvell,armada-3700-uart"; reg = <0x12000 0x18>; - clocks = <&xtalclk>; + clocks = <&uartclk 0>; interrupts = , , @@ -45,7 +48,7 @@ Example: uart1: serial@12200 { compatible = "marvell,armada-3700-uart-ext"; reg = <0x12200 0x30>; - clocks = <&xtalclk>; + clocks = <&uartclk 1>; interrupts = , ; From patchwork Fri Feb 11 19:12:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 12743775 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 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 00EBBC433F5 for ; Fri, 11 Feb 2022 19:14:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=vTBthuwslCEbT3qJQme3QEWLjTOPu1AVGmP5YizdnGs=; b=ygqZOwUbI9ggVg Wc/TKOlFD8K4NEqhAb2bzBzpFrZdVDu2Ka4OrdsJBNZU1YGaFBuU9Wk/fZ+XUlMrw5ETC5IqcJmBr tpvcyzJp1TGPcKlM+6NNCGwkjoGjJx8jKBE0UuPJz9Dyuc9qZWaFNZqNJDC+piPdoVbL+jUR/zhJ8 8GRn+6DjTPazgAKnKPpe3Wnb6277hcgSC7yfS/zr8hEV32JNsi4yLPqIbaL5SdS/RrQInFXv2WRp0 UL/f7TMbGLb+JZUA9fu7nFv4nXP1NV2AICiJ2CJxyqUWHE0XTp+80N2BW1iGm2lvM+vXYd7BaorYd 7HyW5FnEh0UtQLQ3gVQg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLx-008Wk4-K7; Fri, 11 Feb 2022 19:13:09 +0000 Received: from dfw.source.kernel.org ([139.178.84.217]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLk-008WfX-7l for linux-arm-kernel@lists.infradead.org; Fri, 11 Feb 2022 19:12:57 +0000 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 BD84161F3B; Fri, 11 Feb 2022 19:12:55 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8EC34C340F1; Fri, 11 Feb 2022 19:12:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1644606775; bh=icDAxhIilkMcIBD/iE5/3SiYgSSWiOTuHCkrhxzg/7M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jBzHpQ5pdB0MztPObMedFoQYSkox1wWNSoNrPLoB0nNDR35MVSlsgv+pOSyFpp/IK oM3CSNSjYuenhrlMbUdGwfWp1QC1kkJNnrJdPmUsk8oOPAy+mjMW4pSW3ks6q0sKit NUpq4DZKVxjHKmZdHeqYObqM4XAKvUlUAIYndC1YkKlHgWKzfqtWFSclQWTQXjLLvn oq/DpY3BEyv8vF9lK6ovNagwkYk6rvkT6y9Bbe4if0Bv6PajxQklOvutFvXUUkMw94 J65CVQxFaghRpTsGsgsWH2GoiMyNj6juzzFLTWSl6t34AXFPHItlRiTfL/XWfz2pQr K5QmJmhAycRNQ== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Stephen Boyd , Michael Turquette , Greg Kroah-Hartman , Gregory Clement Cc: =?utf-8?q?Pali_Roh=C3=A1r?= , linux-clk@vger.kernel.org, linux-serial@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH v8 5/6] arm64: dts: marvell: armada-37xx: add device node for UART clock and use it Date: Fri, 11 Feb 2022 20:12:37 +0100 Message-Id: <20220211191238.2142-6-kabel@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220211191238.2142-1-kabel@kernel.org> References: <20220211191238.2142-1-kabel@kernel.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220211_111256_341416_56306CB8 X-CRM114-Status: GOOD ( 11.32 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Pali Rohár Define DT node for UART clock "marvell,armada-3700-uart-clock" and use this UART clock as a base clock for all UART devices. Signed-off-by: Pali Rohár Reviewed-by: Marek Behún Signed-off-by: Marek Behún Acked-by: Gregory CLEMENT --- Changes since v7: - changed commit message ("This change defines" -> "Define") - added Marek's Reviewed-by tag --- arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi index 673f4906eef9..4cf6c8aa0ac2 100644 --- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi @@ -132,10 +132,20 @@ avs: avs@11500 { reg = <0x11500 0x40>; }; + uartclk: clock-controller@12010 { + compatible = "marvell,armada-3700-uart-clock"; + reg = <0x12010 0x4>, <0x12210 0x4>; + clocks = <&tbg 0>, <&tbg 1>, <&tbg 2>, + <&tbg 3>, <&xtalclk>; + clock-names = "TBG-A-P", "TBG-B-P", "TBG-A-S", + "TBG-B-S", "xtal"; + #clock-cells = <1>; + }; + uart0: serial@12000 { compatible = "marvell,armada-3700-uart"; reg = <0x12000 0x18>; - clocks = <&xtalclk>; + clocks = <&uartclk 0>; interrupts = , , @@ -147,7 +157,7 @@ uart0: serial@12000 { uart1: serial@12200 { compatible = "marvell,armada-3700-uart-ext"; reg = <0x12200 0x30>; - clocks = <&xtalclk>; + clocks = <&uartclk 1>; interrupts = , ; From patchwork Fri Feb 11 19:12:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 12743778 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 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 03560C433EF for ; Fri, 11 Feb 2022 19:15:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=unlOF3PDrz/ktq3ywr9M9t1ks4GpeQXA6wJtzREGYkY=; b=vQL/1Am6S6mWYX UOM+5p0otKiY6affVaYDdG/XH/MCgmOpizQndpP+As7aUvdpas2lkK3yDaUky1GFCECkX9/ZW+xlT /3zuTFJdy7KRCW+0mjof/M6lhMr+lhZsKCuhKIijbTJnncTH+d0cPNPn3+LPEoyPiEpG+vtQjApd1 F1zwQ6YsX46KcWAwWYX7vGgKkTe+nVQyXy8QrSedz7Ax241W2Os6vGTIEAnbo3rfFIJPO0z/jf+Lg DgCLLquEO9SLjfSkWhoyvYMyDkzG/2Pquh5cmqYFNw3sBMiItaRmNmdjTaPqbCIbggyiH/06mfVO4 j4y6USi+pETvNa0sNMeg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbMf-008X2O-UN; Fri, 11 Feb 2022 19:13:54 +0000 Received: from dfw.source.kernel.org ([2604:1380:4641:c500::1]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nIbLr-008Wgh-6m for linux-arm-kernel@lists.infradead.org; Fri, 11 Feb 2022 19:13:06 +0000 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 9869061F3B; Fri, 11 Feb 2022 19:12:58 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 086E8C340E9; Fri, 11 Feb 2022 19:12:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1644606778; bh=+AeaotqsM8rM5bOHFOpDOXMyO/XVaJ+vOedzjI39y7Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kEgi6NKeSbfB0okv7KmfDi7SnjYOstpC5RNvMQPPMsPXv03xkosCy08F5z0aiMY5N LpaDMbXLDcM+JJD4deyrCmdZDZQnCLto/jqY4UEeubuWHyVqWictP8/FsfT8xzDHhD G6CdSmZT0DaFDWjCdZhkHLdSAhv1gy5oDYvdzdv7Vk/KMK2XykSLq1JRoX+47i/4W5 8CkvA2Tto4GJzBQPqSa8klzf0VlXv9egGzhUtJypAc7jTw4BS3x9gf3/svZjUbUdP3 rsP0wA/eOl2a9IOKZpezr07l/1GBBWwOo85yktNCsWarLrtcO1lWY1HAELak50o0y2 FLOKdTJhOKDCQ== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Stephen Boyd , Michael Turquette , Greg Kroah-Hartman , Gregory Clement Cc: =?utf-8?q?Pali_Roh=C3=A1r?= , linux-clk@vger.kernel.org, linux-serial@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH v8 6/6] serial: mvebu-uart: implement support for baudrates higher than 230400 Bd Date: Fri, 11 Feb 2022 20:12:38 +0100 Message-Id: <20220211191238.2142-7-kabel@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220211191238.2142-1-kabel@kernel.org> References: <20220211191238.2142-1-kabel@kernel.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220211_111303_421401_50E2CBF3 X-CRM114-Status: GOOD ( 26.16 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Pali Rohár Implement simple usage of fractional divisor. When main divisor D is too large to represent requested baudrate then use divisor M from the fractional divisor feature. All the M prescalers are set to the same and maximal value 63, so the fractional part of the fractional divisor is not used at all. We also determine upper limit for possible baudrates. Experiments show that UART at baudrate 1500000 Bd with this configuration is stable. So there is no need to implement complicated calculation of fractional coefficients yet. To use this feature with higher baudrates, it is required to use UART clock provided by UART clock driver. Default boot xtal clock is not capable of higher baudrates. Signed-off-by: Pali Rohár Reviewed-by: Marek Behún Signed-off-by: Marek Behún --- Changes since v7: - change commit message a little - fixed indentation at some places - added Marek's Reviewed-by tag --- drivers/tty/serial/mvebu-uart.c | 83 ++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 56278b29f5f5..97563a2b9f75 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -99,6 +99,7 @@ #define UART_OSAMP 0x14 #define OSAMP_DEFAULT_DIVISOR 16 #define OSAMP_DIVISORS_MASK 0x3F3F3F3F +#define OSAMP_MAX_DIVISOR 63 #define MVEBU_NR_UARTS 2 @@ -479,18 +480,60 @@ static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) return -EOPNOTSUPP; /* - * The baudrate is derived from the UART clock thanks to two divisors: - * > D ("baud generator"): can divide the clock from 2 to 2^10 - 1. - * > M ("fractional divisor"): allows a better accuracy for - * baudrates higher than 230400. + * The baudrate is derived from the UART clock thanks to divisors: + * > d1 * d2 ("TBG divisors"): can divide only TBG clock from 1 to 6 + * > D ("baud generator"): can divide the clock from 1 to 1023 + * > M ("fractional divisor"): allows a better accuracy (from 1 to 63) * - * As the derivation of M is rather complicated, the code sticks to its - * default value (x16) when all the prescalers are zeroed, and only - * makes use of D to configure the desired baudrate. + * Exact formulas for calculating baudrate: + * + * with default x16 scheme: + * baudrate = xtal / (d * 16) + * baudrate = tbg / (d1 * d2 * d * 16) + * + * with fractional divisor: + * baudrate = 10 * xtal / (d * (3 * (m1 + m2) + 2 * (m3 + m4))) + * baudrate = 10 * tbg / (d1*d2 * d * (3 * (m1 + m2) + 2 * (m3 + m4))) + * + * Oversampling value: + * osamp = (m1 << 0) | (m2 << 8) | (m3 << 16) | (m4 << 24); + * + * Where m1 controls number of clock cycles per bit for bits 1,2,3; + * m2 for bits 4,5,6; m3 for bits 7,8 and m4 for bits 9,10. + * + * To simplify baudrate setup set all the M prescalers to the same + * value. For baudrates 9600 Bd and higher, it is enough to use the + * default (x16) divisor or fractional divisor with M = 63, so there + * is no need to use real fractional support (where the M prescalers + * are not equal). + * + * When all the M prescalers are zeroed then default (x16) divisor is + * used. Default x16 scheme is more stable than M (fractional divisor), + * so use M only when D divisor is not enough to derive baudrate. + * + * Member port->uartclk is either xtal clock rate or TBG clock rate + * divided by (d1 * d2). So d1 and d2 are already set by the UART clock + * driver (and UART driver itself cannot change them). Moreover they are + * shared between both UARTs. */ + m_divisor = OSAMP_DEFAULT_DIVISOR; d_divisor = DIV_ROUND_CLOSEST(port->uartclk, baud * m_divisor); + if (d_divisor > BRDV_BAUD_MAX) { + /* + * Experiments show that small M divisors are unstable. + * Use maximal possible M = 63 and calculate D divisor. + */ + m_divisor = OSAMP_MAX_DIVISOR; + d_divisor = DIV_ROUND_CLOSEST(port->uartclk, baud * m_divisor); + } + + if (d_divisor < 1) + d_divisor = 1; + else if (d_divisor > BRDV_BAUD_MAX) + d_divisor = BRDV_BAUD_MAX; + spin_lock_irqsave(&mvebu_uart_lock, flags); brdv = readl(port->membase + UART_BRDV); brdv &= ~BRDV_BAUD_MASK; @@ -500,6 +543,9 @@ static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) osamp = readl(port->membase + UART_OSAMP); osamp &= ~OSAMP_DIVISORS_MASK; + if (m_divisor != OSAMP_DEFAULT_DIVISOR) + osamp |= (m_divisor << 0) | (m_divisor << 8) | + (m_divisor << 16) | (m_divisor << 24); writel(osamp, port->membase + UART_OSAMP); return 0; @@ -529,14 +575,16 @@ static void mvebu_uart_set_termios(struct uart_port *port, port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR; /* - * Maximal divisor is 1023 * 16 when using default (x16) scheme. - * Maximum achievable frequency with simple baudrate divisor is 230400. - * Since the error per bit frame would be of more than 15%, achieving - * higher frequencies would require to implement the fractional divisor - * feature. + * Maximal divisor is 1023 and maximal fractional divisor is 63. And + * experiments show that baudrates above 1/80 of parent clock rate are + * not stable. So disallow baudrates above 1/80 of the parent clock + * rate. If port->uartclk is not available, then + * mvebu_uart_baud_rate_set() fails, so values min_baud and max_baud + * in this case do not matter. */ - min_baud = DIV_ROUND_UP(port->uartclk, 1023 * 16); - max_baud = 230400; + min_baud = DIV_ROUND_UP(port->uartclk, BRDV_BAUD_MAX * + OSAMP_MAX_DIVISOR); + max_baud = port->uartclk / 80; baud = uart_get_baud_rate(port, termios, old, min_baud, max_baud); if (mvebu_uart_baud_rate_set(port, baud)) { @@ -1395,14 +1443,14 @@ static int mvebu_uart_clock_probe(struct platform_device *pdev) * Calculate the smallest TBG d1 and d2 divisors that * still can provide 9600 baudrate. */ - d1 = DIV_ROUND_UP(rate, 9600 * OSAMP_DEFAULT_DIVISOR * + d1 = DIV_ROUND_UP(rate, 9600 * OSAMP_MAX_DIVISOR * BRDV_BAUD_MAX); if (d1 < 1) d1 = 1; else if (d1 > CLK_TBG_DIV1_MAX) d1 = CLK_TBG_DIV1_MAX; - d2 = DIV_ROUND_UP(rate, 9600 * OSAMP_DEFAULT_DIVISOR * + d2 = DIV_ROUND_UP(rate, 9600 * OSAMP_MAX_DIVISOR * BRDV_BAUD_MAX * d1); if (d2 < 1) d2 = 1; @@ -1417,8 +1465,7 @@ static int mvebu_uart_clock_probe(struct platform_device *pdev) } /* Skip clock source which cannot provide 9600 baudrate */ - if (rate > 9600 * OSAMP_DEFAULT_DIVISOR * BRDV_BAUD_MAX * d1 * - d2) + if (rate > 9600 * OSAMP_MAX_DIVISOR * BRDV_BAUD_MAX * d1 * d2) continue; /*