From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA0D83F4118; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; cv=none; b=Y5EvP0rE6IV3Ayk9lFnKDwJs7I0qhqchzKAXXhdQPem8Is9bTPOp+tcI9+aGsYqIcXFz+LV/92kGDMhJbIEbEKj2fIPMIwIR1X6Rjv/YO/W9SlDGCmhRjF1JpWPtQX6uKqW2NI22Xdp7UphuVUiWXPCpilWW/0wI5D6BnsU72U8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; c=relaxed/simple; bh=9o2ym9Q7UN5TolJfnYkWX8F/5ZrsOyVQK0D6QvuiADY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IyVUwDuldEkWddUXlY8np/d0sG1BbNnf5cpNssRBjgsoNOB3qZWH8Plaqep1n6EE2Qo4kQ44feeIOxQ1VHj2MQ0btWC7mDhQDlkhevsDn+0L3oqgM3d+jQPYtO0yui8gVUU4IWPSZpdFd2V02Vwu0GARTqcMB/GSV48ew4lm8wc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iw7uhjC0; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iw7uhjC0" Received: by smtp.kernel.org (Postfix) with ESMTPS id 3E85DC2BCF6; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=9o2ym9Q7UN5TolJfnYkWX8F/5ZrsOyVQK0D6QvuiADY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=iw7uhjC0gxpZsDMbrtiAq4XMdOieKGzMWYx5grFRg97hExfHJQT8kMJpsk4Z0rV6r tXllYRhEFCKtkIxpgFTgs25x86tiQQn9K8R1bmVoPMzBfXgVw+DKCnVXNcc+uoS1F7 YRNPHOeGYANSP+Bp2vOsDzgNLTHOzgLHYnDgMRogQ5a6Y32NFIbvzuanepv6G6vl/C Lmea7v2NuL0Ub93K0QaAHvJ+kzmzWW748eI/IknJMYIcQXuBS7AysfXqm8+2SC0SWJ LgG6xbOL1QaPi0lNvHbSd5i/SL8WVcrK5u17DJtNP/8jo6HBTj1HE+fKIGnKXJujhJ yJXZZAQpgapqg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 19484C55ABF; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:09 +0800 Subject: [PATCH v2 01/10] dt-bindings: arm: add Ambarella CV75 platforms Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-1-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=1726; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=E64N0WzuN3RWFUU+WVOoB7iSv1RpxmiKsY2ydNVTzVA=; b=VPKdGB4d5Ldv0umrXjkMWEm6jdp/dVFupjAWrgqvugNER7Wy0ZmWZDGplb+H9c986jFj6wqcn EspvGexW2I6A00/TlCYPgvVtJYcODKR9hiXhS/h3BiFzQjSpdw/kEOy X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Add the Ambarella SoC board binding and vendor prefix covering the CV75 EVK. Signed-off-by: Long Zhao Reviewed-by: Krzysztof Kozlowski --- .../devicetree/bindings/arm/ambarella.yaml | 23 ++++++++++++++++++= ++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 ++ 2 files changed, 25 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/ambarella.yaml b/Documen= tation/devicetree/bindings/arm/ambarella.yaml new file mode 100644 index 000000000000..347148530f80 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/ambarella.yaml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/ambarella.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ambarella SoC platforms + +maintainers: + - Long Zhao + +properties: + $nodename: + const: "/" + + compatible: + oneOf: + - description: Ambarella CV75 EVK board + items: + - const: ambarella,cv75-evk + - const: ambarella,cv75 + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Docum= entation/devicetree/bindings/vendor-prefixes.yaml index 85a27937832e..f48aa6dabbe9 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -112,6 +112,8 @@ patternProperties: description: Amarula Solutions "^amazon,.*": description: Amazon.com, Inc. + "^ambarella,.*": + description: Ambarella, Inc. "^amcc,.*": description: Applied Micro Circuits Corporation (APM, formally AMCC) "^amd,.*": --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA0443F4108; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; cv=none; b=lkSaAKB1IjIAWGz4nHIldtcw7L0hTJcc6GSSlkFz34aEsmHJViCQ9Wa5bBHJtTugn2uCIwBjHhvfodxJXLssihSAO2Tm5BWhs+d5RltsdpW32usquMgQMU/F0jfEg2ANEQo8TXVkfe8Itro+sgAD39IPYi81okqDhAtpS6LunLc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; c=relaxed/simple; bh=xksXNR3RIxdAAWa4ZhBc1ec5Me5J0Ftc6OOeqFa8oRc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RtXfHYFScxZFxa+DDiw/pT8k58F2KgnTP6+PSq9KDGk3n2bISeWys2acJn70WvDnlBm9pNsjhejZLczNTZKC84SqQm0SbqlobZBWKV7NqVZbigPyTqjVEJfK/sFu5eUE8EnMmwgthiZMBo/wsn4WvmwUSPK6Wj+bPzLyI+QBKso= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=r+t+ygdU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="r+t+ygdU" Received: by smtp.kernel.org (Postfix) with ESMTPS id 506F1C2BCF7; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=xksXNR3RIxdAAWa4ZhBc1ec5Me5J0Ftc6OOeqFa8oRc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=r+t+ygdUF8iAuJnJfsPUYCXVRoWcg8xWExj72eAjkTdVqixYikWlnvx3aQx6hkGeN Z3gSL3+r7X8+MTZEm4vPxjL/tBa388Vw1D7PgUM+RHJMtaWx1QjcFyPbihV2ePTeub hxyRdn3kj4WBlm0mSpnmtM9QnxXpYwY5GMQ84sV2aHHP0jlbGlr7xHDeiVEQ22wzdU ZwTWF+EaftOF6m56yQfOd/TN42wyiLTzTxK/fo2vN2O8tdO3UEjnsjx7gPd4lr9jIL 4bFckpT7+IARfxqhVf0wDdIU0QVCVrPtaVKDEwE/TAI4w/3co+HnzYKHMJRz0NlGmi m77BIKwbYMMoA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2FEA4C56208; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:10 +0800 Subject: [PATCH v2 02/10] dt-bindings: soc: add Ambarella secure scratchpad Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-2-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=1545; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=1vmye3CK0HpbkUmTNeoU7Oscgo8VwkmeZd901TWonnk=; b=f3oS0meErI2j00TKtSZ8nQ8sMS+wTYXOy+doYgvXLIsQdTWSBNPan7rytIrwLIoZznhElBAee KuyY6KX+IeZBHkpzWLPBo4zGKFpEpuqUX2LsFzPBeafjNNlDGKeEVi7 X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Document the Ambarella secure scratchpad syscon used by CV75. Signed-off-by: Long Zhao --- .../ambarella/ambarella,cv75-sec-scratchpad.yaml | 39 ++++++++++++++++++= ++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/ambarella/ambarella,cv75= -sec-scratchpad.yaml b/Documentation/devicetree/bindings/soc/ambarella/amba= rella,cv75-sec-scratchpad.yaml new file mode 100644 index 000000000000..4bfeca0ac3da --- /dev/null +++ b/Documentation/devicetree/bindings/soc/ambarella/ambarella,cv75-sec-sc= ratchpad.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/ambarella/ambarella,cv75-sec-scratc= hpad.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ambarella CV75 Secure Scratchpad Registers + +maintainers: + - Long Zhao + +description: + The secure scratchpad block contains shared system registers used by + other Ambarella SoC blocks through syscon regmap lookups. + +allOf: + - $ref: /schemas/mfd/syscon-common.yaml# + +properties: + compatible: + items: + - const: ambarella,cv75-sec-scratchpad + - const: syscon + + reg: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + scratchpad@e002e000 { + compatible =3D "ambarella,cv75-sec-scratchpad", "syscon"; + reg =3D <0xe002e000 0x1000>; + }; --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA3443F4125; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; cv=none; b=Jrxghi3gKNVLwMilknCzGVQchOAhXxdYfbmUaCT+ElN05W4qO/e1SdLM2dasEbE5mJ3sjKyxbJDgZja27gxwH4Fdl2iAglnwvZB6jzBq3pIR9B6GeCZ96K2kqIkHP84gH8Q6SVh99dbNEZqGKSEqq4UB7zI/ZDRG0GM83A/RY04= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; c=relaxed/simple; bh=cYPUiIG3WZWc0vigCV8eiQKRpmmNx1zWV9ouQXuS/WA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y3YqfnOPwakaQHVEbAMQorAuLiVhHE6DdVyfIGoQrkCLGtqTd1Q0YY/bhZZyDNNjOegYOmgFvbLlLKrDco/0FWWIvLwlu/tFJCaeGkps3upwTXAgtPSTjus9PAAe3HlBk7bi+4YdIVqiR5XYQrQnN9t3YTQxbhak/9q6WaFjXFs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hDenrtRN; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hDenrtRN" Received: by smtp.kernel.org (Postfix) with ESMTPS id 63AE3C2BCFA; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=cYPUiIG3WZWc0vigCV8eiQKRpmmNx1zWV9ouQXuS/WA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=hDenrtRNO4HIiFS1TA7HWICQN8rL+9XKi6FrcZMPjS+T+aW1SMINJNUYMWl+bLkDQ nnb8/6ebOAXkMDZoFM4EGBy+l27I9OJ+3jpyEmzCBxUlb4Nrpgc2tzALtKWXK1YR7U QTt/ru7NdZWm/hVv3FuAapYkQuw1UH5gvL5cFzo64ZWRGFVkCaOsNT03Hy4haKJSGL RBHHW2cWSHelzEZkSYnXm5Cb4QvAB5hvtBoOdAV7dDit7kmoOsvnz8pnRJ1dp38aSO SsoGYeUU3CW1dsprzk1OvcE4QIhjf2OXxykrmZ29mXF0pdzW5Ddy7wh9m/u5JnVjKx 6PuTr5kq4Gt1A== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 44722C5620A; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:11 +0800 Subject: [PATCH v2 03/10] dt-bindings: clock: add Ambarella CV75 RCT clock controller Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-3-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=2901; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=DFTGZ4uvV0RkAC0IaZusCfnS/KQPHA8JTNal3McD4Z4=; b=Ah0cJCSIxUfLesXH+5wJqawUNYqeT3CHVWMhVZ3L9xjHidofApA7nryJBlzdK0oeAKN8oDxl6 4jX8eBz5WTTAlazq5AfOS+U4V6k8iQeARuAXWgCN2XNKZ8drYBFZ7qA X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Add the CV75 RCT clock controller binding and clock ID header. Signed-off-by: Long Zhao --- .../bindings/clock/ambarella,cv75-rct.yaml | 66 ++++++++++++++++++= ++++ include/dt-bindings/clock/ambarella,cv75-clock.h | 17 ++++++ 2 files changed, 83 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yam= l b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml new file mode 100644 index 000000000000..aa96533bab9b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ambarella,cv75-rct.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ambarella CV75 RCT Clock Controller + +maintainers: + - Long Zhao + +description: | + The RCT (Reset and Clock Tree) block on the Ambarella CV75 SoC provides + PLLs, clock muxes and dividers for on-chip peripherals. + + The same register region is also exposed as a syscon for other SoC + blocks (e.g. pin drive-strength), hence the "syscon" compatible. + + Clock indices are defined in + include/dt-bindings/clock/ambarella,cv75-clock.h: + + - CV75_GCLK_UART0: UART0 mux/div clock + - CV75_GCLK_CORE: Core PLL (gclk_core) + - CV75_GCLK_AHB: AHB bus clock (core / 2) + - CV75_GCLK_APB: APB bus clock (core / 4) + +properties: + $nodename: + pattern: "^clock-controller@" + + compatible: + items: + - const: ambarella,cv75-rct + - const: syscon + + reg: + maxItems: 1 + + clocks: + description: Reference crystal oscillator (typically 24 MHz) + maxItems: 1 + + clock-names: + const: osc + + "#clock-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - "#clock-cells" + +additionalProperties: false + +examples: + - | + clock-controller@ed080000 { + compatible =3D "ambarella,cv75-rct", "syscon"; + reg =3D <0xed080000 0x1000>; + clocks =3D <&osc>; + clock-names =3D "osc"; + #clock-cells =3D <1>; + }; diff --git a/include/dt-bindings/clock/ambarella,cv75-clock.h b/include/dt-= bindings/clock/ambarella,cv75-clock.h new file mode 100644 index 000000000000..3d3314446e75 --- /dev/null +++ b/include/dt-bindings/clock/ambarella,cv75-clock.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#ifndef _DT_BINDINGS_CLOCK_AMBARELLA_CV75_CLOCK_H +#define _DT_BINDINGS_CLOCK_AMBARELLA_CV75_CLOCK_H + +/* Boot / bus clocks */ +#define CV75_GCLK_UART0 0 +#define CV75_GCLK_CORE 1 +#define CV75_GCLK_AHB 2 +#define CV75_GCLK_APB 3 + +#define CV75_CLK_NUM 4 + +#endif --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B082E3F4138; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; cv=none; b=DAlU/6qRzxjzL1KPnxg4U4m9MeRihrN+sj9bsHYYuqzClSo08KvJrGMyjEawt1bGG//mY7TNGxnflLnDZlssQD6WwqIdW6bCMO3lxlNoZXTGj1B7pBoQUScDUirlVSwdeCGbzd4eQ3ZnacUTQk/K45e97+ck0h0kTmHwIGWrRGQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008866; c=relaxed/simple; bh=Nlm5hTHmD3jVsjGjeCec+uJpTqsxuKfQw+NDZdxHGoE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QCbs2vGzxPP0SITsRaALashLhSRKqTOZW4mmC+AFa4QhELhHafh7J+UPbsN9B84gXazrxsPwRkC0KKLBIdNnEnQfwJcnMdX2x8alYIGyFLGGs+e91UrSQ1AjkYdN4roTkqMZZ0utcQ01/5KzZvRe1bUK4ooWZBlhU+BiZ5gtg+o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=t95zQJej; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="t95zQJej" Received: by smtp.kernel.org (Postfix) with ESMTPS id 715A2C2BD04; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=Nlm5hTHmD3jVsjGjeCec+uJpTqsxuKfQw+NDZdxHGoE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=t95zQJejNNWaf44gVX1XbrLkTX/Js0YD3NmiU/Rapaf7/VvToPBUGKSuKv7qb/Djs 7in/ykeMEKANpMFTaCgKM+AOJ06E6z17w+r7pA1NyyyzmIIBvcMOpIZlxlTs0VwBUL MxZzkooVYLRDQmMmz5ThAlrtDWMCU6BoJDv/Zy6ql+3tBGhlSyUPYXuRdwcjpbvyfC AaBDuBNGDLvQOkbungQFw1XeKS3plf0G4gb3kNb3wH24NFi+rTbEct7VGGyoM8nteQ VvuyP3k68lJw3EZLohiVrxv8ZiYxff2Qa1/svrUVbvNK/7N4ASk48JZmxxBskSAEnf RcDqCDsTIVy8w== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5AE80C5AC6F; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:12 +0800 Subject: [PATCH v2 04/10] dt-bindings: pinctrl: add Ambarella CV75 pinctrl Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-4-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=4816; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=9rdubmZ/+QRo7Rx2fG5nIwgQAldJunotrOeXcg9Kbfo=; b=F8tcpzImpasxLSOpFpa6um1OEviO7AaVDmtd8wslWwtethIiD275bee1POZMWiMBBcB9DdOFD FcfOCqbzGK7AV21UvOdQljOEMAhfsnfobtLOJK/JcoYagHZhqT/ROQs X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Add the CV75 pin controller binding. Signed-off-by: Long Zhao --- .../bindings/pinctrl/ambarella,cv75-pinctrl.yaml | 175 +++++++++++++++++= ++++ 1 file changed, 175 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinct= rl.yaml b/Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinctrl.= yaml new file mode 100644 index 000000000000..3fa30b275e51 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinctrl.yaml @@ -0,0 +1,175 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/ambarella,cv75-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ambarella CV75 Pin Controller + +maintainers: + - Long Zhao + +description: + The Ambarella CV75 pin controller configures pin multiplexing, bias and + drive strength for the main GPIO banks. Pin groups and their mux settings + are defined by the pin controller driver and selected by function and gr= oup + names. + +allOf: + - $ref: pinctrl.yaml# + +properties: + compatible: + const: ambarella,cv75-pinctrl + + reg: + maxItems: 1 + description: IOMUX registers + + ambarella,drive-strength-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: RCT syscon containing the drive-strength registers. + + ambarella,pull-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: Secure scratchpad syscon containing the pull registers. + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^gpio@[0-9a-f]+$": + type: object + additionalProperties: false + + properties: + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-ranges: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + required: + - reg + - interrupts + - gpio-controller + - "#gpio-cells" + - gpio-ranges + - interrupt-controller + - "#interrupt-cells" + + "^[a-z0-9-]+-pins$": + type: object + allOf: + - $ref: pinmux-node.yaml# + - $ref: pincfg-node.yaml# + additionalProperties: false + + properties: + function: + const: uart0 + + groups: + $ref: /schemas/types.yaml#/definitions/string-array + items: + enum: [uart0tx, uart0rx] + minItems: 2 + maxItems: 2 + uniqueItems: true + + bias-disable: true + bias-pull-down: true + bias-pull-up: true + + drive-strength: + enum: [3, 4, 5, 6, 7, 8, 9, 12] + + required: + - function + - groups + +required: + - compatible + - reg + - ambarella,drive-strength-syscon + - ambarella,pull-syscon + - "#address-cells" + - "#size-cells" + - ranges + +unevaluatedProperties: false + +examples: + - | + #include + + rct: rct { + }; + + scratchpad: scratchpad { + }; + + pinctrl: pinctrl@e4010000 { + compatible =3D "ambarella,cv75-pinctrl"; + #address-cells =3D <1>; + #size-cells =3D <1>; + reg =3D <0xe4010000 0x1000>; + ranges; + ambarella,drive-strength-syscon =3D <&rct>; + ambarella,pull-syscon =3D <&scratchpad>; + + gpio0: gpio@e4013000 { + reg =3D <0xe4013000 0x1000>; + interrupts =3D ; + gpio-controller; + #gpio-cells =3D <2>; + gpio-ranges =3D <&pinctrl 0 0 32>; + interrupt-controller; + #interrupt-cells =3D <2>; + }; + + gpio1: gpio@e4014000 { + reg =3D <0xe4014000 0x1000>; + interrupts =3D ; + gpio-controller; + #gpio-cells =3D <2>; + gpio-ranges =3D <&pinctrl 0 32 32>; + interrupt-controller; + #interrupt-cells =3D <2>; + }; + + gpio2: gpio@e4015000 { + reg =3D <0xe4015000 0x1000>; + interrupts =3D ; + gpio-controller; + #gpio-cells =3D <2>; + gpio-ranges =3D <&pinctrl 0 64 32>; + interrupt-controller; + #interrupt-cells =3D <2>; + }; + + uart0-pins { + function =3D "uart0"; + groups =3D "uart0tx", "uart0rx"; + bias-disable; + drive-strength =3D <8>; + }; + }; --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4F8F741F7F2; Thu, 6 Aug 2026 09:34:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; cv=none; b=Nnsmend+5fn1AsPgEq2jPLMo5wX+DXs3yFVXDQaepNMALtthYk+1oNU4jgrZtH0V4D0mGBdzdAF1PnAMrsZU1b2G9QQnxWKs9Sc3cIRUkQUiyNLEyHjIRsl8IMs2qbp1V7C3DWr6XyItHoxlBq0XOi+yrV7jW/h/YgC0Da/4bDc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; c=relaxed/simple; bh=xUgMhJisYvLuqDZcdPp4HG6wSEX/KHSUvHPVx8bBVro=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kBrLmYWaE9qGHy6TRJ+OxS8Q+zzHyPcT8zVrq2jYLF7A6EiU/jMSEH7FgqkWKVoKoSYEHuGwkdqXBCBV/Ah7yuqQrKeFiaA0TrzB3oO6qtGMV3taKcC6p1EezehvuiXxysfDa4tTgsdhYfhuQ92DyTTYXhV8OgZDqrdv38Rr9J8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mIIDQ00j; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mIIDQ00j" Received: by smtp.kernel.org (Postfix) with ESMTPS id 88BE3C2BCFF; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=xUgMhJisYvLuqDZcdPp4HG6wSEX/KHSUvHPVx8bBVro=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=mIIDQ00j1xrM959FClKDZK5VJnm8wwPwNCrONSyIdy7SeLn7N+cY4rmiEQaYx3MqG ecVHbeUJhniU6XDfuH+9REe6U/nwfQ2EpCEfy6Vq25HL/P2ZfEedKyoDf8gCsyj7LH BFSXFHfCdAJFaLlF0KSGz7Dvr7fjx47h/1eRBu+TjxyIzc+IhIhMkg4CGPppqbWlA4 I0CjM45+gDasdmGfoHkv9KrF5d7OncEaFTB/8fFM/QCqtC+F6bDHfBg8wyKkbd3/4V khbQDLrevVGF3EfcAMQ+qbDWqkBKA3dXj1anqrjKFnhE+tj/pTTbRqOjhROsJR+ryK BYNDY9x/vM7BQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 72765C55838; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:13 +0800 Subject: [PATCH v2 05/10] dt-bindings: serial: add Ambarella UART Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-5-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=1437; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=3IBRZ3r8aIVqSKUt911TKSBxsBmqjCi8qDWSpR60+wA=; b=M6+0z8dDfmpElcLgzbEbUfUoySjemhkkbs6T71oYsaDIRGkmIvWuGiAI40PfLWa3hP6Qi0wI0 0li8Y/TnBYSCa1X2yPoCMk2J9YWaf1jHSF9yoqV/fpNFTwIayiYcOkg X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Document the Ambarella UART controller. Signed-off-by: Long Zhao Reviewed-by: Rob Herring (Arm) --- .../bindings/serial/ambarella,cv75-uart.yaml | 45 ++++++++++++++++++= ++++ 1 file changed, 45 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/ambarella,cv75-uart.y= aml b/Documentation/devicetree/bindings/serial/ambarella,cv75-uart.yaml new file mode 100644 index 000000000000..86d6d8a61ace --- /dev/null +++ b/Documentation/devicetree/bindings/serial/ambarella,cv75-uart.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/ambarella,cv75-uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ambarella CV75 UART + +maintainers: + - Long Zhao + +allOf: + - $ref: serial.yaml# + +properties: + compatible: + const: ambarella,cv75-uart + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + + serial@e4000000 { + compatible =3D "ambarella,cv75-uart"; + reg =3D <0xe4000000 0x1000>; + interrupts =3D ; + clocks =3D <&uart_clk>; + }; --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 09DF34071DC; Thu, 6 Aug 2026 09:34:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; cv=none; b=m1xe5qVsU04OlSiCpuaQrLposRluN/0wccWMqqauT9ixAnlPP+sIwmfjJ5y7fO7TFnG2ODsaPrWh2DFMfsUzeea8NDJElu+BrBXbAmQFA66CjW49ehurY5zoA2OG4MV3jATwDLNEb4fRTK7vMxPFTb+PpWeYBYpUY/9L8D1kdTg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; c=relaxed/simple; bh=hhwh1tiOFZEUqJXjQban5stSSjAI59dj4WO32+uw/Mo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=o7jGkFzd5uPiSoBxajF4FgGAWm+pgZk+muWv1qkfIcNEPoMYoJzp+Wi+C1ng2EGePCdSincFa5QK/cVfBy5VBCdU5enUN8XiCnX5ttEdOivi9p3WA79NowEKJGzcj6RPY0s5wPnJK6vAL/lLI0kNSperdpWb5lhnUW+yGf3pGIs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nGfpWcaZ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nGfpWcaZ" Received: by smtp.kernel.org (Postfix) with ESMTPS id A4447C2BD00; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=hhwh1tiOFZEUqJXjQban5stSSjAI59dj4WO32+uw/Mo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=nGfpWcaZR4RR31l23Rt45Hd4rw3xS1tM5RXY7Sj/qH7ereOFospyk89A9OuOTCxMe 9pikoKRhoDdBO4bcj1rGKXO15LFDBhvunB9FLXpGap09xkXV5BnLeySKe2KeEmFCxP lxcPPCWSFJ09RNZrGJDKzzPRmUp1COmdJdJxEwucPolfDKlEHkriFxv9CV3q+L/9L+ X/Iz49Wh+xKzqpYwUc9wr81GFpllpyiumtYZ5qsSg+Fdm9RnihOhoKmQK+OFGcI1/v I0HoJxjzkOsW2r18446Y0ZyGRw93yag8z5YUbDq8LCv1BijRiqZ1si54YdtVE9HhLN eu6LkVydvRHsw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 904D0C55ABF; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:14 +0800 Subject: [PATCH v2 06/10] clk: ambarella: add CV75 CCU driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-6-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=32703; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=5WTJkFHMnBWQkn+LK1If1gtosy3j7OyE2HS5Ufevpjw=; b=c5UAOFqguJfiek3KdHeG4TLNdiQeZIGCsW2683qukHA3kW/ctWQOupwzrfJq+HbgmbNoycVFq d6cU7tFBDRUACkljRqftVw22MHzBwJp9Mr7C9wTbeky+MTyV/3sfwyZ X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Add a minimal Ambarella clock controller for CV75 covering the UART0 and core/ahb/apb clocks required for early console bring-up. Signed-off-by: Long Zhao --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/ambarella/Kconfig | 16 ++ drivers/clk/ambarella/Makefile | 6 + drivers/clk/ambarella/ccu-cv75.c | 296 ++++++++++++++++++++++++++++ drivers/clk/ambarella/ccu_common.c | 60 ++++++ drivers/clk/ambarella/ccu_common.h | 25 +++ drivers/clk/ambarella/ccu_mux_div.c | 223 +++++++++++++++++++++ drivers/clk/ambarella/ccu_mux_div.h | 35 ++++ drivers/clk/ambarella/ccu_pll.c | 374 ++++++++++++++++++++++++++++++++= ++++ drivers/clk/ambarella/ccu_pll.h | 70 +++++++ 11 files changed, 1107 insertions(+) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 1717ce75a907..fbbf4963716c 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -506,6 +506,7 @@ config COMMON_CLK_RPMI the RISC-V platform management interface (RPMI) specification. =20 source "drivers/clk/actions/Kconfig" +source "drivers/clk/ambarella/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/aspeed/Kconfig" source "drivers/clk/bcm/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cc108a75a900..30d823ee606f 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE) +=3D clk-xgene.o =20 # please keep this section sorted lexicographically by directory path name obj-y +=3D actions/ +obj-y +=3D ambarella/ obj-y +=3D analogbits/ obj-y +=3D aspeed/ obj-$(CONFIG_COMMON_CLK_AT91) +=3D at91/ diff --git a/drivers/clk/ambarella/Kconfig b/drivers/clk/ambarella/Kconfig new file mode 100644 index 000000000000..639993033f96 --- /dev/null +++ b/drivers/clk/ambarella/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config CLK_AMBARELLA_CV75 + bool "Ambarella CV75 RCT clock controller" + depends on ARCH_AMBARELLA || COMPILE_TEST + select CLK_AMBARELLA_CCU + default ARCH_AMBARELLA + help + Say Y to enable support for the Reset and Clock Tree controller + found on Ambarella CV75 SoCs. The controller provides the core PLL, + bus clocks and UART clock required during early platform boot. + It is needed to use the serial console on CV75-based systems. + +config CLK_AMBARELLA_CCU + bool + select REGMAP_MMIO diff --git a/drivers/clk/ambarella/Makefile b/drivers/clk/ambarella/Makefile new file mode 100644 index 000000000000..36e96326ea97 --- /dev/null +++ b/drivers/clk/ambarella/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for ambarella specific clk +# + +obj-$(CONFIG_CLK_AMBARELLA_CCU) +=3D ccu_common.o ccu_mux_div.o ccu_pll.o +obj-$(CONFIG_CLK_AMBARELLA_CV75) +=3D ccu-cv75.o diff --git a/drivers/clk/ambarella/ccu-cv75.c b/drivers/clk/ambarella/ccu-c= v75.c new file mode 100644 index 000000000000..55132dfa7431 --- /dev/null +++ b/drivers/clk/ambarella/ccu-cv75.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Ambarella, Inc. + * + * CV75 RCT clock controller for boot clocks. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "ccu_common.h" +#include "ccu_mux_div.h" +#include "ccu_pll.h" + +enum amb_cv75_clk_type { + AMB_CV75_CLK_FIXED_RATE, + AMB_CV75_CLK_FIXED_FACTOR, + AMB_CV75_CLK_PLL, + AMB_CV75_CLK_DIV, + AMB_CV75_CLK_MUX_DIV, +}; + +enum amb_cv75_clk_ref { + AMB_CV75_CLK_REF_OSC =3D -1, + AMB_CV75_CLK_REF_DUMMY =3D -2, +}; + +/* PLL version for CV75 (ambarella,clkpll-v1 in vendor DTS) */ +static const struct amb_pll_soc_data cv75_pll_soc_data =3D { + .pll_version =3D 1, + .fsout_mask =3D CTRL2_FSOUT_DIV2, + .fsout_val =3D CTRL2_FSOUT_DIV2, + .fsdiv_mask =3D CTRL2_FSDIV_DIV2, + .fsdiv_val =3D CTRL2_FSDIV_DIV2, + .vcodiv_mask =3D CTRL2_VCODIV_DIV2, + .vcodiv_val =3D CTRL2_VCODIV_DIV2, + .vco_max_mhz =3D 2600UL, + .vco_min_mhz =3D 850UL, + .vco_range =3D { 1800UL, 1400UL, 1100UL, 0UL }, +}; + +struct amb_cv75_clk_desc { + int id; + enum amb_cv75_clk_type type; + const char *name; + int parent; + + union { + struct { + unsigned long rate; + } fixed_rate; + struct { + unsigned long flags; + unsigned int mult; + unsigned int div; + } fixed_factor; + struct { + u32 reg_offset[REG_NUM]; + const struct amb_pll_soc_data *soc_data; + } pll; + struct { + u32 reg; + u32 shift; + u32 width; + u32 flags; + u32 fix_divider; + } div; + struct amb_mux_div_desc mux_div; + }; +}; + +static const struct amb_cv75_clk_desc cv75_clks[] =3D { + { + .id =3D AMB_CV75_CLK_REF_DUMMY, + .type =3D AMB_CV75_CLK_FIXED_RATE, + .name =3D "dummy", + .fixed_rate.rate =3D 0, + }, + { + .id =3D CV75_GCLK_CORE, + .type =3D AMB_CV75_CLK_PLL, + .name =3D "core", + .parent =3D AMB_CV75_CLK_REF_OSC, + .pll =3D { + .reg_offset =3D { + 0x000, 0x004, 0x100, + 0x104, 0x000, 0x000, + }, + .soc_data =3D &cv75_pll_soc_data, + }, + }, + { + .id =3D CV75_GCLK_AHB, + .type =3D AMB_CV75_CLK_FIXED_FACTOR, + .name =3D "ahb", + .parent =3D CV75_GCLK_CORE, + .fixed_factor =3D { + .flags =3D 0, + .mult =3D 1, + .div =3D 2, + }, + }, + { + .id =3D CV75_GCLK_APB, + .type =3D AMB_CV75_CLK_FIXED_FACTOR, + .name =3D "apb", + .parent =3D CV75_GCLK_CORE, + .fixed_factor =3D { + .flags =3D 0, + .mult =3D 1, + .div =3D 4, + }, + }, + { + .id =3D CV75_GCLK_UART0, + .type =3D AMB_CV75_CLK_MUX_DIV, + .name =3D "uart0", + .mux_div =3D { + .name =3D "uart0", + .parents =3D (const int[]) { + AMB_CV75_CLK_REF_OSC, CV75_GCLK_CORE, + AMB_CV75_CLK_REF_DUMMY, AMB_CV75_CLK_REF_DUMMY, + }, + .num_parents =3D 4, + .mux_reg =3D 0x1c8, + .mux_shift =3D 0, + .mux_mask =3D 0x3, + .div_reg =3D 0x038, + .div_shift =3D 0, + .div_width =3D 24, + .div_flags =3D CLK_DIVIDER_ONE_BASED, + .fix_divider =3D 1, + }, + }, +}; + +static struct clk_hw *amb_cv75_get_parent(struct amb_ccu *ccu, + struct clk_hw *osc, + struct clk_hw *dummy, + int parent) +{ + if (parent =3D=3D AMB_CV75_CLK_REF_OSC) + return osc; + if (parent =3D=3D AMB_CV75_CLK_REF_DUMMY) + return dummy; + + if (parent < 0 || parent >=3D ccu->data->num) + return ERR_PTR(-EINVAL); + + if (!ccu->data->hws[parent]) + return ERR_PTR(-EPROBE_DEFER); + + return ccu->data->hws[parent]; +} + +static struct clk_hw *amb_cv75_register_mux_div(struct device *dev, + struct amb_ccu *ccu, + const struct amb_cv75_clk_desc *desc, + struct clk_hw *osc, + struct clk_hw *dummy) +{ + const struct amb_mux_div_desc *md =3D &desc->mux_div; + struct clk_parent_data *pdata; + struct clk_hw *parent; + u8 i; + + pdata =3D devm_kcalloc(dev, md->num_parents, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + for (i =3D 0; i < md->num_parents; i++) { + if (md->parents[i] =3D=3D AMB_CV75_CLK_REF_OSC) { + pdata[i].fw_name =3D "osc"; + continue; + } + + parent =3D amb_cv75_get_parent(ccu, osc, dummy, md->parents[i]); + if (IS_ERR(parent)) + return parent; + + pdata[i].hw =3D parent; + } + + return amb_mux_div_register(dev, ccu->map, md, pdata); +} + +static struct clk_hw *amb_cv75_register_clk(struct device *dev, + struct amb_ccu *ccu, + const struct amb_cv75_clk_desc *desc, + struct clk_hw *osc, + struct clk_hw *dummy) +{ + struct amb_pll_desc pll_desc; + struct clk_hw *parent; + + switch (desc->type) { + case AMB_CV75_CLK_FIXED_RATE: + return devm_clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, + desc->fixed_rate.rate); + case AMB_CV75_CLK_FIXED_FACTOR: + parent =3D amb_cv75_get_parent(ccu, osc, dummy, desc->parent); + if (IS_ERR(parent)) + return parent; + + return devm_clk_hw_register_fixed_factor_parent_hw(dev, + desc->name, parent, + desc->fixed_factor.flags, + desc->fixed_factor.mult, + desc->fixed_factor.div); + case AMB_CV75_CLK_PLL: + parent =3D amb_cv75_get_parent(ccu, osc, dummy, desc->parent); + if (IS_ERR(parent)) + return parent; + + pll_desc.name =3D desc->name; + pll_desc.parent =3D parent; + memcpy((void *)pll_desc.reg_offset, desc->pll.reg_offset, + sizeof(pll_desc.reg_offset)); + pll_desc.soc_data =3D desc->pll.soc_data; + pll_desc.frac_mode =3D false; + + return amb_pll_register(dev, ccu->map, &pll_desc); + case AMB_CV75_CLK_DIV: + parent =3D amb_cv75_get_parent(ccu, osc, dummy, desc->parent); + if (IS_ERR(parent)) + return parent; + + return amb_div_register(dev, ccu->map, desc->name, parent, + desc->div.reg, desc->div.shift, + desc->div.width, desc->div.flags, + desc->div.fix_divider); + case AMB_CV75_CLK_MUX_DIV: + return amb_cv75_register_mux_div(dev, ccu, desc, osc, dummy); + default: + return ERR_PTR(-EINVAL); + } +} + +static int amb_cv75_rct_probe(struct platform_device *pdev) +{ + struct amb_ccu *ccu; + struct clk *osc_clk; + struct clk_hw *osc, *dummy =3D NULL, *hw; + int i; + + ccu =3D amb_ccu_init(pdev, CV75_CLK_NUM); + if (IS_ERR(ccu)) + return PTR_ERR(ccu); + + osc_clk =3D devm_clk_get(&pdev->dev, "osc"); + if (IS_ERR(osc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(osc_clk), + "missing osc clock\n"); + osc =3D __clk_get_hw(osc_clk); + + for (i =3D 0; i < ARRAY_SIZE(cv75_clks); i++) { + hw =3D amb_cv75_register_clk(&pdev->dev, ccu, &cv75_clks[i], + osc, dummy); + if (IS_ERR(hw)) + return dev_err_probe(&pdev->dev, PTR_ERR(hw), + "failed to register %s\n", + cv75_clks[i].name); + + if (cv75_clks[i].id =3D=3D AMB_CV75_CLK_REF_DUMMY) + dummy =3D hw; + else + ccu->data->hws[cv75_clks[i].id] =3D hw; + } + + return amb_ccu_register(ccu); +} + +static const struct of_device_id amb_cv75_rct_match[] =3D { + { .compatible =3D "ambarella,cv75-rct" }, + { } +}; +MODULE_DEVICE_TABLE(of, amb_cv75_rct_match); + +static struct platform_driver amb_cv75_rct_driver =3D { + .probe =3D amb_cv75_rct_probe, + .driver =3D { + .name =3D "ambarella-cv75-rct", + .of_match_table =3D amb_cv75_rct_match, + }, +}; +module_platform_driver(amb_cv75_rct_driver); + +MODULE_AUTHOR("Ambarella Inc."); +MODULE_DESCRIPTION("Ambarella CV75 RCT clock controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/ambarella/ccu_common.c b/drivers/clk/ambarella/ccu= _common.c new file mode 100644 index 000000000000..78587c5f2ce1 --- /dev/null +++ b/drivers/clk/ambarella/ccu_common.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#include +#include +#include +#include +#include + +#include "ccu_common.h" + +static const struct regmap_config amb_rct_regmap_config =3D { + .reg_bits =3D 32, + .val_bits =3D 32, + .reg_stride =3D 4, + .max_register =3D AMB_RCT_REG_SIZE - 4, +}; + +struct amb_ccu *amb_ccu_init(struct platform_device *pdev, + unsigned int num_clks) +{ + struct amb_ccu *ccu; + void __iomem *base; + + if (!num_clks) + return ERR_PTR(-EINVAL); + + ccu =3D devm_kzalloc(&pdev->dev, sizeof(*ccu), GFP_KERNEL); + if (!ccu) + return ERR_PTR(-ENOMEM); + + ccu->dev =3D &pdev->dev; + + ccu->data =3D devm_kzalloc(&pdev->dev, + struct_size(ccu->data, hws, num_clks), + GFP_KERNEL); + if (!ccu->data) + return ERR_PTR(-ENOMEM); + + ccu->data->num =3D num_clks; + + base =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return ERR_CAST(base); + + ccu->map =3D devm_regmap_init_mmio(&pdev->dev, base, + &amb_rct_regmap_config); + if (IS_ERR(ccu->map)) + return ERR_CAST(ccu->map); + + return ccu; +} + +int amb_ccu_register(struct amb_ccu *ccu) +{ + return devm_of_clk_add_hw_provider(ccu->dev, of_clk_hw_onecell_get, + ccu->data); +} diff --git a/drivers/clk/ambarella/ccu_common.h b/drivers/clk/ambarella/ccu= _common.h new file mode 100644 index 000000000000..f2bf9d1f06d7 --- /dev/null +++ b/drivers/clk/ambarella/ccu_common.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#ifndef __CCU_COMMON_H +#define __CCU_COMMON_H + +#include +#include +#include + +#define AMB_RCT_REG_SIZE 0x1000 + +struct amb_ccu { + struct device *dev; + struct regmap *map; + struct clk_hw_onecell_data *data; +}; + +struct amb_ccu *amb_ccu_init(struct platform_device *pdev, + unsigned int num_clks); +int amb_ccu_register(struct amb_ccu *ccu); + +#endif diff --git a/drivers/clk/ambarella/ccu_mux_div.c b/drivers/clk/ambarella/cc= u_mux_div.c new file mode 100644 index 000000000000..33430506f8ed --- /dev/null +++ b/drivers/clk/ambarella/ccu_mux_div.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Ambarella, Inc. + * + * Regmap-backed mux + divider, derived from the vendor composite-clock dr= iver. + */ + +#include +#include +#include + +#include "ccu_mux_div.h" + +struct amb_mux { + struct clk_hw hw; + struct regmap *map; + u32 offset; + u32 mask; + u32 shift; +}; + +struct amb_div { + struct clk_hw hw; + struct regmap *map; + u32 offset; + u32 shift; + u32 width; + u32 flags; + u32 fix_divider; +}; + +#define to_amb_mux(_hw) container_of(_hw, struct amb_mux, hw) +#define to_amb_div(_hw) container_of(_hw, struct amb_div, hw) + +static u8 amb_mux_get_parent(struct clk_hw *hw) +{ + struct amb_mux *mux =3D to_amb_mux(hw); + u32 val; + + regmap_read(mux->map, mux->offset, &val); + val >>=3D mux->shift; + val &=3D mux->mask; + + return val; +} + +static int amb_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct amb_mux *mux =3D to_amb_mux(hw); + + return regmap_update_bits(mux->map, mux->offset, + mux->mask << mux->shift, + index << mux->shift); +} + +static const struct clk_ops amb_mux_ops =3D { + .get_parent =3D amb_mux_get_parent, + .set_parent =3D amb_mux_set_parent, +}; + +static unsigned long amb_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct amb_div *div =3D to_amb_div(hw); + unsigned long rate; + u32 val; + + regmap_read(div->map, div->offset, &val); + + if (val & BIT(div->width)) + return 0; + + val >>=3D div->shift; + val &=3D clk_div_mask(div->width); + + rate =3D divider_recalc_rate(hw, parent_rate, val, NULL, + div->flags, div->width); + if (div->fix_divider) + do_div(rate, div->fix_divider); + + return rate; +} + +static int amb_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct amb_div *div =3D to_amb_div(hw); + struct clk_rate_request scaled =3D *req; + int ret; + + if (!req->rate && (div->flags & CLK_DIVIDER_ONE_BASED)) + return 0; + + if (div->fix_divider) { + scaled.rate =3D min(req->rate, + ULONG_MAX / div->fix_divider) * + div->fix_divider; + scaled.min_rate =3D min(req->min_rate, + ULONG_MAX / div->fix_divider) * + div->fix_divider; + scaled.max_rate =3D min(req->max_rate, + ULONG_MAX / div->fix_divider) * + div->fix_divider; + } + + ret =3D divider_determine_rate(hw, &scaled, NULL, div->width, + div->flags); + if (ret) + return ret; + + req->rate =3D scaled.rate; + req->best_parent_rate =3D scaled.best_parent_rate; + req->best_parent_hw =3D scaled.best_parent_hw; + if (div->fix_divider) + do_div(req->rate, div->fix_divider); + + return 0; +} + +static int amb_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct amb_div *div =3D to_amb_div(hw); + int val, mask; + + if (div->fix_divider) + rate *=3D div->fix_divider; + + if (!rate) { + val =3D BIT(div->width); + } else { + val =3D divider_get_val(rate, parent_rate, NULL, + div->width, div->flags); + if (val < 0) + return val; + } + + mask =3D (div->flags & CLK_DIVIDER_ONE_BASED) ? + clk_div_mask(div->width + 1) : clk_div_mask(div->width); + + regmap_update_bits(div->map, div->offset, mask << div->shift, + val << div->shift); + + if (!(div->flags & CLK_DIVIDER_ONE_BASED)) { + regmap_update_bits(div->map, div->offset, BIT(0), BIT(0)); + regmap_update_bits(div->map, div->offset, BIT(0), 0); + } + + return 0; +} + +static const struct clk_ops amb_div_ops =3D { + .recalc_rate =3D amb_div_recalc_rate, + .determine_rate =3D amb_div_determine_rate, + .set_rate =3D amb_div_set_rate, +}; + +struct clk_hw *amb_div_register(struct device *dev, struct regmap *map, + const char *name, const struct clk_hw *parent, + u32 div_reg, u32 div_shift, u32 div_width, + u32 div_flags, u32 fix_divider) +{ + struct amb_div *div; + struct clk_init_data init =3D {}; + int ret; + + div =3D devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->map =3D map; + div->offset =3D div_reg; + div->shift =3D div_shift; + div->width =3D div_width; + div->flags =3D div_flags; + div->fix_divider =3D fix_divider; + + init.name =3D name; + init.ops =3D &amb_div_ops; + init.parent_hws =3D &parent; + init.num_parents =3D 1; + + div->hw.init =3D &init; + + ret =3D devm_clk_hw_register(dev, &div->hw); + if (ret) + return ERR_PTR(ret); + + return &div->hw; +} + +struct clk_hw *amb_mux_div_register(struct device *dev, struct regmap *map, + const struct amb_mux_div_desc *desc, + const struct clk_parent_data *parent_data) +{ + struct amb_mux *mux; + struct amb_div *div; + + mux =3D devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + div =3D devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!mux || !div) + return ERR_PTR(-ENOMEM); + + mux->map =3D map; + mux->offset =3D desc->mux_reg; + mux->shift =3D desc->mux_shift; + mux->mask =3D desc->mux_mask; + + div->map =3D map; + div->offset =3D desc->div_reg; + div->shift =3D desc->div_shift; + div->width =3D desc->div_width; + div->flags =3D desc->div_flags; + div->fix_divider =3D desc->fix_divider; + + return devm_clk_hw_register_composite_pdata(dev, desc->name, + parent_data, + desc->num_parents, + &mux->hw, &amb_mux_ops, + &div->hw, &amb_div_ops, + NULL, NULL, + CLK_SET_RATE_NO_REPARENT); +} diff --git a/drivers/clk/ambarella/ccu_mux_div.h b/drivers/clk/ambarella/cc= u_mux_div.h new file mode 100644 index 000000000000..abaefff1201a --- /dev/null +++ b/drivers/clk/ambarella/ccu_mux_div.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#ifndef __CCU_MUX_DIV_H +#define __CCU_MUX_DIV_H + +#include +#include + +struct amb_mux_div_desc { + const char *name; + const int *parents; + u8 num_parents; + u32 mux_reg; + u32 mux_shift; + u32 mux_mask; + u32 div_reg; + u32 div_shift; + u32 div_width; + u32 div_flags; + u32 fix_divider; +}; + +struct clk_hw *amb_mux_div_register(struct device *dev, struct regmap *map, + const struct amb_mux_div_desc *desc, + const struct clk_parent_data *parent_data); + +struct clk_hw *amb_div_register(struct device *dev, struct regmap *map, + const char *name, const struct clk_hw *parent, + u32 div_reg, u32 div_shift, u32 div_width, + u32 div_flags, u32 fix_divider); + +#endif diff --git a/drivers/clk/ambarella/ccu_pll.c b/drivers/clk/ambarella/ccu_pl= l.c new file mode 100644 index 000000000000..1293836e222e --- /dev/null +++ b/drivers/clk/ambarella/ccu_pll.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ccu_pll.h" + +#define AMB_PLL_MAX_SOUT 16UL +#define AMB_PLL_MAX_SDIV 16UL + +struct amb_pll { + struct clk_hw hw; + struct regmap *map; + u32 reg_offset[REG_NUM]; + const struct amb_pll_soc_data *soc_data; + u32 fix_divider; + bool frac_mode; +}; + +#define to_amb_pll(_hw) container_of(_hw, struct amb_pll, hw) + +static void amb_pll_write_enable(struct regmap *map, u32 offset, u32 val) +{ + regmap_write(map, offset, val); + regmap_write(map, offset, val | CTRL_WRITE_ENABLE); + regmap_write(map, offset, val); +} + +static unsigned long amb_pll_calc_vco(struct amb_pll *pll, + unsigned long parent_rate) +{ + const struct amb_pll_soc_data *soc_data =3D pll->soc_data; + u32 *reg =3D pll->reg_offset; + u32 pre_scaler =3D 1; + u32 ctrl_val, ctrl2_val =3D 0, frac_val; + u32 intp, sdiv, vcodiv, fsdiv; + u64 frac =3D 0, vco; + + if (reg[PRES_OFFSET]) { + regmap_read(pll->map, reg[PRES_OFFSET], &pre_scaler); + pre_scaler =3D (pre_scaler >> 4) + 1; + } + + regmap_read(pll->map, reg[CTRL_OFFSET], &ctrl_val); + intp =3D ((ctrl_val >> 24) & 0x7f) + 1; + sdiv =3D ((ctrl_val >> 12) & 0xf) + 1; + + if (soc_data->pll_version >=3D 2) { + vcodiv =3D (ctrl_val & soc_data->vcodiv_mask) =3D=3D + soc_data->vcodiv_val ? 2 : 1; + fsdiv =3D (ctrl_val & soc_data->fsdiv_mask) =3D=3D + soc_data->fsdiv_val ? 2 : 1; + } else { + regmap_read(pll->map, reg[CTRL2_OFFSET], &ctrl2_val); + vcodiv =3D (ctrl2_val & soc_data->vcodiv_mask) =3D=3D + soc_data->vcodiv_val ? 2 : 1; + fsdiv =3D (ctrl2_val & soc_data->fsdiv_mask) =3D=3D + soc_data->fsdiv_val ? 2 : 1; + } + + vco =3D (u64)parent_rate * vcodiv * fsdiv * intp * sdiv; + vco =3D div_u64(vco, pre_scaler); + + if (ctrl_val & CTRL_FRAC_MODE) { + regmap_read(pll->map, reg[FRAC_OFFSET], &frac_val); + frac =3D (u64)parent_rate * vcodiv * fsdiv * sdiv * frac_val; + frac =3D div_u64(frac, pre_scaler) >> 32; + } + + return vco + frac; +} + +static unsigned long amb_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct amb_pll *pll =3D to_amb_pll(hw); + const struct amb_pll_soc_data *soc_data =3D pll->soc_data; + u32 *reg =3D pll->reg_offset; + u32 pre_scaler =3D 1, post_scaler =3D 1; + u32 ctrl_val, ctrl2_val =3D 0; + u32 vcodiv, fsout, sout; + u64 rate; + + regmap_read(pll->map, reg[CTRL_OFFSET], &ctrl_val); + if (ctrl_val & (CTRL_POWER_DOWN | CTRL_HALT_VCO | CTRL_FORCE_RESET)) + return 0; + + if (reg[PRES_OFFSET]) { + regmap_read(pll->map, reg[PRES_OFFSET], &pre_scaler); + pre_scaler =3D (pre_scaler >> 4) + 1; + } + + if (reg[POST_OFFSET]) { + regmap_read(pll->map, reg[POST_OFFSET], &post_scaler); + post_scaler =3D (post_scaler >> 4) + 1; + } + + if (ctrl_val & CTRL_BYPASS) + return parent_rate / pre_scaler / post_scaler; + + if (soc_data->pll_version >=3D 2) { + vcodiv =3D (ctrl_val & soc_data->vcodiv_mask) =3D=3D + soc_data->vcodiv_val ? 2 : 1; + fsout =3D (ctrl_val & soc_data->fsout_mask) =3D=3D + soc_data->fsout_val ? 2 : 1; + } else { + regmap_read(pll->map, reg[CTRL2_OFFSET], &ctrl2_val); + vcodiv =3D (ctrl2_val & soc_data->vcodiv_mask) =3D=3D + soc_data->vcodiv_val ? 2 : 1; + fsout =3D (ctrl2_val & soc_data->fsout_mask) =3D=3D + soc_data->fsout_val ? 2 : 1; + } + + sout =3D ((ctrl_val >> 16) & 0xf) + 1; + rate =3D amb_pll_calc_vco(pll, parent_rate); + + if (soc_data->pll_version >=3D 2) { + if (!(ctrl_val & CTRL_BYPASS_HSDIV)) + rate =3D div_u64(rate, vcodiv * fsout * sout); + } else { + if (!(ctrl2_val & CTRL2_BYPASS_HSDIV)) + rate =3D div_u64(rate, vcodiv * fsout * sout); + } + + rate =3D div_u64(rate, pll->fix_divider * post_scaler); + + return rate; +} + +static int amb_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct amb_pll *pll =3D to_amb_pll(hw); + unsigned long half_refclk =3D req->best_parent_rate / 2; + + if (pll->frac_mode) + return 0; + + if (!half_refclk) + return 0; + + req->rate =3D roundup(req->rate, half_refclk); + + return 0; +} + +static int amb_pll_calc_params(struct amb_pll *pll, unsigned long rate, + unsigned long parent_rate, u32 ctrl2_val, + u32 *intp, u32 *sdiv, u32 *sout, + u32 *vcodiv, u32 *fsdiv, u32 *fsout) +{ + const struct amb_pll_soc_data *soc_data =3D pll->soc_data; + unsigned long max_numerator, max_denominator; + unsigned long intp_ul, sout_ul, rate_tmp; + u32 ctrl_val; + + *sdiv =3D 1; + + if (soc_data->pll_version >=3D 2) { + ctrl_val =3D 0; + *vcodiv =3D (ctrl_val & soc_data->vcodiv_mask) =3D=3D + soc_data->vcodiv_val ? 2 : 1; + *fsdiv =3D (ctrl_val & soc_data->fsdiv_mask) =3D=3D + soc_data->fsdiv_val ? 2 : 1; + *fsout =3D (ctrl_val & soc_data->fsout_mask) =3D=3D + soc_data->fsout_val ? 2 : 1; + } else { + *vcodiv =3D (ctrl2_val & soc_data->vcodiv_mask) =3D=3D + soc_data->vcodiv_val ? 2 : 1; + *fsdiv =3D (ctrl2_val & soc_data->fsdiv_mask) =3D=3D + soc_data->fsdiv_val ? 2 : 1; + *fsout =3D (ctrl2_val & soc_data->fsout_mask) =3D=3D + soc_data->fsout_val ? 2 : 1; + } + + if (rate < parent_rate) + return -EINVAL; + + max_numerator =3D soc_data->vco_max_mhz; + max_numerator =3D div_u64(max_numerator * 1000000ULL, parent_rate); + max_numerator =3D div_u64(max_numerator, *vcodiv * *fsdiv); + max_numerator =3D min(128UL, max_numerator); + if (!max_numerator) + return -EINVAL; + + max_denominator =3D AMB_PLL_MAX_SOUT; + rate_tmp =3D rate; + rational_best_approximation(rate_tmp, parent_rate, max_numerator, + max_denominator, &intp_ul, &sout_ul); + + while (parent_rate * *fsdiv * intp_ul * *sdiv / *fsout / sout_ul > + rate) { + unsigned long resolution =3D parent_rate / AMB_PLL_MAX_SOUT; + + if (rate_tmp <=3D resolution) + return -EINVAL; + + rate_tmp -=3D resolution; + rational_best_approximation(rate_tmp, parent_rate, + max_numerator, max_denominator, + &intp_ul, &sout_ul); + } + + while (parent_rate / 1000000 * *vcodiv * *fsdiv * intp_ul * *sdiv < + soc_data->vco_min_mhz) { + if (sout_ul > 8 || intp_ul > 64) + break; + + intp_ul *=3D 2; + sout_ul *=3D 2; + } + + if (intp_ul > max_numerator || sout_ul > max_denominator || + *sdiv > AMB_PLL_MAX_SDIV) + return -EINVAL; + + *intp =3D intp_ul; + *sout =3D sout_ul; + + return 0; +} + +static int amb_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct amb_pll *pll =3D to_amb_pll(hw); + const struct amb_pll_soc_data *soc_data =3D pll->soc_data; + u32 *reg =3D pll->reg_offset; + u32 ctrl_val, ctrl2_val =3D 0, ctrl3_val, frac_val =3D 0; + u32 intp, sdiv, sout, vcodiv, fsdiv, fsout; + unsigned long old_rate, new_rate, rate_tmp; + int ret; + + if (!rate) { + regmap_read(pll->map, reg[CTRL_OFFSET], &ctrl_val); + ctrl_val |=3D CTRL_POWER_DOWN | CTRL_HALT_VCO; + amb_pll_write_enable(pll->map, reg[CTRL_OFFSET], ctrl_val); + return 0; + } + + rate *=3D pll->fix_divider; + + if (soc_data->ctrl2_val) + ctrl2_val =3D soc_data->ctrl2_val; + else + regmap_read(pll->map, reg[CTRL2_OFFSET], &ctrl2_val); + + ret =3D amb_pll_calc_params(pll, rate, parent_rate, ctrl2_val, + &intp, &sdiv, &sout, &vcodiv, &fsdiv, + &fsout); + if (ret) + return ret; + + if (soc_data->ctrl2_val) + regmap_write(pll->map, reg[CTRL2_OFFSET], soc_data->ctrl2_val); + + ctrl_val =3D ((intp - 1) & 0x7f) << 24; + ctrl_val |=3D ((sdiv - 1) & 0xf) << 12; + ctrl_val |=3D ((sout - 1) & 0xf) << 16; + if (soc_data->pll_version >=3D 2) { + ctrl_val |=3D vcodiv =3D=3D 2 ? soc_data->vcodiv_val : 0; + ctrl_val |=3D fsdiv =3D=3D 2 ? soc_data->fsdiv_val : 0; + ctrl_val |=3D fsout =3D=3D 2 ? soc_data->fsout_val : 0; + } + + regmap_write(pll->map, reg[CTRL_OFFSET], ctrl_val); + regmap_write(pll->map, reg[FRAC_OFFSET], 0); + + old_rate =3D amb_pll_recalc_rate(hw, parent_rate) * pll->fix_divider; + rate_tmp =3D old_rate > rate ? 0 : rate - old_rate; + if (rate_tmp && pll->frac_mode) { + u64 dividend, divider; + + dividend =3D (u64)rate_tmp * sout * fsout; + dividend <<=3D 32; + divider =3D (u64)sdiv * fsdiv * parent_rate; + frac_val =3D DIV_ROUND_CLOSEST_ULL(dividend, divider); + regmap_write(pll->map, reg[FRAC_OFFSET], frac_val); + ctrl_val |=3D CTRL_FRAC_MODE; + } + + if (soc_data->pll_version >=3D 2) { + ctrl3_val =3D soc_data->ctrl3_val; + regmap_write(pll->map, reg[CTRL3_OFFSET], + ctrl3_val | CTRL3_VCO_CLAMP); + regmap_write(pll->map, reg[CTRL_OFFSET], + ctrl_val | CTRL_FORCE_RESET); + ndelay(100); + regmap_write(pll->map, reg[CTRL_OFFSET], + ctrl_val & ~CTRL_FORCE_RESET); + ndelay(100); + regmap_write(pll->map, reg[CTRL3_OFFSET], + ctrl3_val & ~CTRL3_VCO_CLAMP); + } else { + u32 fvco_mhz, range; + + fvco_mhz =3D amb_pll_calc_vco(pll, parent_rate) / 1000000UL; + for (range =3D 0; range < ARRAY_SIZE(soc_data->vco_range); + range++) { + if (fvco_mhz > soc_data->vco_range[range]) + break; + } + range =3D ARRAY_SIZE(soc_data->vco_range) - range - 1; + + regmap_read(pll->map, reg[CTRL3_OFFSET], &ctrl3_val); + ctrl3_val &=3D ~CTRL3_VCO_RANGE_MASK; + ctrl3_val |=3D range << 1; + regmap_write(pll->map, reg[CTRL3_OFFSET], ctrl3_val); + + if (frac_val) { + ctrl_val |=3D CTRL_FORCE_RESET; + amb_pll_write_enable(pll->map, reg[CTRL_OFFSET], + ctrl_val); + } + + ctrl_val &=3D ~CTRL_FORCE_RESET; + amb_pll_write_enable(pll->map, reg[CTRL_OFFSET], ctrl_val); + } + + new_rate =3D amb_pll_recalc_rate(hw, parent_rate); + rate_tmp =3D rate / pll->fix_divider; + if (max(new_rate, rate_tmp) - min(new_rate, rate_tmp) > 10) + pr_warn("%s: requested %lu, got %lu\n", clk_hw_get_name(hw), + rate_tmp, new_rate); + + return 0; +} + +static const struct clk_ops amb_pll_ops =3D { + .recalc_rate =3D amb_pll_recalc_rate, + .determine_rate =3D amb_pll_determine_rate, + .set_rate =3D amb_pll_set_rate, +}; + +struct clk_hw *amb_pll_register(struct device *dev, struct regmap *map, + const struct amb_pll_desc *desc) +{ + struct amb_pll *pll; + struct clk_init_data init =3D {}; + const struct clk_hw *parent =3D desc->parent; + int ret; + + pll =3D devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->map =3D map; + memcpy(pll->reg_offset, desc->reg_offset, sizeof(pll->reg_offset)); + pll->soc_data =3D desc->soc_data; + pll->fix_divider =3D 1; + pll->frac_mode =3D desc->frac_mode; + + init.name =3D desc->name; + init.ops =3D &amb_pll_ops; + init.flags =3D CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL; + init.parent_hws =3D &parent; + init.num_parents =3D 1; + + pll->hw.init =3D &init; + + ret =3D devm_clk_hw_register(dev, &pll->hw); + if (ret) + return ERR_PTR(ret); + + return &pll->hw; +} diff --git a/drivers/clk/ambarella/ccu_pll.h b/drivers/clk/ambarella/ccu_pl= l.h new file mode 100644 index 000000000000..b9e453e9debf --- /dev/null +++ b/drivers/clk/ambarella/ccu_pll.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#ifndef __CCU_PLL_H +#define __CCU_PLL_H + +#include +#include +#include +#include +#include + +enum { + CTRL_OFFSET =3D 0, + FRAC_OFFSET, + CTRL2_OFFSET, + CTRL3_OFFSET, + PRES_OFFSET, + POST_OFFSET, + REG_NUM, +}; + +#define CTRL_BYPASS BIT(2) +#define CTRL_WRITE_ENABLE BIT(0) +#define CTRL_FRAC_MODE BIT(3) +#define CTRL_FORCE_RESET BIT(4) +#define CTRL_POWER_DOWN BIT(5) +#define CTRL_HALT_VCO BIT(6) +#define CTRL_VCODIV_DIV2 BIT(8) +#define CTRL_FSDIV_DIV2 BIT(9) +#define CTRL_FSOUT_DIV2 BIT(10) +#define CTRL_BYPASS_HSDIV BIT(11) + +#define CTRL2_VCODIV_DIV2 BIT(8) +#define CTRL2_FSDIV_DIV2 BIT(9) +#define CTRL2_FSOUT_DIV2 BIT(11) +#define CTRL2_BYPASS_HSDIV BIT(12) + +#define CTRL3_VCO_RANGE_MASK 0x6 +#define CTRL3_VCO_CLAMP 0x8 + +struct amb_pll_soc_data { + u32 pll_version; + u32 fsout_mask; + u32 fsout_val; + u32 fsdiv_mask; + u32 fsdiv_val; + u32 vcodiv_mask; + u32 vcodiv_val; + u32 vco_max_mhz; + u32 vco_min_mhz; + u32 vco_range[4]; + u32 ctrl2_val; + u32 ctrl3_val; +}; + +struct amb_pll_desc { + const char *name; + const struct clk_hw *parent; + const u32 reg_offset[REG_NUM]; + const struct amb_pll_soc_data *soc_data; + bool frac_mode; +}; + +struct clk_hw *amb_pll_register(struct device *dev, struct regmap *map, + const struct amb_pll_desc *desc); + +#endif --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 32253412C17; Thu, 6 Aug 2026 09:34:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; cv=none; b=rDIJPI+oclUk71Y7WCIrQ0TbTIzT3vyHEYureW513He3KREs/8a871DuIYsVZUjm08OLM4oOfVCMN08pRZz6+Pz2xLczZQN0uIvaS6PZcrhuAOf0TnluU5v9QcOiCNN0sh6hUd+E5NVNdxmca474AKjzTf79kWd2yYI9+v22mZs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; c=relaxed/simple; bh=KGwMsXxm3MeXtb6DeM40aSUujp1xaMEREwVIF5R/HV8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TPCOjpR5h6MkREZxDx/Bdt3t0S/lSjnjzXfg0QcmZH2cF/JKVCafC06nsKLDlOmx/F35xxPpxIHW2J178w2w14N8bFoCj9FCImsE8dTwGEYuMQ82PjSb7IB5ySrxO/Eh2d4UMAES6lyAvjGCk8UqgSn5qlhccty/JlXnnb408CM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=a5m6VggE; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="a5m6VggE" Received: by smtp.kernel.org (Postfix) with ESMTPS id C3E06C2BCC7; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=KGwMsXxm3MeXtb6DeM40aSUujp1xaMEREwVIF5R/HV8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=a5m6VggEgxRizjgxJx3+npiVIT2cxEZjAznSeqpDEoZxM7hKOnNGDH4i4b1cLfyHc TGAusHX+OP1UrJk5/3Jp8zYjIQJqH8FG2ibW73ERrg1dF34o6QoEjKZC89LIthEqZ3 ncDuUMMgUB8IwsKYudvVV8tJxt80RALpQ8Wuoc3qEAl58GGGs7qi36sQKM6tgD5dM6 jizKDu5uYZ4P0FUWomLuz6pRv4RZIOi2355eOhGNBKwWq7FLplVwznHcaF/sHJpyGk iT1J5RXOFNViM3g87gPjTJnMG2kgkaAMRySMl/7Un2Np966hoUEK6tz8M9ES/dNzKv eFeqfX/f5hkRw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id B095EC56208; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:15 +0800 Subject: [PATCH v2 07/10] pinctrl: ambarella: add Ambarella pin controller Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-7-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=51475; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=Aany7zv8x19mNI8q2TIbIh9hrCY7g/e+JIAmKICLCkA=; b=qxbcXfXRo16X77HAkb10oJzvHysj16HmGS348Xr0AZyoodF8raFe/0lkFfDNAdGxvrHjEhr7A ut3FE2YKJrFA/VKAao6AdZFvzil3IIGWncJ9s3M0v1PvYEu9cWekneS X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Add the Ambarella pinmux/GPIO controller driver used by CV75. Signed-off-by: Long Zhao --- drivers/pinctrl/Kconfig | 15 + drivers/pinctrl/Makefile | 2 + drivers/pinctrl/pinctrl-ambarella-cv75.c | 68 ++ drivers/pinctrl/pinctrl-ambarella.c | 1510 ++++++++++++++++++++++++++= ++++ drivers/pinctrl/pinctrl-ambarella.h | 48 + 5 files changed, 1643 insertions(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c2cdd7b2c49b..572a4a898a99 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -37,6 +37,21 @@ config DEBUG_PINCTRL help Say Y here to add some extra checks and diagnostics to PINCTRL calls. =20 +config PINCTRL_AMB + bool "Ambarella pin controller support" + depends on ARCH_AMBARELLA || COMPILE_TEST + select GENERIC_PINCONF + select GPIOLIB + select GPIOLIB_IRQCHIP + select MFD_SYSCON + select PINCONF + select PINMUX + help + Say Y here to enable the pin controller found on Ambarella SoCs. + The driver provides pin multiplexing and pin configuration as well + as GPIO and GPIO interrupt support. It is required to configure + peripheral pins on systems using an Ambarella SoC. + config PINCTRL_AMD bool "AMD GPIO pin control" depends on HAS_IOMEM diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index a35d71135abf..ce0825f4fc1b 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_GENERIC_PINCONF) +=3D pinconf-generic.o obj-$(CONFIG_GENERIC_PINCTRL) +=3D pinctrl-generic.o obj-$(CONFIG_OF) +=3D devicetree.o =20 +obj-$(CONFIG_PINCTRL_AMB) +=3D pinctrl-ambarella.o +obj-$(CONFIG_PINCTRL_AMB) +=3D pinctrl-ambarella-cv75.o obj-$(CONFIG_PINCTRL_AMD) +=3D pinctrl-amd.o obj-$(CONFIG_PINCTRL_AMDISP) +=3D pinctrl-amdisp.o obj-$(CONFIG_PINCTRL_APPLE_GPIO) +=3D pinctrl-apple-gpio.o diff --git a/drivers/pinctrl/pinctrl-ambarella-cv75.c b/drivers/pinctrl/pin= ctrl-ambarella-cv75.c new file mode 100644 index 000000000000..af4641bafebb --- /dev/null +++ b/drivers/pinctrl/pinctrl-ambarella-cv75.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Ambarella CV75 pinctrl data + * + * Copyright (C) 2026, Ambarella, Inc. + */ + +#include + +#include "pinctrl-ambarella.h" + +static const u32 cv75_uart0tx_pinmux[] =3D { + AMBA_PINMUX(44, 1), +}; + +static const u32 cv75_uart0rx_pinmux[] =3D { + AMBA_PINMUX(45, 1), +}; + +static const struct ambpin_group_desc cv75_pin_groups[] =3D { + { + .name =3D "uart0tx", + .pinmux =3D cv75_uart0tx_pinmux, + .num_pins =3D ARRAY_SIZE(cv75_uart0tx_pinmux), + }, + { + .name =3D "uart0rx", + .pinmux =3D cv75_uart0rx_pinmux, + .num_pins =3D ARRAY_SIZE(cv75_uart0rx_pinmux), + }, +}; + +static const char * const cv75_uart0_groups[] =3D { + "uart0tx", + "uart0rx", +}; + +static const struct ambpin_function cv75_pin_functions[] =3D { + { + .name =3D "uart0", + .groups =3D cv75_uart0_groups, + .num_groups =3D ARRAY_SIZE(cv75_uart0_groups), + }, +}; + +const struct amb_pinctrl_data ambarella_cv75_pinctrl_data =3D { + .ds0 =3D { + 0x314, 0x320, 0x32c, + }, + .ds1 =3D { + 0x318, 0x324, 0x330, + }, + .ds2 =3D { + 0x31c, 0x328, 0x334, + }, + .pull_en =3D { + 0x60, 0x64, 0x68, + }, + .pull_dir =3D { + 0x7c, 0x80, 0x84, + }, + .have_ds2 =3D true, + .clk_au_dedicated_pin =3D 96, + .groups =3D cv75_pin_groups, + .nr_groups =3D ARRAY_SIZE(cv75_pin_groups), + .functions =3D cv75_pin_functions, + .nr_functions =3D ARRAY_SIZE(cv75_pin_functions), +}; diff --git a/drivers/pinctrl/pinctrl-ambarella.c b/drivers/pinctrl/pinctrl-= ambarella.c new file mode 100644 index 000000000000..bb79e06c4160 --- /dev/null +++ b/drivers/pinctrl/pinctrl-ambarella.c @@ -0,0 +1,1510 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Pinctrl driver for Ambarella SoCs + * + * History: + * 2013/12/18 - [Cao Rongrong] created file + * + * Copyright (C) 2012-2026, Ambarella, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinconf.h" +#include "pinctrl-ambarella.h" + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D*/ + +#define GPIO_DATA_OFFSET 0x00 +#define GPIO_DIR_OFFSET 0x04 +#define GPIO_IS_OFFSET 0x08 +#define GPIO_IBE_OFFSET 0x0c +#define GPIO_IEV_OFFSET 0x10 +#define GPIO_IE_OFFSET 0x14 +#define GPIO_AFSEL_OFFSET 0x18 +#define GPIO_RIS_OFFSET 0x1c +#define GPIO_MIS_OFFSET 0x20 +#define GPIO_IC_OFFSET 0x24 +#define GPIO_MASK_OFFSET 0x28 +#define GPIO_ENABLE_OFFSET 0x2c + +#define IOMUX_OFFSET(bank, n) (((bank) * 0xc) + ((n) * 4)) +#define IOMUX_CTRL_SET_OFFSET 0xf0 + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D*/ + +#define AMBA_MAX_PINS (AMBA_MAX_BANKS * 32) + +#define PINID_TO_BANK(p) ((p) >> 5) +#define PINID_TO_OFFSET(p) ((p) & 0x1f) + +struct amb_pinctrl_pm_state { + u32 iomux[3]; + u32 pull[2]; + u32 ds[3]; + u32 data; + u32 dir; + u32 is; + u32 ibe; + u32 iev; + u32 ie; + u32 afsel; + u32 mask; +}; + +struct ambpin_group { + const char *name; + const u32 *pinmux; + unsigned int *pins; + unsigned int num_pins; +}; + +struct amb_pinctrl_soc_data; + +struct amb_gpio_bank { + struct amb_pinctrl_soc_data *soc; + void __iomem *base; + struct gpio_chip gc; + struct irq_domain *domain; + unsigned int pin_base; + unsigned int hw_id; + unsigned int irq_wake_mask; + int irq; +}; + +struct amb_pinctrl_soc_data { + struct device *dev; + const struct amb_pinctrl_data *data; + struct amb_gpio_bank banks[AMBA_MAX_BANKS]; + void __iomem *iomux_base; + struct regmap *ds_regmap; + struct regmap *pull_regmap; + unsigned int bank_num; + unsigned int npins; + unsigned long used[BITS_TO_LONGS(AMBA_MAX_PINS)]; + raw_spinlock_t lock; + + struct pinctrl_dev *pctl; + + const struct ambpin_function *functions; + unsigned int nr_functions; + struct ambpin_group *groups; + unsigned int nr_groups; + + struct amb_pinctrl_pm_state pm[AMBA_MAX_BANKS]; +}; + +static struct amb_gpio_bank *amb_irq_data_to_bank(struct irq_data *data) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(data); + + return gpiochip_get_data(gc); +} + +static struct amb_gpio_bank * +amb_pin_to_bank(struct amb_pinctrl_soc_data *soc, unsigned int pin) +{ + unsigned int i; + + for (i =3D 0; i < soc->bank_num; i++) { + struct amb_gpio_bank *bank =3D &soc->banks[i]; + + if (pin >=3D bank->pin_base && + pin < bank->pin_base + bank->gc.ngpio) + return bank; + } + + return NULL; +} + +/* Exclusive end of the GPIO pin number space (max pin_base + ngpio). */ +static unsigned int amb_gpio_pins_end(const struct amb_pinctrl_soc_data *s= oc) +{ + unsigned int end =3D 0, i; + + for (i =3D 0; i < soc->bank_num; i++) + end =3D max(end, soc->banks[i].pin_base + soc->banks[i].gc.ngpio); + + return end; +} + +/* check if the selector is a valid pin group selector */ +static int amb_get_group_count(struct pinctrl_dev *pctldev) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + return soc->nr_groups; +} + +/* return the name of the group selected by the group selector */ +static const char *amb_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + return soc->groups[selector].name; +} + +/* return the pin numbers associated with the specified group */ +static int amb_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, const unsigned int **pins, + unsigned int *num_pins) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + *pins =3D soc->groups[selector].pins; + *num_pins =3D soc->groups[selector].num_pins; + + return 0; +} + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static void amb_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + seq_printf(s, " %s", pinctrl_dev_get_devname(pctldev)); +} +#endif + +/* list of pinctrl callbacks for the pinctrl core */ +static const struct pinctrl_ops amb_pctrl_ops =3D { + .get_groups_count =3D amb_get_group_count, + .get_group_name =3D amb_get_group_name, + .get_group_pins =3D amb_get_group_pins, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .pin_dbg_show =3D amb_pin_dbg_show, +#endif + .dt_node_to_map =3D pinconf_generic_dt_node_to_map_all, + .dt_free_map =3D pinconf_generic_dt_free_map, +}; + +/* check if the selector is a valid pin function selector */ +static int amb_pinmux_request(struct pinctrl_dev *pctldev, unsigned int pi= n) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + if (test_and_set_bit(pin, soc->used)) + return -EBUSY; + + return 0; +} + +/* check if the selector is a valid pin function selector */ +static int amb_pinmux_free(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + clear_bit(pin, soc->used); + + return 0; +} + +/* check if the selector is a valid pin function selector */ +static int amb_pinmux_get_fcount(struct pinctrl_dev *pctldev) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + return soc->nr_functions; +} + +/* return the name of the pin function specified */ +static const char *amb_pinmux_get_fname(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + return soc->functions[selector].name; +} + +/* return the groups associated for the specified function selector */ +static int amb_pinmux_get_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + *groups =3D soc->functions[selector].groups; + *num_groups =3D soc->functions[selector].num_groups; + + return 0; +} + +static void amb_pinmux_set_altfunc(struct amb_pinctrl_soc_data *soc, + u32 bank, u32 offset, u32 altfunc) +{ + u32 i, data; + + /* On CV3 platform, only ARM cluster0 (safety domain) can access pinctrl = registers */ + if (soc->data->hsm_domain_id !=3D 0) + return; + + for (i =3D 0; i < 3; i++) { + data =3D readl_relaxed(soc->iomux_base + IOMUX_OFFSET(bank, i)); + data &=3D (~(0x1 << offset)); + data |=3D (((altfunc >> i) & 0x1) << offset); + writel_relaxed(data, soc->iomux_base + IOMUX_OFFSET(bank, i)); + } + + writel_relaxed(0x1, soc->iomux_base + IOMUX_CTRL_SET_OFFSET); + writel_relaxed(0x0, soc->iomux_base + IOMUX_CTRL_SET_OFFSET); +} + +/* enable a specified pinmux by writing to registers */ +static int amb_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned int selector, unsigned int group) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + const struct ambpin_group *grp; + u32 i, pin, alt, bank, offset; + unsigned long flags; + + grp =3D &soc->groups[group]; + + raw_spin_lock_irqsave(&soc->lock, flags); + for (i =3D 0; i < grp->num_pins; i++) { + pin =3D AMBA_PINMUX_TO_PIN(grp->pinmux[i]); + alt =3D AMBA_PINMUX_TO_ALT(grp->pinmux[i]); + bank =3D PINID_TO_BANK(pin); + offset =3D PINID_TO_OFFSET(pin); + amb_pinmux_set_altfunc(soc, bank, offset, alt); + } + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; +} + +static int amb_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + u32 bank, offset; + unsigned long flags; + + if (!range || !range->gc) { + dev_err(soc->dev, "invalid range: %p\n", range); + return -EINVAL; + } + + if (test_and_set_bit(pin, soc->used)) + return -EBUSY; + + bank =3D PINID_TO_BANK(pin); + offset =3D PINID_TO_OFFSET(pin); + + raw_spin_lock_irqsave(&soc->lock, flags); + amb_pinmux_set_altfunc(soc, bank, offset, 0); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; +} + +static void amb_pinmux_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + + dev_dbg(soc->dev, "disable pin %u as GPIO\n", pin); + /* Set the pin to some default state, GPIO is usually default */ + + clear_bit(pin, soc->used); +} + +/* list of pinmux callbacks for the pinmux vertical in pinctrl core */ +static const struct pinmux_ops amb_pinmux_ops =3D { + .request =3D amb_pinmux_request, + .free =3D amb_pinmux_free, + .get_functions_count =3D amb_pinmux_get_fcount, + .get_function_name =3D amb_pinmux_get_fname, + .get_function_groups =3D amb_pinmux_get_groups, + .set_mux =3D amb_pinmux_set_mux, + .gpio_request_enable =3D amb_pinmux_gpio_request_enable, + .gpio_disable_free =3D amb_pinmux_gpio_disable_free, +}; + +static int amb_drive_strength_to_reg(struct amb_pinctrl_soc_data *soc, + u32 strength) +{ + if (soc->data->have_ds2) { + switch (strength) { + case 3: + return 0; + case 4: + case 5: + return 1; + case 6: + return 2; + case 7: + case 8: + return 3; + case 9: + return 4; + case 12: + return 5; + default: + return -EINVAL; + } + } + + switch (strength) { + case 2: + return 0; + case 4: + return 1; + case 8: + return 2; + case 12: + return 3; + default: + return -EINVAL; + } +} + +static int amb_reg_to_drive_strength(struct amb_pinctrl_soc_data *soc, u32= ds) +{ + static const int ds2_ma[] =3D { 3, 4, 6, 8, 9, 12 }; + static const int ds_ma[] =3D { 2, 4, 8, 12 }; + + if (soc->data->have_ds2) { + if (ds >=3D ARRAY_SIZE(ds2_ma)) + return -EINVAL; + + return ds2_ma[ds]; + } + + if (ds >=3D ARRAY_SIZE(ds_ma)) + return -EINVAL; + + return ds_ma[ds]; +} + +/* set the pin config settings for a specified pin */ +static int amb_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + struct amb_gpio_bank *gpio_bank; + u32 i, bank, offset; + unsigned long config, flags; + enum pin_config_param param; + u32 arg; + int ds; + + gpio_bank =3D amb_pin_to_bank(soc, pin); + if (!gpio_bank) + return -EINVAL; + + bank =3D gpio_bank->hw_id; + offset =3D pin - gpio_bank->pin_base; + + raw_spin_lock_irqsave(&soc->lock, flags); + for (i =3D 0; i < num_configs; i++) { + config =3D configs[i]; + param =3D pinconf_to_config_param(config); + arg =3D pinconf_to_config_argument(config); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + regmap_update_bits(soc->pull_regmap, + soc->data->pull_en[bank], BIT(offset), 0); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + regmap_update_bits(soc->pull_regmap, soc->data->pull_dir[bank], + BIT(offset), + (param =3D=3D PIN_CONFIG_BIAS_PULL_UP) ? + BIT(offset) : 0); + regmap_update_bits(soc->pull_regmap, soc->data->pull_en[bank], + BIT(offset), BIT(offset)); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + ds =3D amb_drive_strength_to_reg(soc, arg); + if (ds < 0) { + raw_spin_unlock_irqrestore(&soc->lock, flags); + return ds; + } + if (soc->data->have_ds2) { + regmap_update_bits(soc->ds_regmap, + soc->data->ds0[bank], BIT(offset), + (ds & BIT(0)) ? BIT(offset) : 0); + regmap_update_bits(soc->ds_regmap, + soc->data->ds1[bank], BIT(offset), + (ds & BIT(1)) ? BIT(offset) : 0); + regmap_update_bits(soc->ds_regmap, + soc->data->ds2[bank], BIT(offset), + (ds & BIT(2)) ? BIT(offset) : 0); + } else { + regmap_update_bits(soc->ds_regmap, + soc->data->ds0[bank], BIT(offset), + (ds & BIT(1)) ? BIT(offset) : 0); + regmap_update_bits(soc->ds_regmap, + soc->data->ds1[bank], BIT(offset), + (ds & BIT(0)) ? BIT(offset) : 0); + } + break; + default: + raw_spin_unlock_irqrestore(&soc->lock, flags); + return -EOPNOTSUPP; + } + } + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; +} + +static int amb_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + const struct ambpin_group *grp =3D &soc->groups[selector]; + int ret; + u32 i; + + for (i =3D 0; i < grp->num_pins; i++) { + ret =3D amb_pinconf_set(pctldev, grp->pins[i], configs, + num_configs); + if (ret) + return ret; + } + + return 0; +} + +/* get the pin config settings for a specified pin */ +static int amb_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + struct amb_gpio_bank *gpio_bank; + enum pin_config_param param =3D pinconf_to_config_param(*config); + u32 bank, offset, pull_en, pull_dir, ds0, ds1, ds2, ds; + int ret, strength; + + gpio_bank =3D amb_pin_to_bank(soc, pin); + if (!gpio_bank) + return -EINVAL; + + bank =3D gpio_bank->hw_id; + offset =3D pin - gpio_bank->pin_base; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + ret =3D regmap_read(soc->pull_regmap, soc->data->pull_en[bank], + &pull_en); + if (ret) + return ret; + + ret =3D regmap_read(soc->pull_regmap, soc->data->pull_dir[bank], + &pull_dir); + if (ret) + return ret; + + pull_en =3D (pull_en >> offset) & 1; + pull_dir =3D (pull_dir >> offset) & 1; + + if (param =3D=3D PIN_CONFIG_BIAS_DISABLE) { + if (pull_en) + return -EINVAL; + *config =3D pinconf_to_config_packed(param, 0); + return 0; + } + + if (!pull_en) + return -EINVAL; + if (param =3D=3D PIN_CONFIG_BIAS_PULL_UP && !pull_dir) + return -EINVAL; + if (param =3D=3D PIN_CONFIG_BIAS_PULL_DOWN && pull_dir) + return -EINVAL; + + *config =3D pinconf_to_config_packed(param, 1); + return 0; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret =3D regmap_read(soc->ds_regmap, soc->data->ds0[bank], &ds0); + if (ret) + return ret; + + ret =3D regmap_read(soc->ds_regmap, soc->data->ds1[bank], &ds1); + if (ret) + return ret; + + ds0 =3D (ds0 >> offset) & 1; + ds1 =3D (ds1 >> offset) & 1; + if (soc->data->have_ds2) { + ret =3D regmap_read(soc->ds_regmap, soc->data->ds2[bank], &ds2); + if (ret) + return ret; + + ds2 =3D (ds2 >> offset) & 1; + ds =3D (ds2 << 2) | (ds1 << 1) | ds0; + } else { + ds =3D (ds0 << 1) | ds1; + } + + strength =3D amb_reg_to_drive_strength(soc, ds); + if (strength < 0) + return strength; + + *config =3D pinconf_to_config_packed(param, strength); + return 0; + + default: + return -EOPNOTSUPP; + } +} + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static void amb_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct amb_pinctrl_soc_data *soc =3D pinctrl_dev_get_drvdata(pctldev); + struct amb_gpio_bank *gpio_bank; + u32 pull_en, pull_dir, ds0, ds1, ds2, ds; + u32 bank, offset; + int strength; + + gpio_bank =3D amb_pin_to_bank(soc, pin); + if (!gpio_bank) { + seq_puts(s, " (no pinconf)"); + return; + } + + bank =3D gpio_bank->hw_id; + offset =3D pin - gpio_bank->pin_base; + + regmap_read(soc->pull_regmap, soc->data->pull_en[bank], &pull_en); + pull_en =3D (pull_en >> offset) & 1; + regmap_read(soc->pull_regmap, soc->data->pull_dir[bank], &pull_dir); + pull_dir =3D (pull_dir >> offset) & 1; + seq_printf(s, " pull: %s,", + pull_en ? (pull_dir ? "up" : "down") : "disable"); + + regmap_read(soc->ds_regmap, soc->data->ds0[bank], &ds0); + ds0 =3D (ds0 >> offset) & 1; + regmap_read(soc->ds_regmap, soc->data->ds1[bank], &ds1); + ds1 =3D (ds1 >> offset) & 1; + if (soc->data->have_ds2) { + regmap_read(soc->ds_regmap, soc->data->ds2[bank], &ds2); + ds2 =3D (ds2 >> offset) & 1; + ds =3D (ds2 << 2) | (ds1 << 1) | ds0; + } else { + ds =3D (ds0 << 1) | ds1; + } + + strength =3D amb_reg_to_drive_strength(soc, ds); + if (strength < 0) + seq_puts(s, " drive-strength: invalid"); + else + seq_printf(s, " drive-strength: %dmA", strength); +} +#endif + +/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code = */ +static const struct pinconf_ops amb_pinconf_ops =3D { + .is_generic =3D true, + .pin_config_get =3D amb_pinconf_get, + .pin_config_set =3D amb_pinconf_set, + .pin_config_group_set =3D amb_pinconf_group_set, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .pin_config_dbg_show =3D amb_pinconf_dbg_show, +#endif +}; + +/* register the pinctrl interface with the pinctrl subsystem */ +static int amb_pinctrl_register(struct amb_pinctrl_soc_data *soc) +{ + struct pinctrl_pin_desc *pindesc; + struct pinctrl_desc *amb_pinctrl_desc; + unsigned int pin; + + /* dynamically populate the pin number and pin name for pindesc */ + pindesc =3D devm_kcalloc(soc->dev, soc->npins, sizeof(*pindesc), + GFP_KERNEL); + if (!pindesc) + return -ENOMEM; + + for (pin =3D 0; pin < soc->npins; pin++) { + pindesc[pin].number =3D pin; + pindesc[pin].name =3D devm_kasprintf(soc->dev, GFP_KERNEL, + "io%u", pin); + if (!pindesc[pin].name) + return -ENOMEM; + } + + amb_pinctrl_desc =3D devm_kzalloc(soc->dev, sizeof(*amb_pinctrl_desc), GF= P_KERNEL); + if (!amb_pinctrl_desc) + return -ENOMEM; + + amb_pinctrl_desc->name =3D dev_name(soc->dev); + amb_pinctrl_desc->pins =3D pindesc; + amb_pinctrl_desc->npins =3D soc->npins; + amb_pinctrl_desc->pctlops =3D &amb_pctrl_ops; + amb_pinctrl_desc->pmxops =3D &amb_pinmux_ops; + amb_pinctrl_desc->confops =3D &amb_pinconf_ops; + amb_pinctrl_desc->owner =3D THIS_MODULE; + + soc->pctl =3D devm_pinctrl_register(soc->dev, amb_pinctrl_desc, soc); + if (IS_ERR(soc->pctl)) { + dev_err(soc->dev, "could not register pinctrl driver\n"); + return PTR_ERR(soc->pctl); + } + + return 0; +} + +/* gpiolib gpio_set callback function */ +static int amb_gpio_set(struct gpio_chip *gc, unsigned int pin, int value) +{ + struct amb_gpio_bank *bank =3D gpiochip_get_data(gc); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + u32 data; + unsigned long flags; + + raw_spin_lock_irqsave(&soc->lock, flags); + writel_relaxed(BIT(pin), bank->base + GPIO_MASK_OFFSET); + data =3D value ? BIT(pin) : 0; + writel_relaxed(data, bank->base + GPIO_DATA_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; +} + +/* gpiolib gpio_get callback function */ +static int amb_gpio_get(struct gpio_chip *gc, unsigned int pin) +{ + struct amb_gpio_bank *bank =3D gpiochip_get_data(gc); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + u32 data; + unsigned long flags; + + raw_spin_lock_irqsave(&soc->lock, flags); + writel_relaxed(BIT(pin), bank->base + GPIO_MASK_OFFSET); + data =3D readl_relaxed(bank->base + GPIO_DATA_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return !!(data & BIT(pin)); +} + +static int amb_gpio_get_direction(struct gpio_chip *gc, unsigned int pin) +{ + struct amb_gpio_bank *bank =3D gpiochip_get_data(gc); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + u32 data; + unsigned long flags; + + raw_spin_lock_irqsave(&soc->lock, flags); + data =3D readl_relaxed(bank->base + GPIO_DIR_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return data & BIT(pin) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int amb_gpio_set_direction(struct gpio_chip *gc, unsigned int pin, = bool input) +{ + struct amb_gpio_bank *bank =3D gpiochip_get_data(gc); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + u32 data; + unsigned long flags; + + raw_spin_lock_irqsave(&soc->lock, flags); + data =3D readl_relaxed(bank->base + GPIO_DIR_OFFSET); + if (input) + data &=3D ~BIT(pin); + else + data |=3D BIT(pin); + writel_relaxed(data, bank->base + GPIO_DIR_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; +} + +/* gpiolib gpio_direction_input callback function */ +static int amb_gpio_direction_input(struct gpio_chip *gc, unsigned int pin) +{ + return amb_gpio_set_direction(gc, pin, true); +} + +/* gpiolib gpio_direction_output callback function */ +static int amb_gpio_direction_output(struct gpio_chip *gc, unsigned int pi= n, int value) +{ + struct amb_gpio_bank *bank =3D gpiochip_get_data(gc); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + unsigned long flags; + u32 data; + + raw_spin_lock_irqsave(&soc->lock, flags); + + writel_relaxed(BIT(pin), bank->base + GPIO_MASK_OFFSET); + writel_relaxed(value ? BIT(pin) : 0, + bank->base + GPIO_DATA_OFFSET); + + data =3D readl_relaxed(bank->base + GPIO_DIR_OFFSET); + writel_relaxed(data | BIT(pin), bank->base + GPIO_DIR_OFFSET); + + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; +} + +/* gpiolib gpio_to_irq callback function */ +static int amb_gpio_to_irq(struct gpio_chip *gc, unsigned int pin) +{ + struct amb_gpio_bank *bank =3D gpiochip_get_data(gc); + + return irq_create_mapping(bank->domain, pin); +} + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static void amb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) +{ + struct amb_gpio_bank *bank =3D gpiochip_get_data(gc); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + u32 afsel, data, dir, mask, iomux0, iomux1, iomux2, alt; + unsigned long flags; + u32 i; + + raw_spin_lock_irqsave(&soc->lock, flags); + afsel =3D readl_relaxed(bank->base + GPIO_AFSEL_OFFSET); + dir =3D readl_relaxed(bank->base + GPIO_DIR_OFFSET); + mask =3D readl_relaxed(bank->base + GPIO_MASK_OFFSET); + writel_relaxed(0xffffffff, bank->base + GPIO_MASK_OFFSET); + data =3D readl_relaxed(bank->base + GPIO_DATA_OFFSET); + writel_relaxed(mask, bank->base + GPIO_MASK_OFFSET); + + iomux0 =3D readl_relaxed(soc->iomux_base + IOMUX_OFFSET(bank->hw_id, 0)); + iomux1 =3D readl_relaxed(soc->iomux_base + IOMUX_OFFSET(bank->hw_id, 1)); + iomux2 =3D readl_relaxed(soc->iomux_base + IOMUX_OFFSET(bank->hw_id, 2)); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + seq_printf(s, "\nGPIO[%d]:\n", bank->hw_id); + seq_printf(s, "GPIO_AFSEL:\t0x%08X\n", afsel); + seq_printf(s, "GPIO_DIR:\t0x%08X\n", dir); + seq_printf(s, "GPIO_MASK:\t0x%08X\n", mask); + seq_printf(s, "GPIO_DATA:\t0x%08X\n", data); + seq_printf(s, "IOMUX_REG%d_0:\t0x%08X\n", bank->hw_id, iomux0); + seq_printf(s, "IOMUX_REG%d_1:\t0x%08X\n", bank->hw_id, iomux1); + seq_printf(s, "IOMUX_REG%d_2:\t0x%08X\n", bank->hw_id, iomux2); + + for (i =3D 0; i < gc->ngpio; i++) { + + seq_printf(s, " gpio-%-3d", gc->base + i); + + alt =3D ((iomux2 >> i) & 1) << 2; + alt |=3D ((iomux1 >> i) & 1) << 1; + alt |=3D ((iomux0 >> i) & 1) << 0; + if (alt) { + seq_printf(s, " [HW ] (alt%d)\n", alt); + } else { + char *label __free(kfree) =3D gpiochip_dup_line_label(gc, i); + if (IS_ERR(label)) { + pr_debug("Failed to duplicate label\n"); + continue; + } + + seq_printf(s, " [GPIO] (%-20.20s) %s %s\n", + label ? label : "", + (dir & BIT(i)) ? "out" : "in ", + (data & BIT(i)) ? "hi" : "lo"); + } + } +} +#endif + +static void amb_gpio_irq_enable(struct irq_data *data) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(data); + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + void __iomem *gpio_base =3D bank->base; + void __iomem *iomux_base =3D soc->iomux_base; + u32 i, val, offset; + unsigned long flags; + + offset =3D irqd_to_hwirq(data); + + gpiochip_enable_irq(gc, offset); + + raw_spin_lock_irqsave(&soc->lock, flags); + + val =3D readl_relaxed(gpio_base + GPIO_DIR_OFFSET); + val &=3D ~(0x1 << offset); + writel_relaxed(val, gpio_base + GPIO_DIR_OFFSET); + + for (i =3D 0; i < 3; i++) { + val =3D readl_relaxed(iomux_base + IOMUX_OFFSET(bank->hw_id, i)); + val &=3D ~(0x1 << offset); + writel_relaxed(val, iomux_base + IOMUX_OFFSET(bank->hw_id, i)); + } + writel_relaxed(0x1, iomux_base + IOMUX_CTRL_SET_OFFSET); + writel_relaxed(0x0, iomux_base + IOMUX_CTRL_SET_OFFSET); + + writel_relaxed(0x1 << offset, gpio_base + GPIO_IC_OFFSET); + + val =3D readl_relaxed(gpio_base + GPIO_IE_OFFSET); + val |=3D 0x1 << offset; + writel_relaxed(val, gpio_base + GPIO_IE_OFFSET); + + raw_spin_unlock_irqrestore(&soc->lock, flags); +} + +static void amb_gpio_irq_disable(struct irq_data *data) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(data); + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + void __iomem *gpio_base =3D bank->base; + u32 offset, ie; + unsigned long flags; + + offset =3D irqd_to_hwirq(data); + + raw_spin_lock_irqsave(&soc->lock, flags); + ie =3D readl_relaxed(gpio_base + GPIO_IE_OFFSET); + writel_relaxed(ie & ~(0x1 << offset), gpio_base + GPIO_IE_OFFSET); + writel_relaxed(0x1 << offset, gpio_base + GPIO_IC_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + gpiochip_disable_irq(gc, offset); +} + +static void amb_gpio_irq_ack(struct irq_data *data) +{ + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + void __iomem *gpio_base =3D bank->base; + u32 offset =3D irqd_to_hwirq(data); + unsigned long flags; + + raw_spin_lock_irqsave(&soc->lock, flags); + writel_relaxed(0x1 << offset, gpio_base + GPIO_IC_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); +} + +static void amb_gpio_irq_mask(struct irq_data *data) +{ + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + void __iomem *gpio_base =3D bank->base; + u32 offset, ie; + unsigned long flags; + + offset =3D irqd_to_hwirq(data); + + raw_spin_lock_irqsave(&soc->lock, flags); + ie =3D readl_relaxed(gpio_base + GPIO_IE_OFFSET); + writel_relaxed(ie & ~(0x1 << offset), gpio_base + GPIO_IE_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); +} + +static void amb_gpio_irq_mask_ack(struct irq_data *data) +{ + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + void __iomem *gpio_base =3D bank->base; + u32 offset, ie; + unsigned long flags; + + offset =3D irqd_to_hwirq(data); + + raw_spin_lock_irqsave(&soc->lock, flags); + ie =3D readl_relaxed(gpio_base + GPIO_IE_OFFSET); + writel_relaxed(ie & ~(0x1 << offset), gpio_base + GPIO_IE_OFFSET); + writel_relaxed(0x1 << offset, gpio_base + GPIO_IC_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); +} + +static void amb_gpio_irq_unmask(struct irq_data *data) +{ + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + void __iomem *gpio_base =3D bank->base; + u32 offset, ie; + unsigned long flags; + + offset =3D irqd_to_hwirq(data); + + raw_spin_lock_irqsave(&soc->lock, flags); + ie =3D readl_relaxed(gpio_base + GPIO_IE_OFFSET); + writel_relaxed(ie | (0x1 << offset), gpio_base + GPIO_IE_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); +} + +static int amb_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + void __iomem *gpio_base =3D bank->base; + u32 offset =3D irqd_to_hwirq(data); + u32 mask, bit, sense, bothedges, event; + unsigned long flags; + + mask =3D ~BIT(offset); + bit =3D BIT(offset); + + raw_spin_lock_irqsave(&soc->lock, flags); + sense =3D readl_relaxed(gpio_base + GPIO_IS_OFFSET); + bothedges =3D readl_relaxed(gpio_base + GPIO_IBE_OFFSET); + event =3D readl_relaxed(gpio_base + GPIO_IEV_OFFSET); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + sense &=3D mask; + bothedges &=3D mask; + event |=3D bit; + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_FALLING: + sense &=3D mask; + bothedges &=3D mask; + event &=3D mask; + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_BOTH: + sense &=3D mask; + bothedges |=3D bit; + event &=3D mask; + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + sense |=3D bit; + bothedges &=3D mask; + event |=3D bit; + irq_set_handler_locked(data, handle_level_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + sense |=3D bit; + bothedges &=3D mask; + event &=3D mask; + irq_set_handler_locked(data, handle_level_irq); + break; + default: + raw_spin_unlock_irqrestore(&soc->lock, flags); + return -EINVAL; + } + + writel_relaxed(sense, gpio_base + GPIO_IS_OFFSET); + writel_relaxed(bothedges, gpio_base + GPIO_IBE_OFFSET); + writel_relaxed(event, gpio_base + GPIO_IEV_OFFSET); + /* clear obsolete irq */ + writel_relaxed(BIT(offset), gpio_base + GPIO_IC_OFFSET); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; +} + +static int amb_gpio_irq_set_wake(struct irq_data *data, unsigned int on) +{ + if (IS_ENABLED(CONFIG_PM)) { + struct amb_gpio_bank *bank =3D amb_irq_data_to_bank(data); + struct amb_pinctrl_soc_data *soc =3D bank->soc; + u32 offset =3D irqd_to_hwirq(data); + unsigned long flags; + int ret; + + ret =3D irq_set_irq_wake(bank->irq, on); + if (ret) + return ret; + + raw_spin_lock_irqsave(&soc->lock, flags); + if (on) + bank->irq_wake_mask |=3D BIT(offset); + else + bank->irq_wake_mask &=3D ~BIT(offset); + raw_spin_unlock_irqrestore(&soc->lock, flags); + + return 0; + } + return 0; +} + +static struct irq_chip amb_gpio_irqchip =3D { + .name =3D "GPIO", + .irq_enable =3D amb_gpio_irq_enable, + .irq_disable =3D amb_gpio_irq_disable, + .irq_ack =3D amb_gpio_irq_ack, + .irq_mask =3D amb_gpio_irq_mask, + .irq_mask_ack =3D amb_gpio_irq_mask_ack, + .irq_unmask =3D amb_gpio_irq_unmask, + .irq_set_type =3D amb_gpio_irq_set_type, + .irq_set_wake =3D amb_gpio_irq_set_wake, + .irq_request_resources =3D gpiochip_irq_reqres, + .irq_release_resources =3D gpiochip_irq_relres, + .flags =3D IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND, +}; + +static int amb_gpio_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct amb_gpio_bank *bank =3D d->host_data; + + irq_set_chip_data(irq, &bank->gc); + irq_set_chip_and_handler(irq, &amb_gpio_irqchip, handle_level_irq); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops amb_gpio_irq_domain_ops =3D { + .map =3D amb_gpio_irqdomain_map, + .xlate =3D irq_domain_xlate_twocell, +}; + +static void amb_gpio_handle_irq(struct irq_desc *desc) +{ + struct amb_gpio_bank *bank; + struct irq_chip *irqchip; + u32 bit, gpio_mis; + + irqchip =3D irq_desc_get_chip(desc); + chained_irq_enter(irqchip, desc); + + bank =3D irq_desc_get_handler_data(desc); + gpio_mis =3D readl_relaxed(bank->base + GPIO_MIS_OFFSET); + while (gpio_mis) { + bit =3D __ffs(gpio_mis); + generic_handle_domain_irq(bank->domain, bit); + gpio_mis &=3D ~BIT(bit); + } + + chained_irq_exit(irqchip, desc); +} + +static int amb_gpio_parse_dt(struct amb_pinctrl_soc_data *soc) +{ + struct of_phandle_args args; + struct resource res; + unsigned int hw_id, i; + int rval; + + for_each_available_child_of_node_scoped(soc->dev->of_node, np) { + struct amb_gpio_bank *bank; + + if (!of_property_present(np, "gpio-controller")) + continue; + + if (soc->bank_num >=3D AMBA_MAX_BANKS) + return dev_err_probe(soc->dev, -EINVAL, + "too many GPIO banks\n"); + + rval =3D of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, + &args); + if (rval) + return dev_err_probe(soc->dev, rval, + "%pOF: invalid gpio-ranges\n", np); + + if (args.np !=3D soc->dev->of_node || args.args[0] || + args.args[1] % 32 || !args.args[2] || args.args[2] > 32) { + of_node_put(args.np); + return dev_err_probe(soc->dev, -EINVAL, + "%pOF: invalid GPIO pin range\n", np); + } + + hw_id =3D args.args[1] / 32; + of_node_put(args.np); + if (hw_id >=3D AMBA_MAX_BANKS) + return dev_err_probe(soc->dev, -EINVAL, + "%pOF: invalid GPIO bank\n", np); + + for (i =3D 0; i < soc->bank_num; i++) { + if (soc->banks[i].hw_id =3D=3D hw_id) + return dev_err_probe(soc->dev, -EINVAL, + "%pOF: duplicate GPIO bank\n", + np); + } + + bank =3D &soc->banks[soc->bank_num]; + bank->soc =3D soc; + bank->pin_base =3D args.args[1]; + bank->hw_id =3D hw_id; + bank->gc.ngpio =3D args.args[2]; + bank->gc.fwnode =3D of_fwnode_handle(np); + + rval =3D of_address_to_resource(np, 0, &res); + if (rval) + return dev_err_probe(soc->dev, rval, + "%pOF: couldn't get registers\n", np); + + bank->base =3D devm_ioremap_resource(soc->dev, &res); + if (IS_ERR(bank->base)) + return dev_err_probe(soc->dev, PTR_ERR(bank->base), + "%pOF: couldn't map registers\n", np); + + bank->irq =3D of_irq_get(np, 0); + if (bank->irq < 0) + return dev_err_probe(soc->dev, bank->irq, + "%pOF: couldn't get interrupt\n", np); + + soc->bank_num++; + } + + if (!soc->bank_num) + return dev_err_probe(soc->dev, -ENODEV, + "no gpio-controller child nodes\n"); + + return 0; +} + +static void amb_gpio_irq_cleanup(void *data) +{ + struct amb_gpio_bank *bank =3D data; + + irq_set_chained_handler_and_data(bank->irq, NULL, NULL); + irq_domain_remove(bank->domain); + bank->domain =3D NULL; +} + +static int amb_gpio_register(struct amb_pinctrl_soc_data *soc) +{ + struct amb_gpio_bank *bank; + unsigned int i; + int rval; + + for (i =3D 0; i < soc->bank_num; i++) { + bank =3D &soc->banks[i]; + bank->gc.label =3D devm_kasprintf(soc->dev, GFP_KERNEL, + "%s-gpio%u", dev_name(soc->dev), + bank->hw_id); + if (!bank->gc.label) + return -ENOMEM; + + bank->gc.base =3D -1; + bank->gc.request =3D gpiochip_generic_request; + bank->gc.free =3D gpiochip_generic_free; + bank->gc.direction_input =3D amb_gpio_direction_input; + bank->gc.direction_output =3D amb_gpio_direction_output; + bank->gc.get_direction =3D amb_gpio_get_direction; + bank->gc.get =3D amb_gpio_get; + bank->gc.set =3D amb_gpio_set; + bank->gc.to_irq =3D amb_gpio_to_irq; +#if IS_ENABLED(CONFIG_DEBUG_FS) + bank->gc.dbg_show =3D amb_gpio_dbg_show; +#endif + bank->gc.parent =3D soc->dev; + + bank->domain =3D irq_domain_add_linear(to_of_node(bank->gc.fwnode), + bank->gc.ngpio, + &amb_gpio_irq_domain_ops, + bank); + if (!bank->domain) + return dev_err_probe(soc->dev, -ENODEV, + "GPIO%u: failed to create irqdomain\n", + i); + + rval =3D devm_gpiochip_add_data(soc->dev, &bank->gc, bank); + if (rval) { + irq_domain_remove(bank->domain); + bank->domain =3D NULL; + return dev_err_probe(soc->dev, rval, + "GPIO%u: gpiochip registration failed\n", + i); + } + + writel_relaxed(0xffffffff, bank->base + GPIO_ENABLE_OFFSET); + writel_relaxed(0x00000000, bank->base + GPIO_AFSEL_OFFSET); + writel_relaxed(0x00000000, bank->base + GPIO_MASK_OFFSET); + + irq_set_irq_type(bank->irq, IRQ_TYPE_LEVEL_HIGH); + irq_set_chained_handler_and_data(bank->irq, + amb_gpio_handle_irq, bank); + + rval =3D devm_add_action_or_reset(soc->dev, + amb_gpio_irq_cleanup, bank); + if (rval) + return rval; + } + + return 0; +} + +static int amb_pinctrl_probe(struct platform_device *pdev) +{ + struct amb_pinctrl_soc_data *soc; + struct device_node *np; + unsigned int *group_pins; + unsigned int gpio_pins, group, group_pin; + size_t nr_group_pins =3D 0; + int i, rval; + + soc =3D devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL); + if (!soc) + return -ENOMEM; + + soc->dev =3D &pdev->dev; + soc->data =3D of_device_get_match_data(&pdev->dev); + if (!soc->data) + return dev_err_probe(&pdev->dev, -EINVAL, "missing soc data"); + + np =3D pdev->dev.of_node; + soc->iomux_base =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(soc->iomux_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(soc->iomux_base), + "couldn't get iomux reg"); + + rval =3D amb_gpio_parse_dt(soc); + if (rval) + return rval; + + soc->ds_regmap =3D syscon_regmap_lookup_by_phandle(np, "ambarella,drive-s= trength-syscon"); + if (IS_ERR(soc->ds_regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(soc->ds_regmap), + "couldn't get drive-strength regmap"); + + soc->pull_regmap =3D syscon_regmap_lookup_by_phandle(np, "ambarella,pull-= syscon"); + if (IS_ERR(soc->pull_regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(soc->pull_regmap), + "couldn't get pull regmap"); + + gpio_pins =3D amb_gpio_pins_end(soc); + soc->npins =3D gpio_pins; + if (soc->data->clk_au_dedicated_pin >=3D gpio_pins) + soc->npins =3D soc->data->clk_au_dedicated_pin + 1; + + if (soc->npins > AMBA_MAX_PINS) + return dev_err_probe(&pdev->dev, -EINVAL, "too many pins\n"); + + soc->nr_groups =3D soc->data->nr_groups; + soc->functions =3D soc->data->functions; + soc->nr_functions =3D soc->data->nr_functions; + if (!soc->data->groups || !soc->nr_groups || + !soc->functions || !soc->nr_functions) + return dev_err_probe(&pdev->dev, -EINVAL, + "missing pin groups or functions\n"); + + soc->groups =3D devm_kcalloc(&pdev->dev, soc->nr_groups, + sizeof(*soc->groups), GFP_KERNEL); + if (!soc->groups) + return -ENOMEM; + + for (group =3D 0; group < soc->nr_groups; group++) { + const struct ambpin_group_desc *desc =3D + &soc->data->groups[group]; + + if (!desc->name || !desc->pinmux || !desc->num_pins) + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid pin group %u\n", group); + + nr_group_pins +=3D desc->num_pins; + } + + group_pins =3D devm_kcalloc(&pdev->dev, nr_group_pins, + sizeof(*group_pins), GFP_KERNEL); + if (!group_pins) + return -ENOMEM; + + for (group =3D 0; group < soc->nr_groups; group++) { + const struct ambpin_group_desc *desc =3D + &soc->data->groups[group]; + struct ambpin_group *grp =3D &soc->groups[group]; + + grp->name =3D desc->name; + grp->pinmux =3D desc->pinmux; + grp->pins =3D group_pins; + grp->num_pins =3D desc->num_pins; + for (group_pin =3D 0; + group_pin < grp->num_pins; + group_pin++) { + u32 pinmux =3D grp->pinmux[group_pin]; + unsigned int pin =3D AMBA_PINMUX_TO_PIN(pinmux); + unsigned int alt =3D AMBA_PINMUX_TO_ALT(pinmux); + + if (pin >=3D soc->npins || alt > 7) + return dev_err_probe(&pdev->dev, -EINVAL, + "group %s has invalid pinmux %#x\n", + grp->name, pinmux); + + grp->pins[group_pin] =3D pin; + } + group_pins +=3D grp->num_pins; + } + + raw_spin_lock_init(&soc->lock); + + /* Mark all pins unavailable, then clear pins that exist. */ + bitmap_fill(soc->used, AMBA_MAX_PINS); + for (i =3D 0; i < soc->bank_num; i++) { + unsigned int pin; + + for (pin =3D soc->banks[i].pin_base; + pin < soc->banks[i].pin_base + soc->banks[i].gc.ngpio; + pin++) + clear_bit(pin, soc->used); + } + + if (soc->data->clk_au_dedicated_pin >=3D gpio_pins && + soc->data->clk_au_dedicated_pin < AMBA_MAX_PINS) + clear_bit(soc->data->clk_au_dedicated_pin, soc->used); + + rval =3D amb_pinctrl_register(soc); + if (rval) + return dev_err_probe(&pdev->dev, rval, "pinctrl register failed!"); + + rval =3D amb_gpio_register(soc); + if (rval) + return dev_err_probe(&pdev->dev, rval, "gpio register failed!"); + + platform_set_drvdata(pdev, soc); + dev_info(&pdev->dev, "Ambarella pinctrl driver registered"); + + return 0; +} + +static int amb_pinctrl_suspend_noirq(struct device *dev) +{ + struct amb_pinctrl_soc_data *soc =3D dev_get_drvdata(dev); + u32 bank, hw, i; + + for (i =3D 0; i < soc->bank_num; i++) { + hw =3D soc->banks[i].hw_id; + + regmap_read(soc->pull_regmap, soc->data->pull_en[hw], &soc->pm[i].pull[0= ]); + regmap_read(soc->pull_regmap, soc->data->pull_dir[hw], &soc->pm[i].pull[= 1]); + + regmap_read(soc->ds_regmap, soc->data->ds0[hw], &soc->pm[i].ds[0]); + regmap_read(soc->ds_regmap, soc->data->ds1[hw], &soc->pm[i].ds[1]); + if (soc->data->have_ds2) + regmap_read(soc->ds_regmap, soc->data->ds2[hw], &soc->pm[i].ds[2]); + + soc->pm[i].iomux[0] =3D readl_relaxed(soc->iomux_base + IOMUX_OFFSET(hw,= 0)); + soc->pm[i].iomux[1] =3D readl_relaxed(soc->iomux_base + IOMUX_OFFSET(hw,= 1)); + soc->pm[i].iomux[2] =3D readl_relaxed(soc->iomux_base + IOMUX_OFFSET(hw,= 2)); + + soc->pm[i].afsel =3D readl_relaxed(soc->banks[i].base + GPIO_AFSEL_OFFSE= T); + soc->pm[i].dir =3D readl_relaxed(soc->banks[i].base + GPIO_DIR_OFFSET); + soc->pm[i].is =3D readl_relaxed(soc->banks[i].base + GPIO_IS_OFFSET); + soc->pm[i].ibe =3D readl_relaxed(soc->banks[i].base + GPIO_IBE_OFFSET); + soc->pm[i].iev =3D readl_relaxed(soc->banks[i].base + GPIO_IEV_OFFSET); + soc->pm[i].ie =3D readl_relaxed(soc->banks[i].base + GPIO_IE_OFFSET); + soc->pm[i].mask =3D readl_relaxed(soc->banks[i].base + GPIO_MASK_OFFSET); + writel_relaxed(0xffffffff, soc->banks[i].base + GPIO_MASK_OFFSET); + soc->pm[i].data =3D readl_relaxed(soc->banks[i].base + GPIO_DATA_OFFSET); + + if (soc->banks[i].irq_wake_mask) + writel_relaxed(soc->banks[i].irq_wake_mask, + soc->banks[i].base + GPIO_IE_OFFSET); + } + + if (soc->data->clk_au_dedicated_pin >=3D amb_gpio_pins_end(soc)) { + bank =3D PINID_TO_BANK(soc->data->clk_au_dedicated_pin); + soc->pm[bank].iomux[0] =3D + readl_relaxed(soc->iomux_base + IOMUX_OFFSET(bank, 0)); + soc->pm[bank].iomux[1] =3D + readl_relaxed(soc->iomux_base + IOMUX_OFFSET(bank, 1)); + soc->pm[bank].iomux[2] =3D + readl_relaxed(soc->iomux_base + IOMUX_OFFSET(bank, 2)); + } + + return 0; +} + +static int amb_pinctrl_resume_noirq(struct device *dev) +{ + struct amb_pinctrl_soc_data *soc =3D dev_get_drvdata(dev); + u32 bank, hw, i; + + for (i =3D 0; i < soc->bank_num; i++) { + hw =3D soc->banks[i].hw_id; + + regmap_write(soc->pull_regmap, soc->data->pull_en[hw], soc->pm[i].pull[0= ]); + regmap_write(soc->pull_regmap, soc->data->pull_dir[hw], soc->pm[i].pull[= 1]); + + regmap_write(soc->ds_regmap, soc->data->ds0[hw], soc->pm[i].ds[0]); + regmap_write(soc->ds_regmap, soc->data->ds1[hw], soc->pm[i].ds[1]); + if (soc->data->have_ds2) + regmap_write(soc->ds_regmap, soc->data->ds2[hw], soc->pm[i].ds[2]); + + writel_relaxed(soc->pm[i].iomux[0], soc->iomux_base + IOMUX_OFFSET(hw, 0= )); + writel_relaxed(soc->pm[i].iomux[1], soc->iomux_base + IOMUX_OFFSET(hw, 1= )); + writel_relaxed(soc->pm[i].iomux[2], soc->iomux_base + IOMUX_OFFSET(hw, 2= )); + + writel_relaxed(soc->pm[i].afsel, soc->banks[i].base + GPIO_AFSEL_OFFSET); + writel_relaxed(soc->pm[i].dir, soc->banks[i].base + GPIO_DIR_OFFSET); + /* Expose DATA writes while restoring the saved GPIO state. */ + writel_relaxed(0xffffffff, + soc->banks[i].base + GPIO_MASK_OFFSET); + writel_relaxed(soc->pm[i].data, + soc->banks[i].base + GPIO_DATA_OFFSET); + /* Ensure DATA restore reaches hardware before restoring mask. */ + wmb(); + writel_relaxed(soc->pm[i].mask, soc->banks[i].base + GPIO_MASK_OFFSET); + writel_relaxed(soc->pm[i].is, soc->banks[i].base + GPIO_IS_OFFSET); + writel_relaxed(soc->pm[i].ibe, soc->banks[i].base + GPIO_IBE_OFFSET); + writel_relaxed(soc->pm[i].iev, soc->banks[i].base + GPIO_IEV_OFFSET); + writel_relaxed(soc->pm[i].ie, soc->banks[i].base + GPIO_IE_OFFSET); + writel_relaxed(0xffffffff, soc->banks[i].base + GPIO_ENABLE_OFFSET); + } + + if (soc->data->clk_au_dedicated_pin >=3D amb_gpio_pins_end(soc)) { + bank =3D PINID_TO_BANK(soc->data->clk_au_dedicated_pin); + writel_relaxed(soc->pm[bank].iomux[0], + soc->iomux_base + IOMUX_OFFSET(bank, 0)); + writel_relaxed(soc->pm[bank].iomux[1], + soc->iomux_base + IOMUX_OFFSET(bank, 1)); + writel_relaxed(soc->pm[bank].iomux[2], + soc->iomux_base + IOMUX_OFFSET(bank, 2)); + /* Ensure dedicated-pin iomux writes reach hardware before commit. */ + wmb(); + } + + writel_relaxed(0x1, soc->iomux_base + IOMUX_CTRL_SET_OFFSET); + writel_relaxed(0x0, soc->iomux_base + IOMUX_CTRL_SET_OFFSET); + + return 0; +} + +static DEFINE_NOIRQ_DEV_PM_OPS(amb_pinctrl_pm_ops, + amb_pinctrl_suspend_noirq, + amb_pinctrl_resume_noirq); + +static const struct of_device_id amb_pinctrl_dt_match[] =3D { + { + .compatible =3D "ambarella,cv75-pinctrl", + .data =3D &ambarella_cv75_pinctrl_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, amb_pinctrl_dt_match); + +static struct platform_driver amb_pinctrl_driver =3D { + .probe =3D amb_pinctrl_probe, + .driver =3D { + .name =3D "ambarella-pinctrl", + .of_match_table =3D of_match_ptr(amb_pinctrl_dt_match), + .pm =3D pm_sleep_ptr(&amb_pinctrl_pm_ops), + }, +}; + +static int __init amb_pinctrl_drv_register(void) +{ + return platform_driver_register(&amb_pinctrl_driver); +} +arch_initcall(amb_pinctrl_drv_register); + +MODULE_AUTHOR("Cao Rongrong "); +MODULE_DESCRIPTION("Ambarella SoC pinctrl driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/pinctrl-ambarella.h b/drivers/pinctrl/pinctrl-= ambarella.h new file mode 100644 index 000000000000..5fe776393ee3 --- /dev/null +++ b/drivers/pinctrl/pinctrl-ambarella.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Ambarella pinctrl common data definitions + * + * Copyright (C) 2012-2026, Ambarella, Inc. + */ + +#ifndef _PINCTRL_AMBARELLA_H +#define _PINCTRL_AMBARELLA_H + +#include + +#define AMBA_MAX_BANKS 8 + +#define AMBA_PINMUX(pin, alt) (((alt) << 12) | (pin)) +#define AMBA_PINMUX_TO_PIN(mux) ((mux) & 0xfff) +#define AMBA_PINMUX_TO_ALT(mux) (((mux) >> 12) & 0xf) + +struct ambpin_group_desc { + const char *name; + const u32 *pinmux; + unsigned int num_pins; +}; + +struct ambpin_function { + const char *name; + const char * const *groups; + unsigned int num_groups; +}; + +struct amb_pinctrl_data { + unsigned int ds0[AMBA_MAX_BANKS]; + unsigned int ds1[AMBA_MAX_BANKS]; + unsigned int ds2[AMBA_MAX_BANKS]; + unsigned int pull_en[AMBA_MAX_BANKS]; + unsigned int pull_dir[AMBA_MAX_BANKS]; + bool have_ds2; + u32 hsm_domain_id; + u32 clk_au_dedicated_pin; + const struct ambpin_group_desc *groups; + unsigned int nr_groups; + const struct ambpin_function *functions; + unsigned int nr_functions; +}; + +extern const struct amb_pinctrl_data ambarella_cv75_pinctrl_data; + +#endif /* _PINCTRL_AMBARELLA_H */ --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8AC64433E8B; Thu, 6 Aug 2026 09:34:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; cv=none; b=i8oOOAgecrHPlBy/41mTOasZL4aLXncAl2FqwYIS4k4kiaIf/JztBTdTtTexOvLs9qdtULQ/fh+MakvnHutTSqxzRjV7GYWb5CExW7g21XWdaibIga8iGnbb4ecs+rcjZZ6aUev3Um6M4hnZkiLPGiXcpXfoIxpawkITo+jCZZI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; c=relaxed/simple; bh=/sEjHv6JgpDifHs0Xs3XOEeVQMZ0riTW0L3YTYDie2s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LsXSAWkXzvClZPdm1aEIl6nMkGqcHuBcLX2u7Pi4BpvNfEP/jO3QjQNlesjHAe7amjS4i9SWddVRaTCDGTSecVdjv6/msHdur0opuzc/JFVcqPuXimHDYCF6i/NwehoKVekvTUoZW3QtW5GSnNEb3xat/QNlmQI9ISHZBmtJQgs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DNNoK8h8; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DNNoK8h8" Received: by smtp.kernel.org (Postfix) with ESMTPS id D96BFC2BCB9; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008866; bh=/sEjHv6JgpDifHs0Xs3XOEeVQMZ0riTW0L3YTYDie2s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=DNNoK8h8Xc63NxSMFUlBWE5QLf48hHqYmAhEuMFdRBWhX6zk8SeP2x6iDDMjfc1x5 RMyNutxoxtUPIVBYk2X8MjHwRPqg+cdZ8c+KqWbcXWmhLc0aSc7EeAPuYz/XbaysCz D5ClYkhnH/V7xVr6vMtr1KJbXf+wf6HWMXsv9V+g0eyiMsa/zJvVcyaZrWkrSJ9bhs EVbPU6OYE1lWkyLAui6jqd4FwnvNvSeh30eJy943jPLoIEjQwMWi5C9jJHGP+IbydQ NOmf2fszgBpCWaqc4Fd8oXX1LnPqcGZZfKZBCGwAujQ65Q1yDSGJBxy1aHRbUmD+wH HOJxzwUZUemkg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id C7144C55ABF; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:16 +0800 Subject: [PATCH v2 08/10] serial: ambarella: add Ambarella UART driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-8-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=30393; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=x5ZwYPKckVRDIHXasH/O86AV2C4++inhHOv9lB+Nvew=; b=anh5iR4IBoUA5vXwskisEQm0f3+J1xjl4pNWgC+bWS/M4HYb8EieSP3wGtJ8Cn+/90LsSIdZI ZR9HXtYV+g/BfcbO4Mytk8R8+Y7BlQyKrIUBiMp0KBAtJ/GD8TKda2d X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Add an Ambarella UART driver with console support for early boot bring-up on CV75. Keep udelay() in wait_for_tx(); it runs under console/poll paths that may hold the port lock with IRQs disabled. Signed-off-by: Long Zhao --- drivers/tty/serial/Kconfig | 21 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/ambarella_uart.c | 1001 +++++++++++++++++++++++++++++++= ++++ 3 files changed, 1023 insertions(+) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index cf7dba473b20..e3f5ec794c72 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -18,6 +18,27 @@ source "drivers/tty/serial/8250/Kconfig" =20 comment "Non-8250 serial port support" =20 +config SERIAL_AMBARELLA + bool "Ambarella UART support" + depends on ARCH_AMBARELLA || COMPILE_TEST + select SERIAL_CORE + help + Say Y here to enable the on-chip UART controller found on Ambarella + SoCs. The driver supports interrupt-driven transmit and receive + operation through the serial core. It can also provide a system + console when SERIAL_AMBARELLA_CONSOLE is enabled. + +config SERIAL_AMBARELLA_CONSOLE + bool "Console on Ambarella UART" + depends on SERIAL_AMBARELLA + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Say Y here to use an Ambarella UART as the system console. This + enables both the regular serial console and earlycon support for + messages emitted before the full UART driver is initialized. + Select this option for early boot diagnostics on Ambarella systems. + config SERIAL_AMBA_PL010 tristate "ARM AMBA PL010 serial port support" depends on ARM_AMBA || COMPILE_TEST diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index bba7b21a4a1d..5c951719528b 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -26,6 +26,7 @@ obj-y +=3D 8250/ =20 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) +=3D altera_jtaguart.o obj-$(CONFIG_SERIAL_ALTERA_UART) +=3D altera_uart.o +obj-$(CONFIG_SERIAL_AMBARELLA) +=3D ambarella_uart.o obj-$(CONFIG_SERIAL_AMBA_PL010) +=3D amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) +=3D amba-pl011.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) +=3D apbuart.o diff --git a/drivers/tty/serial/ambarella_uart.c b/drivers/tty/serial/ambar= ella_uart.c new file mode 100644 index 000000000000..7356b242f0ef --- /dev/null +++ b/drivers/tty/serial/ambarella_uart.c @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UART_RB_OFFSET 0x00 +#define UART_TH_OFFSET 0x00 +#define UART_DLL_OFFSET 0x00 +#define UART_IE_OFFSET 0x04 +#define UART_DLH_OFFSET 0x04 +#define UART_II_OFFSET 0x08 +#define UART_FC_OFFSET 0x08 +#define UART_LC_OFFSET 0x0c +#define UART_MC_OFFSET 0x10 +#define UART_LS_OFFSET 0x14 +#define UART_MS_OFFSET 0x18 +#define UART_US_OFFSET 0x7c +#define UART_SRR_OFFSET 0x88 + +#define UART_IE_ERETOI 0x40 +#define UART_IE_ETOI 0x20 +#define UART_IE_EDSSI 0x08 +#define UART_IE_ELSI 0x04 +#define UART_IE_ETBEI 0x02 +#define UART_IE_ERBFI 0x01 + +#define UART_II_MODEM_STATUS_CHANGED 0x00 +#define UART_II_NO_INT_PENDING 0x01 +#define UART_II_THR_EMPTY 0x02 +#define UART_II_RCV_DATA_AVAIL 0x04 +#define UART_II_RCV_STATUS 0x06 +#define UART_II_CHAR_TIMEOUT 0x0c +#define UART_II_CHAR_TIMEOUT_FIFO_EMPTY 0x0d + +#define UART_FC_RX_2_TO_FULL 0xc0 +#define UART_FC_TX_EMPTY 0x00 +#define UART_FC_XMITR 0x04 +#define UART_FC_RCVRR 0x02 +#define UART_FC_FIFOE 0x01 + +#define UART_LC_DLAB 0x80 +#define UART_LC_BRK 0x40 +#define UART_LC_EVEN_PARITY 0x10 +#define UART_LC_ODD_PARITY 0x00 +#define UART_LC_PEN 0x08 +#define UART_LC_STOP_2BIT 0x04 +#define UART_LC_STOP_1BIT 0x00 +#define UART_LC_CLS_8_BITS 0x03 +#define UART_LC_CLS_7_BITS 0x02 +#define UART_LC_CLS_6_BITS 0x01 +#define UART_LC_CLS_5_BITS 0x00 + +#define UART_MC_AFCE 0x20 +#define UART_MC_LB 0x10 +#define UART_MC_OUT2 0x08 +#define UART_MC_OUT1 0x04 +#define UART_MC_RTS 0x02 +#define UART_MC_DTR 0x01 + +#define UART_LS_TEMT 0x40 +#define UART_LS_THRE 0x20 +#define UART_LS_BI 0x10 +#define UART_LS_FE 0x08 +#define UART_LS_PE 0x04 +#define UART_LS_OE 0x02 +#define UART_LS_DR 0x01 + +#define UART_MS_DCD 0x80 +#define UART_MS_RI 0x40 +#define UART_MS_DSR 0x20 +#define UART_MS_CTS 0x10 +#define UART_MS_DDCD 0x08 +#define UART_MS_DCTS 0x01 + +#define UART_US_TFNF 0x02 + +#define UART_FIFO_SIZE 64 + +#define DEFAULT_AMBARELLA_UART_MCR 0 +#define DEFAULT_AMBARELLA_UART_IER (UART_IE_ELSI | UART_IE_ERBFI | \ + UART_IE_ETOI) + +#define AMBA_UART_MAX_NUM 8 + +#define AMBA_UART_RESET_FLAG 0 /* bit 0 */ + +/* Poll timeout in microseconds (atomic helpers use udelay). */ +#define AMBARELLA_UART_TIMEOUT_US 1000000 + +struct ambarella_uart_port { + struct uart_port port; + struct clk *uart_pll; + unsigned long flags; + u32 mcr; + /* Software copy of UART_IE, updated with the port lock held. */ + u32 ier; + bool console_line_ended; +}; + +static struct ambarella_uart_port ambarella_port[AMBA_UART_MAX_NUM]; + +static struct ambarella_uart_port * +to_ambarella_uart_port(struct uart_port *port) +{ + return container_of(port, struct ambarella_uart_port, port); +} + +static void serial_ambarella_ier_write(struct uart_port *port, u32 ier) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + + amb_port->ier =3D ier; + writel_relaxed(ier, port->membase + UART_IE_OFFSET); +} + +static void serial_ambarella_ier_set(struct uart_port *port, u32 set) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + + amb_port->ier |=3D set; + writel_relaxed(amb_port->ier, port->membase + UART_IE_OFFSET); +} + +static void serial_ambarella_ier_clear(struct uart_port *port, u32 clear) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + + amb_port->ier &=3D ~clear; + writel_relaxed(amb_port->ier, port->membase + UART_IE_OFFSET); +} + +static void serial_ambarella_ier_toggle(struct uart_port *port, u32 mask) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + + amb_port->ier &=3D ~mask; + writel_relaxed(amb_port->ier, port->membase + UART_IE_OFFSET); + amb_port->ier |=3D mask; + writel_relaxed(amb_port->ier, port->membase + UART_IE_OFFSET); +} + +static void __serial_ambarella_stop_tx(struct uart_port *port) +{ + serial_ambarella_ier_clear(port, UART_IE_ETBEI); +} + +static u32 __serial_ambarella_read_ms(struct uart_port *port) +{ + return readl_relaxed(port->membase + UART_MS_OFFSET); +} + +static void __serial_ambarella_enable_ms(struct uart_port *port) +{ + serial_ambarella_ier_set(port, UART_IE_EDSSI); +} + +static void __serial_ambarella_disable_ms(struct uart_port *port) +{ + serial_ambarella_ier_clear(port, UART_IE_EDSSI); +} + +static inline void wait_for_tx(struct uart_port *port) +{ + u32 ls; + int ret; + + ret =3D readl_poll_timeout_atomic(port->membase + UART_LS_OFFSET, ls, + ls & UART_LS_TEMT, 1, + AMBARELLA_UART_TIMEOUT_US); + if (likely(!ret)) + return; + + /* Recover a stuck TX path so console/poll can continue. */ + writel_relaxed(UART_FC_RX_2_TO_FULL | UART_FC_TX_EMPTY | + UART_FC_XMITR | UART_FC_RCVRR, + port->membase + UART_FC_OFFSET); + udelay(100); + writel_relaxed(UART_FC_FIFOE | UART_FC_RX_2_TO_FULL | + UART_FC_TX_EMPTY | UART_FC_XMITR | + UART_FC_RCVRR, + port->membase + UART_FC_OFFSET); +} + +static inline int wait_for_rx(struct uart_port *port) +{ + u32 ls; + + return readl_relaxed_poll_timeout_atomic(port->membase + UART_LS_OFFSET, + ls, ls & UART_LS_DR, 1, + AMBARELLA_UART_TIMEOUT_US); +} + +static inline int tx_fifo_is_full(struct uart_port *port) +{ + return !(readl_relaxed(port->membase + UART_US_OFFSET) & UART_US_TFNF); +} + +static void serial_ambarella_hw_setup(struct uart_port *port) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + + if (!test_and_set_bit(AMBA_UART_RESET_FLAG, &amb_port->flags)) { + if (amb_port->uart_pll) + port->uartclk =3D clk_get_rate(amb_port->uart_pll); + /* reset the whole UART only once */ + writel_relaxed(0x01, port->membase + UART_SRR_OFFSET); + mdelay(1); + writel_relaxed(0x00, port->membase + UART_SRR_OFFSET); + } + + writel_relaxed(UART_FC_FIFOE | UART_FC_RX_2_TO_FULL | UART_FC_TX_EMPTY | + UART_FC_XMITR | UART_FC_RCVRR, port->membase + UART_FC_OFFSET); + /* Keep interrupts disabled until the IRQ handler is registered. */ + serial_ambarella_ier_write(port, 0); +} + +static inline void serial_ambarella_receive_chars(struct uart_port *port, + u32 tmo) +{ + u32 ch, flag, ls; + bool have_char; + int max_count; + + ls =3D readl_relaxed(port->membase + UART_LS_OFFSET); + max_count =3D port->fifosize; + + do { + flag =3D TTY_NORMAL; + have_char =3D ls & UART_LS_DR; + if (have_char || (ls & UART_LS_BI) || tmo) + ch =3D readl_relaxed(port->membase + UART_RB_OFFSET); + if (have_char) { + port->icount.rx++; + tmo =3D 0; + } + + if (unlikely(ls & (UART_LS_BI | UART_LS_PE | + UART_LS_FE | UART_LS_OE))) { + if (ls & UART_LS_BI) { + ls &=3D ~(UART_LS_FE | UART_LS_PE); + port->icount.brk++; + + if (uart_handle_break(port)) + goto ignore_char; + } + if (ls & UART_LS_FE) + port->icount.frame++; + if (ls & UART_LS_PE) + port->icount.parity++; + if (ls & UART_LS_OE) + port->icount.overrun++; + + ls &=3D port->read_status_mask; + + if (ls & UART_LS_BI) + flag =3D TTY_BREAK; + else if (ls & UART_LS_FE) + flag =3D TTY_FRAME; + else if (ls & UART_LS_PE) + flag =3D TTY_PARITY; + else if (ls & UART_LS_OE) + flag =3D TTY_OVERRUN; + + if (ls & UART_LS_OE) + pr_debug("%s: OVERFLOW\n", __func__); + } + + if (have_char) { + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, ls, UART_LS_OE, ch, flag); + } + +ignore_char: + ls =3D readl_relaxed(port->membase + UART_LS_OFFSET); + } while ((ls & (UART_LS_DR | UART_LS_BI)) && (max_count-- > 0)); + + tty_flip_buffer_push(&port->state->port); +} + +static void serial_ambarella_transmit_chars(struct uart_port *port) +{ + struct tty_port *tport =3D &port->state->port; + int count; + + if (port->x_char) { + writel_relaxed(port->x_char, port->membase + UART_TH_OFFSET); + port->icount.tx++; + port->x_char =3D 0; + return; + } + + if (uart_tx_stopped(port) || kfifo_is_empty(&tport->xmit_fifo)) { + __serial_ambarella_stop_tx(port); + return; + } + + count =3D port->fifosize; + while (count-- > 0) { + unsigned char c; + + if (tx_fifo_is_full(port)) + break; + + if (!kfifo_peek(&tport->xmit_fifo, &c)) + break; + + writel_relaxed(c, port->membase + UART_TH_OFFSET); + kfifo_skip(&tport->xmit_fifo); + port->icount.tx++; + if (kfifo_is_empty(&tport->xmit_fifo)) + break; + } + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(port); + if (kfifo_is_empty(&tport->xmit_fifo)) + __serial_ambarella_stop_tx(port); +} + +static inline void serial_ambarella_check_modem_status(struct uart_port *p= ort) +{ + u32 ms; + + ms =3D __serial_ambarella_read_ms(port); + + if (ms & UART_MS_RI) + port->icount.rng++; + if (ms & UART_MS_DSR) + port->icount.dsr++; + if (ms & UART_MS_DCTS) + uart_handle_cts_change(port, (ms & UART_MS_CTS)); + if (ms & UART_MS_DDCD) + uart_handle_dcd_change(port, (ms & UART_MS_DCD)); + + wake_up_interruptible(&port->state->port.delta_msr_wait); +} + +static irqreturn_t serial_ambarella_irq(int irq, void *dev_id) +{ + struct uart_port *port =3D dev_id; + u32 ii; + + scoped_guard(uart_port_lock_irqsave, port) { + ii =3D readl_relaxed(port->membase + UART_II_OFFSET); + switch (ii & 0x0F) { + case UART_II_MODEM_STATUS_CHANGED: + serial_ambarella_check_modem_status(port); + break; + case UART_II_THR_EMPTY: + serial_ambarella_transmit_chars(port); + break; + case UART_II_RCV_STATUS: + case UART_II_RCV_DATA_AVAIL: + serial_ambarella_receive_chars(port, 0); + break; + case UART_II_CHAR_TIMEOUT_FIFO_EMPTY: + /* Clear ERETOI to dismiss timeout-with-empty-FIFO IRQ */ + serial_ambarella_ier_toggle(port, UART_IE_ERETOI); + fallthrough; + case UART_II_CHAR_TIMEOUT: + serial_ambarella_receive_chars(port, 1); + break; + case UART_II_NO_INT_PENDING: + break; + default: + pr_debug("%s: 0x%x\n", __func__, ii); + break; + } + } + + return IRQ_HANDLED; +} + +static void serial_ambarella_enable_ms(struct uart_port *port) +{ + __serial_ambarella_enable_ms(port); +} + +static void serial_ambarella_start_tx(struct uart_port *port) +{ + /* if transmit buffer is not allocated, just return */ + if (!port->state->port.xmit_buf) + return; + + serial_ambarella_ier_set(port, UART_IE_ETBEI); + serial_ambarella_transmit_chars(port); +} + +static void serial_ambarella_stop_tx(struct uart_port *port) +{ + __serial_ambarella_stop_tx(port); +} + +static void serial_ambarella_stop_rx(struct uart_port *port) +{ + serial_ambarella_ier_clear(port, UART_IE_ERBFI); +} + +static unsigned int serial_ambarella_tx_empty(struct uart_port *port) +{ + unsigned int lsr; + + guard(uart_port_lock_irqsave)(port); + lsr =3D readl_relaxed(port->membase + UART_LS_OFFSET); + + return ((lsr & (UART_LS_TEMT | UART_LS_THRE)) =3D=3D + (UART_LS_TEMT | UART_LS_THRE)) ? TIOCSER_TEMT : 0; +} + +static unsigned int serial_ambarella_get_mctrl(struct uart_port *port) +{ + u32 ms, mctrl =3D 0; + + ms =3D __serial_ambarella_read_ms(port); + + if (ms & UART_MS_CTS) + mctrl |=3D TIOCM_CTS; + if (ms & UART_MS_DSR) + mctrl |=3D TIOCM_DSR; + if (ms & UART_MS_RI) + mctrl |=3D TIOCM_RI; + if (ms & UART_MS_DCD) + mctrl |=3D TIOCM_CD; + + return mctrl; +} + +static void serial_ambarella_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + u32 mcr, mcr_new =3D 0; + + mcr =3D readl_relaxed(port->membase + UART_MC_OFFSET); + + if (mctrl & TIOCM_DTR) + mcr_new |=3D UART_MC_DTR; + if (mctrl & TIOCM_RTS) + mcr_new |=3D UART_MC_RTS; + if (mctrl & TIOCM_OUT1) + mcr_new |=3D UART_MC_OUT1; + if (mctrl & TIOCM_OUT2) + mcr_new |=3D UART_MC_OUT2; + if (mctrl & TIOCM_LOOP) + mcr_new |=3D UART_MC_LB; + + mcr_new |=3D amb_port->mcr; + if (mcr_new !=3D mcr) { + if ((mcr & UART_MC_AFCE) =3D=3D UART_MC_AFCE) { + mcr &=3D ~UART_MC_AFCE; + writel_relaxed(mcr, port->membase + UART_MC_OFFSET); + } + writel_relaxed(mcr_new, port->membase + UART_MC_OFFSET); + } +} + +static void serial_ambarella_break_ctl(struct uart_port *port, int break_s= tate) +{ + u32 lcr; + + guard(uart_port_lock_irqsave)(port); + lcr =3D readl_relaxed(port->membase + UART_LC_OFFSET); + if (break_state !=3D 0) + writel_relaxed(lcr | UART_LC_BRK, port->membase + UART_LC_OFFSET); + else + writel_relaxed(lcr & ~UART_LC_BRK, port->membase + UART_LC_OFFSET); +} + +static void serial_ambarella_hw_deinit(struct ambarella_uart_port *amb_por= t) +{ + struct uart_port *port =3D &amb_port->port; + + /* Disable interrupts */ + serial_ambarella_ier_write(port, 0); + + /* Reset the Rx and Tx FIFOs */ + writel_relaxed(UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR, + port->membase + UART_SRR_OFFSET); +} + +static int serial_ambarella_startup(struct uart_port *port) +{ + int rval; + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + + serial_ambarella_hw_setup(port); + + rval =3D request_irq(port->irq, serial_ambarella_irq, IRQF_TRIGGER_HIGH, + dev_name(amb_port->port.dev), &amb_port->port); + if (rval < 0) { + dev_err(amb_port->port.dev, + "Failed to register ISR for IRQ %d\n", port->irq); + serial_ambarella_hw_deinit(amb_port); + return rval; + } + + serial_ambarella_ier_write(port, DEFAULT_AMBARELLA_UART_IER); + + return 0; +} + +static void serial_ambarella_shutdown(struct uart_port *port) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + u32 lcr; + + scoped_guard(uart_port_lock_irqsave, port) { + serial_ambarella_hw_deinit(amb_port); + lcr =3D readl_relaxed(port->membase + UART_LC_OFFSET); + writel_relaxed(lcr & ~UART_LC_BRK, port->membase + UART_LC_OFFSET); + } + + free_irq(amb_port->port.irq, &amb_port->port); +} + +static void serial_ambarella_set_termios(struct uart_port *port, + struct ktermios *termios, + const struct ktermios *old) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + unsigned int baud, quot; + u32 lc =3D 0x0; + + port->uartclk =3D clk_get_rate(amb_port->uart_pll); + switch (termios->c_cflag & CSIZE) { + case CS5: + lc |=3D UART_LC_CLS_5_BITS; + break; + case CS6: + lc |=3D UART_LC_CLS_6_BITS; + break; + case CS7: + lc |=3D UART_LC_CLS_7_BITS; + break; + case CS8: + default: + lc |=3D UART_LC_CLS_8_BITS; + break; + } + + if (termios->c_cflag & CSTOPB) + lc |=3D UART_LC_STOP_2BIT; + else + lc |=3D UART_LC_STOP_1BIT; + + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + lc |=3D (UART_LC_PEN | UART_LC_ODD_PARITY); + else + lc |=3D (UART_LC_PEN | UART_LC_EVEN_PARITY); + } + + baud =3D uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + quot =3D uart_get_divisor(port, baud); + + scoped_guard(uart_port_lock_irqsave, port) { + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask =3D UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + port->read_status_mask |=3D UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |=3D UART_LSR_BI; + + port->ignore_status_mask =3D 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |=3D UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |=3D UART_LSR_BI; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |=3D UART_LSR_OE; + } + if ((termios->c_cflag & CREAD) =3D=3D 0) + port->ignore_status_mask |=3D UART_LSR_DR; + + if ((termios->c_cflag & CRTSCTS) =3D=3D 0) { + amb_port->mcr &=3D ~UART_MC_AFCE; + port->status &=3D ~UPSTAT_AUTOCTS; + } else { + amb_port->mcr |=3D UART_MC_AFCE; + port->status |=3D UPSTAT_AUTOCTS; + } + + writel_relaxed(UART_LC_DLAB, port->membase + UART_LC_OFFSET); + writel_relaxed(quot & 0xff, port->membase + UART_DLL_OFFSET); + writel_relaxed((quot >> 8) & 0xff, port->membase + UART_DLH_OFFSET); + writel_relaxed(lc, port->membase + UART_LC_OFFSET); + if (UART_ENABLE_MS(port, termios->c_cflag)) + __serial_ambarella_enable_ms(port); + else + __serial_ambarella_disable_ms(port); + serial_ambarella_set_mctrl(port, port->mctrl); + } +} + +static void serial_ambarella_pm(struct uart_port *port, + unsigned int state, unsigned int oldstate) +{ +} + +static void serial_ambarella_release_port(struct uart_port *port) +{ +} + +static int serial_ambarella_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_ambarella_config_port(struct uart_port *port, int flags) +{ +} + +static int serial_ambarella_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int rval =3D 0; + + if (ser->type !=3D PORT_UNKNOWN && ser->type !=3D PORT_UART00) + rval =3D -EINVAL; + if (port->irq !=3D ser->irq) + rval =3D -EINVAL; + if (ser->io_type !=3D SERIAL_IO_MEM) + rval =3D -EINVAL; + + return rval; +} + +static const char *serial_ambarella_type(struct uart_port *port) +{ + return "ambuart"; +} + +#ifdef CONFIG_CONSOLE_POLL +static void serial_ambarella_poll_put_char(struct uart_port *port, + unsigned char chr) +{ + if (!port->suspended) { + wait_for_tx(port); + writel_relaxed(chr, port->membase + UART_TH_OFFSET); + } +} + +static int serial_ambarella_poll_get_char(struct uart_port *port) +{ + if (port->suspended) + return NO_POLL_CHAR; + + if (wait_for_rx(port)) + return NO_POLL_CHAR; + + return readl_relaxed(port->membase + UART_RB_OFFSET); +} +#endif + +static const struct uart_ops serial_ambarella_pops =3D { + .tx_empty =3D serial_ambarella_tx_empty, + .set_mctrl =3D serial_ambarella_set_mctrl, + .get_mctrl =3D serial_ambarella_get_mctrl, + .stop_tx =3D serial_ambarella_stop_tx, + .start_tx =3D serial_ambarella_start_tx, + .stop_rx =3D serial_ambarella_stop_rx, + .enable_ms =3D serial_ambarella_enable_ms, + .break_ctl =3D serial_ambarella_break_ctl, + .startup =3D serial_ambarella_startup, + .shutdown =3D serial_ambarella_shutdown, + .set_termios =3D serial_ambarella_set_termios, + .pm =3D serial_ambarella_pm, + .type =3D serial_ambarella_type, + .release_port =3D serial_ambarella_release_port, + .request_port =3D serial_ambarella_request_port, + .config_port =3D serial_ambarella_config_port, + .verify_port =3D serial_ambarella_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char =3D serial_ambarella_poll_put_char, + .poll_get_char =3D serial_ambarella_poll_get_char, +#endif +}; + +#if defined(CONFIG_SERIAL_AMBARELLA_CONSOLE) + +static struct uart_driver serial_ambarella_reg; + +static void serial_ambarella_putchar(struct uart_port *port, unsigned char= ch) +{ + wait_for_tx(port); + writel_relaxed(ch, port->membase + UART_TH_OFFSET); +} + +static void serial_ambarella_console_putchar(struct uart_port *port, + unsigned char ch) +{ + struct ambarella_uart_port *amb_port =3D to_ambarella_uart_port(port); + + serial_ambarella_putchar(port, ch); + amb_port->console_line_ended =3D (ch =3D=3D '\n'); +} + +static void serial_ambarella_console_device_lock(struct console *co, + unsigned long *flags) +{ + __uart_port_lock_irqsave(&ambarella_port[co->index].port, flags); +} + +static void serial_ambarella_console_device_unlock(struct console *co, + unsigned long flags) +{ + __uart_port_unlock_irqrestore(&ambarella_port[co->index].port, flags); +} + +static void serial_ambarella_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct ambarella_uart_port *amb_port =3D &ambarella_port[co->index]; + struct uart_port *port =3D &amb_port->port; + + if (port->suspended) + return; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + if (!amb_port->console_line_ended) + uart_console_write(port, "\n", 1, serial_ambarella_console_putchar); + uart_console_write(port, wctxt->outbuf, wctxt->len, + serial_ambarella_console_putchar); + wait_for_tx(port); + + nbcon_exit_unsafe(wctxt); +} + +static void serial_ambarella_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct ambarella_uart_port *amb_port =3D &ambarella_port[co->index]; + struct uart_port *port =3D &amb_port->port; + + if (port->suspended) + return; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + if (nbcon_exit_unsafe(wctxt)) { + unsigned int len =3D READ_ONCE(wctxt->len); + unsigned int i; + + /* + * Toggle unsafe per byte so a higher-priority context can + * take over. After a failed enter/exit, outbuf/len are no + * longer trusted and printing must stop. + */ + for (i =3D 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) + break; + uart_console_write(port, wctxt->outbuf + i, 1, + serial_ambarella_console_putchar); + if (!nbcon_exit_unsafe(wctxt)) + break; + } + } + + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + wait_for_tx(port); + nbcon_exit_unsafe(wctxt); +} + +static int __init serial_ambarella_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + struct ambarella_uart_port *amb_port; + int baud =3D 115200, bits =3D 8, parity =3D 'n', flow =3D 'n'; + + if (co->index < 0 || co->index >=3D serial_ambarella_reg.nr) + co->index =3D 0; + + amb_port =3D &ambarella_port[co->index]; + port =3D &amb_port->port; + if (!port->membase) { + pr_err("No device available for serial console\n"); + return -ENODEV; + } + + port->ops =3D &serial_ambarella_pops; + port->line =3D co->index; + amb_port->console_line_ended =3D true; + + serial_ambarella_hw_setup(port); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console serial_ambarella_console =3D { + .name =3D "ttyAB", + .device =3D uart_console_device, + .setup =3D serial_ambarella_console_setup, + .write_atomic =3D serial_ambarella_console_write_atomic, + .write_thread =3D serial_ambarella_console_write_thread, + .device_lock =3D serial_ambarella_console_device_lock, + .device_unlock =3D serial_ambarella_console_device_unlock, + .flags =3D CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON, + .index =3D -1, + .data =3D &serial_ambarella_reg, +}; + +static void serial_ambarella_console_early_write(struct console *con, + const char *s, + unsigned int count) +{ + struct earlycon_device *dev =3D con->data; + + uart_console_write(&dev->port, s, count, serial_ambarella_putchar); +} + +static int __init serial_ambarella_console_early_setup(struct earlycon_dev= ice *dev, + const char *opt) +{ + if (!dev->port.membase) + return -ENODEV; + + dev->con->write =3D serial_ambarella_console_early_write; + + return 0; +} + +OF_EARLYCON_DECLARE(ambarella_uart, "ambarella,cv75-uart", + serial_ambarella_console_early_setup); + +#define AMBARELLA_CONSOLE (&serial_ambarella_console) +#else +#define AMBARELLA_CONSOLE NULL +#endif + +static struct uart_driver serial_ambarella_reg =3D { + .owner =3D THIS_MODULE, + .driver_name =3D "ambarella-uart", + .dev_name =3D "ttyAB", + .major =3D 0, + .minor =3D 0, + .nr =3D AMBA_UART_MAX_NUM, + .cons =3D AMBARELLA_CONSOLE, +}; + +static int serial_ambarella_probe(struct platform_device *pdev) +{ + struct ambarella_uart_port *amb_port; + struct resource *mem; + struct pinctrl *pinctrl; + int irq, id, rval; + + mem =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource!\n"); + return -ENODEV; + } + + irq =3D platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource!\n"); + return -ENODEV; + } + + id =3D of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0 || id >=3D serial_ambarella_reg.nr) { + dev_err(&pdev->dev, "Invalid uart ID %d!\n", id); + return -ENXIO; + } + + pinctrl =3D devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_err(&pdev->dev, "Failed to request pinctrl\n"); + return PTR_ERR(pinctrl); + } + + amb_port =3D &ambarella_port[id]; + + amb_port->uart_pll =3D devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(amb_port->uart_pll)) { + dev_err(&pdev->dev, "Get uart clk failed!\n"); + return PTR_ERR(amb_port->uart_pll); + } + + amb_port->mcr =3D DEFAULT_AMBARELLA_UART_MCR; + + amb_port->port.dev =3D &pdev->dev; + amb_port->port.type =3D PORT_UART00; + amb_port->port.iotype =3D UPIO_MEM; + amb_port->port.fifosize =3D UART_FIFO_SIZE; + amb_port->port.uartclk =3D clk_get_rate(amb_port->uart_pll); + amb_port->port.ops =3D &serial_ambarella_pops; + amb_port->port.irq =3D irq; + amb_port->port.line =3D id; + amb_port->port.mapbase =3D mem->start; + amb_port->port.membase =3D devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(amb_port->port.membase)) + return PTR_ERR(amb_port->port.membase); + + rval =3D uart_add_one_port(&serial_ambarella_reg, &amb_port->port); + if (rval < 0) + dev_err(&pdev->dev, "failed to add port: %d, %d!\n", id, rval); + + platform_set_drvdata(pdev, amb_port); + + return rval; +} + +static void serial_ambarella_remove(struct platform_device *pdev) +{ + struct ambarella_uart_port *amb_port; + + amb_port =3D platform_get_drvdata(pdev); + uart_remove_one_port(&serial_ambarella_reg, &amb_port->port); +} + +static int serial_ambarella_suspend(struct device *dev) +{ + struct ambarella_uart_port *amb_port =3D dev_get_drvdata(dev); + + return uart_suspend_port(&serial_ambarella_reg, &amb_port->port); +} + +static int serial_ambarella_resume(struct device *dev) +{ + struct ambarella_uart_port *amb_port =3D dev_get_drvdata(dev); + + clear_bit(AMBA_UART_RESET_FLAG, &amb_port->flags); + serial_ambarella_hw_setup(&amb_port->port); + + return uart_resume_port(&serial_ambarella_reg, &amb_port->port); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(serial_ambarella_pm_ops, + serial_ambarella_suspend, + serial_ambarella_resume); + +static const struct of_device_id ambarella_serial_of_match[] =3D { + { .compatible =3D "ambarella,cv75-uart" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ambarella_serial_of_match); + +static struct platform_driver serial_ambarella_driver =3D { + .probe =3D serial_ambarella_probe, + .remove =3D serial_ambarella_remove, + .driver =3D { + .name =3D "ambarella-uart", + .of_match_table =3D ambarella_serial_of_match, + .pm =3D pm_sleep_ptr(&serial_ambarella_pm_ops), + }, +}; + +static int __init serial_ambarella_init(void) +{ + int rval; + + rval =3D uart_register_driver(&serial_ambarella_reg); + if (rval < 0) + return rval; + + rval =3D platform_driver_register(&serial_ambarella_driver); + if (rval < 0) { + uart_unregister_driver(&serial_ambarella_reg); + return rval; + } + + return 0; +} + +static void __exit serial_ambarella_exit(void) +{ + platform_driver_unregister(&serial_ambarella_driver); + uart_unregister_driver(&serial_ambarella_reg); +} + +module_init(serial_ambarella_init); +module_exit(serial_ambarella_exit); + +MODULE_DESCRIPTION("Ambarella UART driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ambarella-uart"); --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 92546434E35; Thu, 6 Aug 2026 09:34:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; cv=none; b=oqx4zN6VQZx17anMfks3ysRmLndMCWFrYtWwNwSgVk2hj5l0E34tIT5t+EjdOLFIxk1C4rUwdfS+XK0xAX9WpZB9sWjDVnQ+uEaraqN22JoCZUFxlu1ZtX11BM7bnwdVvrHwfB5GgAePYLCwu+Td5iUg8ME8z6oRpAtdnVGTtu4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; c=relaxed/simple; bh=X8b8p1l2o3D0Xv9RAiFs7ueXKdhQZGuH/6Gj1rFylvY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AXHt8HGvHqju5UsFotU2eQr8NDIwCRYQZ501zBMf71E4jBng0BV1wWN37KTaE//tcCVvyrC4GXrnUDMn6rEweHoHhyx2jR0s4ITXefyCiU4mG00D1cPHF23B9FPQ8jFSJc9jWBYbU3Lr/UjQljA1PkvLFnW3j4eLkNAr0z4XMh8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tFDKQUDU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="tFDKQUDU" Received: by smtp.kernel.org (Postfix) with ESMTPS id F1FADC2BCF6; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008867; bh=X8b8p1l2o3D0Xv9RAiFs7ueXKdhQZGuH/6Gj1rFylvY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=tFDKQUDUypL0Mk8C2b23TYhwU3jR4x5WbRTnIKE5lmys9ANWdRw4nzPq0ldL6/88R TETWRDWcPu082N0GREl7U1wfD3Tw1OptsOuJwszneFWZYvb6aOIFNQlJSIAUsQ4Pg0 aIDTP8BBR+JjBlJZe65YuV5CLH4gO+uApQvMwJccDMEX+OE6t98Ypx6JxkURsMGGSR MKuKE2FZLKNqVfoJkgIR+1A5basD88irSji3icGNWknNlXHAZ/jkVHCCIfd/6pzlnK Ttsdq3HtugX9hmP+Buy6WGsRkYk45Ldcg2rkWIq/dELcjHVAIKnhi1Rg+UfVRlBcnO QE0e8m6yTYYsw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id DF399C5620A; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:17 +0800 Subject: [PATCH v2 09/10] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-9-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=7996; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=6vJ94VlfBvvZA0fx7qMQy5vR9YqyghJ/Vg2eYaSwrT0=; b=afzO8MptAlCtg3qKNK2CZh4dztk4jr5TNbqA9ZLgdFbIW1HtIDsh8R/CvpypbD7NwrXTQef2V 73JbV2916x8DHOqbxIxzv4/ZctQ26wEXQmVMc26v8uUruEGxedd2Nt2 X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Enable the Ambarella ARMv8 platform Kconfig, add the CV75 SoC and EVK device trees, and select the Ambarella UART in the arm64 defconfig for console bring-up. Signed-off-by: Long Zhao --- arch/arm64/Kconfig.platforms | 9 ++ arch/arm64/boot/dts/Makefile | 1 + arch/arm64/boot/dts/ambarella/Makefile | 3 + arch/arm64/boot/dts/ambarella/cv75-evk.dts | 30 ++++++ arch/arm64/boot/dts/ambarella/cv75.dtsi | 166 +++++++++++++++++++++++++= ++++ arch/arm64/configs/defconfig | 3 + 6 files changed, 212 insertions(+) diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index d2acfac73003..166430ddb3c9 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -26,6 +26,15 @@ config ARCH_SUNXI help This enables support for Allwinner sunxi based SoCs like the A64. =20 +config ARCH_AMBARELLA + bool "Ambarella SoC family" + select ARM_PSCI + select HAVE_ARM_ARCH_TIMER + select PINCTRL + select PINCTRL_AMB + help + This enables support for the ARM64 based Ambarella SoCs like CV75. + config ARCH_ALPINE bool "Annapurna Labs Alpine platform" select ALPINE_MSI if PCI diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index fc726b215f12..f1709eb5d915 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -4,6 +4,7 @@ subdir-y +=3D airoha subdir-y +=3D allwinner subdir-y +=3D altera subdir-y +=3D amazon +subdir-y +=3D ambarella subdir-y +=3D amd subdir-y +=3D amlogic subdir-y +=3D apm diff --git a/arch/arm64/boot/dts/ambarella/Makefile b/arch/arm64/boot/dts/a= mbarella/Makefile new file mode 100644 index 000000000000..ddca3d7b18cc --- /dev/null +++ b/arch/arm64/boot/dts/ambarella/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +dtb-$(CONFIG_ARCH_AMBARELLA) +=3D cv75-evk.dtb diff --git a/arch/arm64/boot/dts/ambarella/cv75-evk.dts b/arch/arm64/boot/d= ts/ambarella/cv75-evk.dts new file mode 100644 index 000000000000..42a0fe24b83b --- /dev/null +++ b/arch/arm64/boot/dts/ambarella/cv75-evk.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Ambarella CV75 EVK board + */ + +/dts-v1/; + +#include "cv75.dtsi" + +/ { + model =3D "Ambarella CV75 EVK Board"; + compatible =3D "ambarella,cv75-evk", "ambarella,cv75"; + + aliases { + serial0 =3D &uart0; + }; + + chosen { + stdout-path =3D &uart0; + }; + + memory@200000 { + device_type =3D "memory"; + reg =3D <0x0 0x00200000 0x0 0x07e00000>; + }; +}; + +&uart0 { + status =3D "okay"; +}; diff --git a/arch/arm64/boot/dts/ambarella/cv75.dtsi b/arch/arm64/boot/dts/= ambarella/cv75.dtsi new file mode 100644 index 000000000000..825b773f052f --- /dev/null +++ b/arch/arm64/boot/dts/ambarella/cv75.dtsi @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Ambarella CV75 SoC + * + * Boot peripheral: UART0 console. + * Clocks: a single ambarella,cv75-rct provider. + */ + +#include +#include + +/ { + compatible =3D "ambarella,cv75"; + interrupt-parent =3D <&gic>; + #address-cells =3D <2>; + #size-cells =3D <2>; + + osc: oscillator { + compatible =3D "fixed-clock"; + #clock-cells =3D <0>; + clock-output-names =3D "osc"; + clock-frequency =3D <24000000>; + }; + + psci { + compatible =3D "arm,psci-0.2"; + method =3D "smc"; + }; + + cpus { + #address-cells =3D <1>; + #size-cells =3D <0>; + + cpu@0 { + compatible =3D "arm,cortex-a76"; + device_type =3D "cpu"; + reg =3D <0x000>; + enable-method =3D "psci"; + }; + + cpu@1 { + compatible =3D "arm,cortex-a76"; + device_type =3D "cpu"; + reg =3D <0x100>; + enable-method =3D "psci"; + }; + }; + + gic: interrupt-controller@fff0101000 { + compatible =3D "arm,gic-400"; + #interrupt-cells =3D <3>; + #address-cells =3D <0>; + #size-cells =3D <1>; + interrupt-controller; + reg =3D <0xff 0xf0101000 0x0 0x1000>, + <0xff 0xf0102000 0x0 0x2000>, + <0xff 0xf0104000 0x0 0x2000>, + <0xff 0xf0106000 0x0 0x2000>; + interrupts =3D ; + }; + + timer { + compatible =3D "arm,armv8-timer"; + interrupts =3D , + , + , + , + ; + }; + + soc { + compatible =3D "simple-bus"; + #address-cells =3D <2>; + #size-cells =3D <2>; + ranges; + + /* Clock provider and shared system registers. */ + rct: clock-controller@ffed080000 { + compatible =3D "ambarella,cv75-rct", "syscon"; + reg =3D <0xff 0xed080000 0x0 0x1000>; + clocks =3D <&osc>; + clock-names =3D "osc"; + #clock-cells =3D <1>; + + assigned-clocks =3D <&rct CV75_GCLK_UART0>; + assigned-clock-parents =3D <&osc>; + assigned-clock-rates =3D <24000000>; + }; + + apb@ffe4000000 { + compatible =3D "simple-bus"; + #address-cells =3D <1>; + #size-cells =3D <1>; + reg =3D <0xff 0xe4000000 0x0 0x01000000>; + ranges =3D <0xe4000000 0xff 0xe4000000 0x01000000>; + + uart0: serial@e4000000 { + compatible =3D "ambarella,cv75-uart"; + reg =3D <0xe4000000 0x1000>; + interrupts =3D ; + clocks =3D <&rct CV75_GCLK_UART0>; + pinctrl-names =3D "default"; + pinctrl-0 =3D <&uart0_pins>; + status =3D "disabled"; + }; + + pinctrl: pinctrl@e4010000 { + compatible =3D "ambarella,cv75-pinctrl"; + #address-cells =3D <1>; + #size-cells =3D <1>; + reg =3D <0xe4010000 0x1000>; + ranges; + ambarella,drive-strength-syscon =3D <&rct>; + ambarella,pull-syscon =3D <&s_scratchpad_syscon>; + + gpio0: gpio@e4013000 { + reg =3D <0xe4013000 0x1000>; + interrupts =3D ; + gpio-controller; + #gpio-cells =3D <2>; + gpio-ranges =3D <&pinctrl 0 0 32>; + interrupt-controller; + #interrupt-cells =3D <2>; + }; + + gpio1: gpio@e4014000 { + reg =3D <0xe4014000 0x1000>; + interrupts =3D ; + gpio-controller; + #gpio-cells =3D <2>; + gpio-ranges =3D <&pinctrl 0 32 32>; + interrupt-controller; + #interrupt-cells =3D <2>; + }; + + gpio2: gpio@e4015000 { + reg =3D <0xe4015000 0x1000>; + interrupts =3D ; + gpio-controller; + #gpio-cells =3D <2>; + gpio-ranges =3D <&pinctrl 0 64 32>; + interrupt-controller; + #interrupt-cells =3D <2>; + }; + + uart0_pins: uart0-pins { + function =3D "uart0"; + groups =3D "uart0tx", "uart0rx"; + }; + }; + }; + + ahb@ffe0000000 { + compatible =3D "simple-bus"; + #address-cells =3D <1>; + #size-cells =3D <1>; + reg =3D <0xff 0xe0000000 0x0 0x01000000>; + ranges =3D <0xe0000000 0xff 0xe0000000 0x01000000>; + + s_scratchpad_syscon: scratchpad@e002e000 { + compatible =3D "ambarella,cv75-sec-scratchpad", "syscon"; + reg =3D <0xe002e000 0x1000>; + }; + }; + }; +}; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index ad3bd3d9507b..64e8fbe43d27 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -36,6 +36,7 @@ CONFIG_CRASH_DUMP=3Dy CONFIG_ARCH_ACTIONS=3Dy CONFIG_ARCH_AIROHA=3Dy CONFIG_ARCH_SUNXI=3Dy +CONFIG_ARCH_AMBARELLA=3Dy CONFIG_ARCH_ALPINE=3Dy CONFIG_ARCH_APPLE=3Dy CONFIG_ARCH_ARTPEC=3Dy @@ -567,6 +568,8 @@ CONFIG_SERIAL_8250_OMAP=3Dy CONFIG_SERIAL_8250_MT6577=3Dy CONFIG_SERIAL_8250_UNIPHIER=3Dy CONFIG_SERIAL_OF_PLATFORM=3Dy +CONFIG_SERIAL_AMBARELLA=3Dy +CONFIG_SERIAL_AMBARELLA_CONSOLE=3Dy CONFIG_SERIAL_AMBA_PL011=3Dy CONFIG_SERIAL_AMBA_PL011_CONSOLE=3Dy CONFIG_SERIAL_MESON=3Dy --=20 2.34.1 From nobody Fri Oct 2 01:56:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 33D50416859; Thu, 6 Aug 2026 09:34:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; cv=none; b=Rn3agx7zjk2sfFuFhxkUWJ4IgOufj6nZ48FaIIjP0XoIA+ft3G8+XXS52SadaHg0uEFFNd+vNjbB0huWD9KrXgiMb+kZ+/cJ2K+Hi/FGskWEUnVsDaSY/kY2UZc/LJhT8+SbYYQwovn2lVqN39S8LAwZeH8VeFHzuogo13Q5i0w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786008867; c=relaxed/simple; bh=Z2E9zTsEDJP0zeF+i+JgJFiaNg9dXXSm8HlaEi6y/IU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rnwl37WAANm5L2eU28iSwavzlOtI2NF4RGmMtFuPgYh3YIUDez3wl8YnY+QshaEUNwSMMMY5Egxqw2/otueDRoEuhlDsi7qVol2i72i8spyjDtkhAuh/qwdAzYOr37EhqP6lrfd0uUk6LcChWB4Yl2lLokQbCakFi9ViLHzCGH4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pdizhPxX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="pdizhPxX" Received: by smtp.kernel.org (Postfix) with ESMTPS id 131F6C2BCF7; Thu, 6 Aug 2026 09:34:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786008867; bh=Z2E9zTsEDJP0zeF+i+JgJFiaNg9dXXSm8HlaEi6y/IU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=pdizhPxXUuHxGuXeSg4WLxPTMGebF0ymR/rm8GvYX8qlfK1JffTAeIQaJ+igQqnfB IaF/JuPBQMzqM2dldTLMPfjxo5L6Cwo2Zfqf8KJIV07vLFk3Y7nQbZ34g1z6udA67w 7e55vdo10X++1r21fxwabRtkis9BSqCNkiB+lbMy4VFfRYVK6mFegu98WfnmL7YKuY KibHJljxl02Cf1uSoVR4RhT18IhIBiQnSLyxQjpEwaA9kMGXg07FtpMJ3McOUt6xlf RmktpVeGZelocXSvVTy2+KDo7bOLf/YqQlICu26CnDQylTkqX8VTpuaJQ/B9CiVcuA zdWb06iA4d67w== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id F24CAC5AC67; Thu, 6 Aug 2026 09:34:26 +0000 (UTC) From: Long Zhao via B4 Relay Date: Thu, 06 Aug 2026 17:34:18 +0800 Subject: [PATCH v2 10/10] MAINTAINERS: add ARM/AMBARELLA SoC support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-longzhao-upstream-cv75-v2-v2-10-6b09707c5fe9@ambarella.com> References: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> In-Reply-To: <20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Turquette , Stephen Boyd , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Catalin Marinas , Will Deacon , Arnd Bergmann , Alexandre Belloni Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, soc@lists.linux.dev, Long Zhao X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786008863; l=1365; i=longzhao@ambarella.com; s=20260730; h=from:subject:message-id; bh=xx2F3xyEKnw6VoTF4kbTyKftrweRLyCy3Ck5hCpXsBs=; b=CYPiCCAYitDcu2nNmIqOUgU1M2v+u8yv6F5uq4RplmWynENMCpHv/8g2l3sZp2Jv6N1l+oF7H feJngjTBpAUDjsRTFuGGf14U9pSIDGhGLTCCQWqtln7EUtZQ8fI/oNM X-Developer-Key: i=longzhao@ambarella.com; a=ed25519; pk=/xGhPTOX0/eYsyTWzyjjgAIW1Z/YD+IlnOnvcdRozow= X-Endpoint-Received: by B4 Relay for longzhao@ambarella.com/20260730 with auth_id=903 X-Original-From: Long Zhao Reply-To: longzhao@ambarella.com From: Long Zhao Add a maintainer entry covering Ambarella CV75 bindings, drivers and device trees. Signed-off-by: Long Zhao --- MAINTAINERS | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 803b60ad628f..c06c84fab57a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2502,6 +2502,23 @@ F: drivers/clocksource/asm9260_timer.c F: drivers/rtc/rtc-asm9260.c F: drivers/watchdog/asm9260_wdt.c =20 +ARM/AMBARELLA SOC SUPPORT +M: Long Zhao +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: devicetree@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/arm/ambarella.yaml +F: Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml +F: Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinctrl.yaml +F: Documentation/devicetree/bindings/serial/ambarella,cv75-uart.yaml +F: Documentation/devicetree/bindings/soc/ambarella/ +F: arch/arm64/boot/dts/ambarella/ +F: drivers/clk/ambarella/ +F: drivers/pinctrl/pinctrl-ambarella* +F: drivers/tty/serial/ambarella_uart.c +F: include/dt-bindings/clock/ambarella,cv75-clock.h +N: ambarella + ARM/AMD PENSANDO ARM64 ARCHITECTURE M: Brad Larson L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) --=20 2.34.1