* [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT
@ 2026-08-13 9:56 Long Zhao via B4 Relay
2026-08-13 9:56 ` [PATCH v3 1/6] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Long Zhao via B4 Relay @ 2026-08-13 9:56 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michael Turquette,
Stephen Boyd, Catalin Marinas, Will Deacon, Arnd Bergmann,
Alexandre Belloni, Linus Walleij
Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, soc,
Long Zhao, Krzysztof Kozlowski
This is part A of a 3-part CV75 v3 split:
A: bindings + RCT/CCU clocks + SoC/EVK DT + MAINTAINERS (this series)
B: pinctrl + PL061 GPIO variant
C: 8250_dw UART quirks
B depends on A; C depends on A+B. B and C follow as separate threads.
Link to v2:
https://lore.kernel.org/r/20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com
Summary:
- arm / syscon / clock bindings and CV75 RCT CCU driver
- minimal CV75 SoC + EVK DT (UART0, clocks)
- MAINTAINERS entry for Ambarella
Changes since v2 (A scope):
- scratchpad as generic syscon (Krzysztof)
- RCT as dedicated clock-controller binding/header (Krzysztof)
- DT nodes under /soc with unit-address ordering (Krzysztof/Rob)
- clk robustness: div_u64 and mux/div + PLL edge cases (internal review)
Verified on CV75 with A+B+C (boot, UART, clk_summary).
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
Long Zhao (6):
dt-bindings: arm: add Ambarella CV75 platforms
dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad
dt-bindings: clock: add Ambarella CV75 RCT
clk: ambarella: add CV75 CCU driver
arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT
MAINTAINERS: add ARM/AMBARELLA SoC support
.../devicetree/bindings/arm/ambarella.yaml | 24 ++
.../bindings/clock/ambarella,cv75-rct.yaml | 58 ++++
Documentation/devicetree/bindings/mfd/syscon.yaml | 1 +
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 14 +
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 | 178 ++++++++++
arch/arm64/configs/defconfig | 1 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/ambarella/Kconfig | 13 +
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 | 231 +++++++++++++
drivers/clk/ambarella/ccu_mux_div.h | 35 ++
drivers/clk/ambarella/ccu_pll.c | 378 +++++++++++++++++++++
drivers/clk/ambarella/ccu_pll.h | 70 ++++
include/dt-bindings/clock/ambarella,cv75-rct.h | 15 +
23 files changed, 1452 insertions(+)
---
base-commit: 34cf6dafc47441dfb6b356a095b89c3585a93714
change-id: 20260812-cv75-v3-a-c49121aca9a2
Best regards,
--
Long Zhao <longzhao@ambarella.com>
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 1/6] dt-bindings: arm: add Ambarella CV75 platforms 2026-08-13 9:56 [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT Long Zhao via B4 Relay @ 2026-08-13 9:56 ` Long Zhao via B4 Relay 2026-08-13 9:56 ` [PATCH v3 2/6] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Long Zhao via B4 Relay ` (4 subsequent siblings) 5 siblings, 0 replies; 11+ messages in thread From: Long Zhao via B4 Relay @ 2026-08-13 9:56 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michael Turquette, Stephen Boyd, Catalin Marinas, Will Deacon, Arnd Bergmann, Alexandre Belloni, Linus Walleij Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, soc, Long Zhao, Krzysztof Kozlowski From: Long Zhao <longzhao@ambarella.com> Add the Ambarella vendor prefix and CV75 EVK board compatible for the initial SoC bring-up. Signed-off-by: Long Zhao <longzhao@ambarella.com> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> --- .../devicetree/bindings/arm/ambarella.yaml | 24 ++++++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 ++ 2 files changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/ambarella.yaml b/Documentation/devicetree/bindings/arm/ambarella.yaml new file mode 100644 index 000000000000..72946e94fa76 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/ambarella.yaml @@ -0,0 +1,24 @@ +# 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 <longzhao@ambarella.com> + +properties: + $nodename: + const: "/" + + compatible: + oneOf: + - description: Ambarella CV75 boards + items: + - enum: + - ambarella,cv75-evk + - const: ambarella,cv75 + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/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,.*": -- 2.34.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 2/6] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad 2026-08-13 9:56 [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT Long Zhao via B4 Relay 2026-08-13 9:56 ` [PATCH v3 1/6] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay @ 2026-08-13 9:56 ` Long Zhao via B4 Relay 2026-08-13 10:03 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 3/6] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay ` (3 subsequent siblings) 5 siblings, 1 reply; 11+ messages in thread From: Long Zhao via B4 Relay @ 2026-08-13 9:56 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michael Turquette, Stephen Boyd, Catalin Marinas, Will Deacon, Arnd Bergmann, Alexandre Belloni, Linus Walleij Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, soc, Long Zhao From: Long Zhao <longzhao@ambarella.com> Allow ambarella,cv75-sec-scratchpad with syscon for shared secure scratchpad registers used by pinctrl pull configuration. Signed-off-by: Long Zhao <longzhao@ambarella.com> --- Documentation/devicetree/bindings/mfd/syscon.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml index fe882d3f828b..9ebf702c06f1 100644 --- a/Documentation/devicetree/bindings/mfd/syscon.yaml +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml @@ -32,6 +32,7 @@ properties: - allwinner,sun50i-a64-system-controller - altr,l3regs - altr,sdr-ctl + - ambarella,cv75-sec-scratchpad - amd,pensando-elba-syscon - amlogic,meson-mx-assist - amlogic,meson-mx-bootrom -- 2.34.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/6] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad 2026-08-13 9:56 ` [PATCH v3 2/6] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Long Zhao via B4 Relay @ 2026-08-13 10:03 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-08-13 10:03 UTC (permalink / raw) To: Long Zhao; +Cc: robh, linux-clk, conor+dt, devicetree Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] The compatible string 'ambarella,cv75-sec-scratchpad' was omitted from the 'select' block in syscon.yaml. - [Low] The commit message subject for the MFD subsystem change does not capitalize the description. -- --- Patch [2]: [PATCH v3 2/6] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad --- commit ea79700d5daf0e265e9307d2ab596a80db0d5db8 Author: Long Zhao <longzhao@ambarella.com> dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Allow ambarella,cv75-sec-scratchpad with syscon for shared secure scratchpad registers used by pinctrl pull configuration. > dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad [Severity: Low] This isn't a bug, but should the word "add" be capitalized here to follow the MFD subsystem naming conventions? > diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml > index e228670880631..05c7f59aba5de 100644 > --- a/Documentation/devicetree/bindings/mfd/syscon.yaml > +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml > @@ -148,6 +148,7 @@ properties: > - allwinner,sun50i-a64-system-controller > - altr,l3regs > - altr,sdr-ctl > + - ambarella,cv75-sec-scratchpad > - amd,pensando-elba-syscon > - amlogic,meson-mx-assist > - amlogic,meson-mx-bootrom [Severity: Medium] Will older dtschema tools skip validation for this new compatible string? It appears this compatible string was omitted from the select block earlier in the file, which is required for compatibility with dtschema <2024.02: select: properties: compatible: contains: enum: ... - altr,sdr-ctl - amd,pensando-elba-syscon Should ambarella,cv75-sec-scratchpad also be added to this enum block so older tools correctly select and apply this schema? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260813-cv75-v3-a-v3-0-cfdd20b1b4dd@ambarella.com?part=2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 3/6] dt-bindings: clock: add Ambarella CV75 RCT 2026-08-13 9:56 [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT Long Zhao via B4 Relay 2026-08-13 9:56 ` [PATCH v3 1/6] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay 2026-08-13 9:56 ` [PATCH v3 2/6] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Long Zhao via B4 Relay @ 2026-08-13 9:56 ` Long Zhao via B4 Relay 2026-08-13 10:02 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 4/6] clk: ambarella: add CV75 CCU driver Long Zhao via B4 Relay ` (2 subsequent siblings) 5 siblings, 1 reply; 11+ messages in thread From: Long Zhao via B4 Relay @ 2026-08-13 9:56 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michael Turquette, Stephen Boyd, Catalin Marinas, Will Deacon, Arnd Bergmann, Alexandre Belloni, Linus Walleij Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, soc, Long Zhao From: Long Zhao <longzhao@ambarella.com> Document the CV75 RCT clock controller and the clock ID header used by DT consumers. Signed-off-by: Long Zhao <longzhao@ambarella.com> --- .../bindings/clock/ambarella,cv75-rct.yaml | 58 ++++++++++++++++++++++ include/dt-bindings/clock/ambarella,cv75-rct.h | 15 ++++++ 2 files changed, 73 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml new file mode 100644 index 000000000000..eb5956fc32bc --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml @@ -0,0 +1,58 @@ +# 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 <longzhao@ambarella.com> + +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-rct.h. + +properties: + + 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 = "ambarella,cv75-rct", "syscon"; + reg = <0xed080000 0x1000>; + clocks = <&osc>; + clock-names = "osc"; + #clock-cells = <1>; + }; diff --git a/include/dt-bindings/clock/ambarella,cv75-rct.h b/include/dt-bindings/clock/ambarella,cv75-rct.h new file mode 100644 index 000000000000..0a6e946c9108 --- /dev/null +++ b/include/dt-bindings/clock/ambarella,cv75-rct.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#ifndef _DT_BINDINGS_CLOCK_AMBARELLA_CV75_RCT_H +#define _DT_BINDINGS_CLOCK_AMBARELLA_CV75_RCT_H + +/* Boot / bus clocks */ +#define CV75_GCLK_UART0 0 +#define CV75_GCLK_CORE 1 +#define CV75_GCLK_AHB 2 +#define CV75_GCLK_APB 3 + +#endif -- 2.34.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 3/6] dt-bindings: clock: add Ambarella CV75 RCT 2026-08-13 9:56 ` [PATCH v3 3/6] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay @ 2026-08-13 10:02 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-08-13 10:02 UTC (permalink / raw) To: Long Zhao; +Cc: conor+dt, robh, linux-clk, devicetree Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The binding for the Ambarella CV75 Reset and Clock Tree (RCT) is missing the `#reset-cells` property, failing to describe the hardware's reset provider capabilities. Device tree bindings must describe complete hardware capabilities rather than just the subset supported by the initial driver implementation. -- --- Patch [3]: [PATCH v3 3/6] dt-bindings: clock: add Ambarella CV75 RCT --- commit 34559a1295566c6903b0871d6d486afaede08d21 Author: Long Zhao <longzhao@ambarella.com> dt-bindings: clock: add Ambarella CV75 RCT This commit documents the CV75 RCT clock controller and the clock ID header used by DT consumers. It introduces the new binding schema which defines the register properties, clock requirements, and provides an example node. > diff --git a/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml > new file mode 100644 > index 0000000000000..eb5956fc32bc4 > --- /dev/null > +++ b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml > @@ -0,0 +1,58 @@ [ ... ] > +properties: [ ... ] > + "#clock-cells": > + const: 1 > + > +required: > + - compatible > + - reg > + - clocks > + - clock-names > + - "#clock-cells" [Severity: Medium] Since the commit message indicates this is a Reset and Clock Tree (RCT) block, should #reset-cells be defined and included in this required properties list to accurately reflect the hardware's role as a reset provider? Device tree bindings typically need to describe complete hardware capabilities, so existing device trees that omit the property will not silently pass schema validation when reset support is eventually added. > + > +additionalProperties: false > + > +examples: > + - | > + clock-controller@ed080000 { > + compatible = "ambarella,cv75-rct", "syscon"; > + reg = <0xed080000 0x1000>; > + clocks = <&osc>; > + clock-names = "osc"; > + #clock-cells = <1>; > + }; [Severity: Medium] If #reset-cells is added to the binding, should this example node also be updated to demonstrate its correct usage alongside #clock-cells? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260813-cv75-v3-a-v3-0-cfdd20b1b4dd@ambarella.com?part=3 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 4/6] clk: ambarella: add CV75 CCU driver 2026-08-13 9:56 [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT Long Zhao via B4 Relay ` (2 preceding siblings ...) 2026-08-13 9:56 ` [PATCH v3 3/6] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay @ 2026-08-13 9:56 ` Long Zhao via B4 Relay 2026-08-13 10:10 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 5/6] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay 2026-08-13 9:56 ` [PATCH v3 6/6] MAINTAINERS: add ARM/AMBARELLA SoC support Long Zhao via B4 Relay 5 siblings, 1 reply; 11+ messages in thread From: Long Zhao via B4 Relay @ 2026-08-13 9:56 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michael Turquette, Stephen Boyd, Catalin Marinas, Will Deacon, Arnd Bergmann, Alexandre Belloni, Linus Walleij Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, soc, Long Zhao From: Long Zhao <longzhao@ambarella.com> Add the Ambarella CV75 clock driver covering the core PLL and the UART0 / AHB / APB clocks needed for early console bring-up. Signed-off-by: Long Zhao <longzhao@ambarella.com> --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/ambarella/Kconfig | 13 ++ 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 | 231 ++++++++++++++++++++++ drivers/clk/ambarella/ccu_mux_div.h | 35 ++++ drivers/clk/ambarella/ccu_pll.c | 378 ++++++++++++++++++++++++++++++++++++ drivers/clk/ambarella/ccu_pll.h | 70 +++++++ 11 files changed, 1116 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. 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) += clk-xgene.o # please keep this section sorted lexicographically by directory path name obj-y += actions/ +obj-y += ambarella/ obj-y += analogbits/ obj-y += aspeed/ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ diff --git a/drivers/clk/ambarella/Kconfig b/drivers/clk/ambarella/Kconfig new file mode 100644 index 000000000000..d79bb4bd6df5 --- /dev/null +++ b/drivers/clk/ambarella/Kconfig @@ -0,0 +1,13 @@ +# 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 the Ambarella CV75 RCT clock controller. + +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) += ccu_common.o ccu_mux_div.o ccu_pll.o +obj-$(CONFIG_CLK_AMBARELLA_CV75) += ccu-cv75.o diff --git a/drivers/clk/ambarella/ccu-cv75.c b/drivers/clk/ambarella/ccu-cv75.c new file mode 100644 index 000000000000..d682da3cf28d --- /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 <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/ambarella,cv75-rct.h> + +#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 = -1, + AMB_CV75_CLK_REF_DUMMY = -2, +}; + +/* PLL version for CV75 (ambarella,clkpll-v1 in vendor DTS) */ +static const struct amb_pll_soc_data cv75_pll_soc_data = { + .pll_version = 1, + .fsout_mask = CTRL2_FSOUT_DIV2, + .fsout_val = CTRL2_FSOUT_DIV2, + .fsdiv_mask = CTRL2_FSDIV_DIV2, + .fsdiv_val = CTRL2_FSDIV_DIV2, + .vcodiv_mask = CTRL2_VCODIV_DIV2, + .vcodiv_val = CTRL2_VCODIV_DIV2, + .vco_max_mhz = 2600UL, + .vco_min_mhz = 850UL, + .vco_range = { 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[] = { + { + .id = AMB_CV75_CLK_REF_DUMMY, + .type = AMB_CV75_CLK_FIXED_RATE, + .name = "dummy", + .fixed_rate.rate = 0, + }, + { + .id = CV75_GCLK_CORE, + .type = AMB_CV75_CLK_PLL, + .name = "core", + .parent = AMB_CV75_CLK_REF_OSC, + .pll = { + .reg_offset = { + 0x000, 0x004, 0x100, + 0x104, 0x000, 0x000, + }, + .soc_data = &cv75_pll_soc_data, + }, + }, + { + .id = CV75_GCLK_AHB, + .type = AMB_CV75_CLK_FIXED_FACTOR, + .name = "ahb", + .parent = CV75_GCLK_CORE, + .fixed_factor = { + .flags = 0, + .mult = 1, + .div = 2, + }, + }, + { + .id = CV75_GCLK_APB, + .type = AMB_CV75_CLK_FIXED_FACTOR, + .name = "apb", + .parent = CV75_GCLK_CORE, + .fixed_factor = { + .flags = 0, + .mult = 1, + .div = 4, + }, + }, + { + .id = CV75_GCLK_UART0, + .type = AMB_CV75_CLK_MUX_DIV, + .name = "uart0", + .mux_div = { + .name = "uart0", + .parents = (const int[]) { + AMB_CV75_CLK_REF_OSC, CV75_GCLK_CORE, + AMB_CV75_CLK_REF_DUMMY, AMB_CV75_CLK_REF_DUMMY, + }, + .num_parents = 4, + .mux_reg = 0x1c8, + .mux_shift = 0, + .mux_mask = 0x3, + .div_reg = 0x038, + .div_shift = 0, + .div_width = 24, + .div_flags = CLK_DIVIDER_ONE_BASED, + .fix_divider = 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 == AMB_CV75_CLK_REF_OSC) + return osc; + if (parent == AMB_CV75_CLK_REF_DUMMY) + return dummy; + + if (parent < 0 || parent >= 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 = &desc->mux_div; + struct clk_parent_data *pdata; + struct clk_hw *parent; + u8 i; + + pdata = devm_kcalloc(dev, md->num_parents, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < md->num_parents; i++) { + if (md->parents[i] == AMB_CV75_CLK_REF_OSC) { + pdata[i].fw_name = "osc"; + continue; + } + + parent = amb_cv75_get_parent(ccu, osc, dummy, md->parents[i]); + if (IS_ERR(parent)) + return parent; + + pdata[i].hw = 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 = 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 = amb_cv75_get_parent(ccu, osc, dummy, desc->parent); + if (IS_ERR(parent)) + return parent; + + pll_desc.name = desc->name; + pll_desc.parent = parent; + memcpy((void *)pll_desc.reg_offset, desc->pll.reg_offset, + sizeof(pll_desc.reg_offset)); + pll_desc.soc_data = desc->pll.soc_data; + pll_desc.frac_mode = false; + + return amb_pll_register(dev, ccu->map, &pll_desc); + case AMB_CV75_CLK_DIV: + parent = 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 = NULL, *hw; + int i; + + ccu = amb_ccu_init(pdev, ARRAY_SIZE(cv75_clks)); + if (IS_ERR(ccu)) + return PTR_ERR(ccu); + + osc_clk = 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 = __clk_get_hw(osc_clk); + + for (i = 0; i < ARRAY_SIZE(cv75_clks); i++) { + hw = 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 == AMB_CV75_CLK_REF_DUMMY) + dummy = hw; + else + ccu->data->hws[cv75_clks[i].id] = hw; + } + + return amb_ccu_register(ccu); +} + +static const struct of_device_id amb_cv75_rct_match[] = { + { .compatible = "ambarella,cv75-rct" }, + { } +}; +MODULE_DEVICE_TABLE(of, amb_cv75_rct_match); + +static struct platform_driver amb_cv75_rct_driver = { + .probe = amb_cv75_rct_probe, + .driver = { + .name = "ambarella-cv75-rct", + .of_match_table = 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 <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "ccu_common.h" + +static const struct regmap_config amb_rct_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 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 = devm_kzalloc(&pdev->dev, sizeof(*ccu), GFP_KERNEL); + if (!ccu) + return ERR_PTR(-ENOMEM); + + ccu->dev = &pdev->dev; + + ccu->data = devm_kzalloc(&pdev->dev, + struct_size(ccu->data, hws, num_clks), + GFP_KERNEL); + if (!ccu->data) + return ERR_PTR(-ENOMEM); + + ccu->data->num = num_clks; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return ERR_CAST(base); + + ccu->map = 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 <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/regmap.h> + +#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/ccu_mux_div.c new file mode 100644 index 000000000000..77368a988315 --- /dev/null +++ b/drivers/clk/ambarella/ccu_mux_div.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Ambarella, Inc. + * + * Regmap-backed mux + divider, derived from the vendor composite-clock driver. + */ + +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/math64.h> +#include <linux/regmap.h> + +#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 = to_amb_mux(hw); + u32 val; + + regmap_read(mux->map, mux->offset, &val); + val >>= mux->shift; + val &= mux->mask; + + return val; +} + +static int amb_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct amb_mux *mux = 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 = { + .get_parent = amb_mux_get_parent, + .set_parent = amb_mux_set_parent, +}; + +static unsigned long amb_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct amb_div *div = to_amb_div(hw); + unsigned long rate; + u32 val; + + regmap_read(div->map, div->offset, &val); + + /* Divider reset/disable bit sits above the field at shift+width. */ + if (val & (BIT(div->width) << div->shift)) + return 0; + + val >>= div->shift; + val &= clk_div_mask(div->width); + + rate = divider_recalc_rate(hw, parent_rate, val, NULL, + div->flags, div->width); + if (div->fix_divider) + rate = div_u64(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 = to_amb_div(hw); + struct clk_rate_request scaled = *req; + int ret; + + if (!req->rate && (div->flags & CLK_DIVIDER_ONE_BASED)) + return 0; + + if (div->fix_divider) { + scaled.rate = min(req->rate, + ULONG_MAX / div->fix_divider) * + div->fix_divider; + scaled.min_rate = min(req->min_rate, + ULONG_MAX / div->fix_divider) * + div->fix_divider; + scaled.max_rate = min(req->max_rate, + ULONG_MAX / div->fix_divider) * + div->fix_divider; + } + + ret = divider_determine_rate(hw, &scaled, NULL, div->width, + div->flags); + if (ret) + return ret; + + req->rate = scaled.rate; + req->best_parent_rate = scaled.best_parent_rate; + req->best_parent_hw = scaled.best_parent_hw; + if (div->fix_divider) + req->rate = div_u64(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 = to_amb_div(hw); + int val, mask; + + if (div->fix_divider) + rate *= div->fix_divider; + + if (!rate) { + /* Assert the reset/disable bit above the divider field. */ + val = BIT(div->width); + mask = clk_div_mask(div->width + 1); + } else { + val = divider_get_val(rate, parent_rate, NULL, + div->width, div->flags); + if (val < 0) + return val; + + mask = (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); + + /* + * Non-ONE_BASED dividers use bit 0 as a write-enable strobe. Skip the + * pulse when shift == 0 so we do not corrupt the divider field itself. + */ + if (!(div->flags & CLK_DIVIDER_ONE_BASED) && div->shift) { + 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 = { + .recalc_rate = amb_div_recalc_rate, + .determine_rate = amb_div_determine_rate, + .set_rate = 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 = {}; + int ret; + + div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->map = map; + div->offset = div_reg; + div->shift = div_shift; + div->width = div_width; + div->flags = div_flags; + div->fix_divider = fix_divider; + + init.name = name; + init.ops = &amb_div_ops; + init.parent_hws = &parent; + init.num_parents = 1; + + div->hw.init = &init; + + ret = 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 = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!mux || !div) + return ERR_PTR(-ENOMEM); + + mux->map = map; + mux->offset = desc->mux_reg; + mux->shift = desc->mux_shift; + mux->mask = desc->mux_mask; + + div->map = map; + div->offset = desc->div_reg; + div->shift = desc->div_shift; + div->width = desc->div_width; + div->flags = desc->div_flags; + div->fix_divider = 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/ccu_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 <linux/clk-provider.h> +#include <linux/regmap.h> + +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_pll.c new file mode 100644 index 000000000000..f4a90b4d179a --- /dev/null +++ b/drivers/clk/ambarella/ccu_pll.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Ambarella, Inc. + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/math64.h> +#include <linux/rational.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#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 = pll->soc_data; + u32 *reg = pll->reg_offset; + u32 pre_scaler = 1; + u32 ctrl_val, ctrl2_val = 0, frac_val; + u32 intp, sdiv, vcodiv, fsdiv; + u64 frac = 0, vco; + + if (reg[PRES_OFFSET]) { + regmap_read(pll->map, reg[PRES_OFFSET], &pre_scaler); + pre_scaler = (pre_scaler >> 4) + 1; + } + + regmap_read(pll->map, reg[CTRL_OFFSET], &ctrl_val); + intp = ((ctrl_val >> 24) & 0x7f) + 1; + sdiv = ((ctrl_val >> 12) & 0xf) + 1; + + if (soc_data->pll_version >= 2) { + vcodiv = (ctrl_val & soc_data->vcodiv_mask) == + soc_data->vcodiv_val ? 2 : 1; + fsdiv = (ctrl_val & soc_data->fsdiv_mask) == + soc_data->fsdiv_val ? 2 : 1; + } else { + regmap_read(pll->map, reg[CTRL2_OFFSET], &ctrl2_val); + vcodiv = (ctrl2_val & soc_data->vcodiv_mask) == + soc_data->vcodiv_val ? 2 : 1; + fsdiv = (ctrl2_val & soc_data->fsdiv_mask) == + soc_data->fsdiv_val ? 2 : 1; + } + + vco = (u64)parent_rate * vcodiv * fsdiv * intp * sdiv; + vco = div_u64(vco, pre_scaler); + + if (ctrl_val & CTRL_FRAC_MODE) { + regmap_read(pll->map, reg[FRAC_OFFSET], &frac_val); + frac = (u64)parent_rate * vcodiv * fsdiv * sdiv * frac_val; + frac = 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 = to_amb_pll(hw); + const struct amb_pll_soc_data *soc_data = pll->soc_data; + u32 *reg = pll->reg_offset; + u32 pre_scaler = 1, post_scaler = 1; + u32 ctrl_val, ctrl2_val = 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 = (pre_scaler >> 4) + 1; + } + + if (reg[POST_OFFSET]) { + regmap_read(pll->map, reg[POST_OFFSET], &post_scaler); + post_scaler = (post_scaler >> 4) + 1; + } + + if (ctrl_val & CTRL_BYPASS) + return parent_rate / pre_scaler / post_scaler; + + if (soc_data->pll_version >= 2) { + vcodiv = (ctrl_val & soc_data->vcodiv_mask) == + soc_data->vcodiv_val ? 2 : 1; + fsout = (ctrl_val & soc_data->fsout_mask) == + soc_data->fsout_val ? 2 : 1; + } else { + regmap_read(pll->map, reg[CTRL2_OFFSET], &ctrl2_val); + vcodiv = (ctrl2_val & soc_data->vcodiv_mask) == + soc_data->vcodiv_val ? 2 : 1; + fsout = (ctrl2_val & soc_data->fsout_mask) == + soc_data->fsout_val ? 2 : 1; + } + + sout = ((ctrl_val >> 16) & 0xf) + 1; + rate = amb_pll_calc_vco(pll, parent_rate); + + if (soc_data->pll_version >= 2) { + if (!(ctrl_val & CTRL_BYPASS_HSDIV)) + rate = div_u64(rate, vcodiv * fsout * sout); + } else { + if (!(ctrl2_val & CTRL2_BYPASS_HSDIV)) + rate = div_u64(rate, vcodiv * fsout * sout); + } + + rate = 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 = to_amb_pll(hw); + unsigned long half_refclk = req->best_parent_rate / 2; + + if (pll->frac_mode) + return 0; + + if (!half_refclk) + return -EINVAL; + + req->rate = 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 = pll->soc_data; + unsigned long max_numerator, max_denominator; + unsigned long intp_ul, sout_ul, rate_tmp; + u32 ctrl_val; + + *sdiv = 1; + + if (soc_data->pll_version >= 2) { + ctrl_val = 0; + *vcodiv = (ctrl_val & soc_data->vcodiv_mask) == + soc_data->vcodiv_val ? 2 : 1; + *fsdiv = (ctrl_val & soc_data->fsdiv_mask) == + soc_data->fsdiv_val ? 2 : 1; + *fsout = (ctrl_val & soc_data->fsout_mask) == + soc_data->fsout_val ? 2 : 1; + } else { + *vcodiv = (ctrl2_val & soc_data->vcodiv_mask) == + soc_data->vcodiv_val ? 2 : 1; + *fsdiv = (ctrl2_val & soc_data->fsdiv_mask) == + soc_data->fsdiv_val ? 2 : 1; + *fsout = (ctrl2_val & soc_data->fsout_mask) == + soc_data->fsout_val ? 2 : 1; + } + + if (rate < parent_rate) + return -EINVAL; + + max_numerator = soc_data->vco_max_mhz; + max_numerator = div_u64(max_numerator * 1000000ULL, parent_rate); + max_numerator = div_u64(max_numerator, *vcodiv * *fsdiv); + max_numerator = min(128UL, max_numerator); + if (!max_numerator) + return -EINVAL; + + max_denominator = AMB_PLL_MAX_SOUT; + rate_tmp = 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 = parent_rate / AMB_PLL_MAX_SOUT; + + /* Avoid an infinite loop when parent_rate < AMB_PLL_MAX_SOUT. */ + if (!resolution) + resolution = 1; + + if (rate_tmp <= resolution) + return -EINVAL; + + rate_tmp -= 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 *= 2; + sout_ul *= 2; + } + + if (intp_ul > max_numerator || sout_ul > max_denominator || + *sdiv > AMB_PLL_MAX_SDIV) + return -EINVAL; + + *intp = intp_ul; + *sout = 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 = to_amb_pll(hw); + const struct amb_pll_soc_data *soc_data = pll->soc_data; + u32 *reg = pll->reg_offset; + u32 ctrl_val, ctrl2_val = 0, ctrl3_val, frac_val = 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 |= CTRL_POWER_DOWN | CTRL_HALT_VCO; + amb_pll_write_enable(pll->map, reg[CTRL_OFFSET], ctrl_val); + return 0; + } + + rate *= pll->fix_divider; + + if (soc_data->ctrl2_val) + ctrl2_val = soc_data->ctrl2_val; + else + regmap_read(pll->map, reg[CTRL2_OFFSET], &ctrl2_val); + + ret = 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 = ((intp - 1) & 0x7f) << 24; + ctrl_val |= ((sdiv - 1) & 0xf) << 12; + ctrl_val |= ((sout - 1) & 0xf) << 16; + if (soc_data->pll_version >= 2) { + ctrl_val |= vcodiv == 2 ? soc_data->vcodiv_val : 0; + ctrl_val |= fsdiv == 2 ? soc_data->fsdiv_val : 0; + ctrl_val |= fsout == 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 = amb_pll_recalc_rate(hw, parent_rate) * pll->fix_divider; + rate_tmp = old_rate > rate ? 0 : rate - old_rate; + if (rate_tmp && pll->frac_mode) { + u64 dividend, divider; + + dividend = (u64)rate_tmp * sout * fsout; + dividend <<= 32; + divider = (u64)sdiv * fsdiv * parent_rate; + frac_val = DIV_ROUND_CLOSEST_ULL(dividend, divider); + regmap_write(pll->map, reg[FRAC_OFFSET], frac_val); + ctrl_val |= CTRL_FRAC_MODE; + } + + if (soc_data->pll_version >= 2) { + ctrl3_val = 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 = amb_pll_calc_vco(pll, parent_rate) / 1000000UL; + for (range = 0; range < ARRAY_SIZE(soc_data->vco_range); + range++) { + if (fvco_mhz > soc_data->vco_range[range]) + break; + } + range = ARRAY_SIZE(soc_data->vco_range) - range - 1; + + regmap_read(pll->map, reg[CTRL3_OFFSET], &ctrl3_val); + ctrl3_val &= ~CTRL3_VCO_RANGE_MASK; + ctrl3_val |= range << 1; + regmap_write(pll->map, reg[CTRL3_OFFSET], ctrl3_val); + + if (frac_val) { + ctrl_val |= CTRL_FORCE_RESET; + amb_pll_write_enable(pll->map, reg[CTRL_OFFSET], + ctrl_val); + } + + ctrl_val &= ~CTRL_FORCE_RESET; + amb_pll_write_enable(pll->map, reg[CTRL_OFFSET], ctrl_val); + } + + new_rate = amb_pll_recalc_rate(hw, parent_rate); + rate_tmp = 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 = { + .recalc_rate = amb_pll_recalc_rate, + .determine_rate = amb_pll_determine_rate, + .set_rate = 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 = {}; + const struct clk_hw *parent = desc->parent; + int ret; + + pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->map = map; + memcpy(pll->reg_offset, desc->reg_offset, sizeof(pll->reg_offset)); + pll->soc_data = desc->soc_data; + pll->fix_divider = 1; + pll->frac_mode = desc->frac_mode; + + init.name = desc->name; + init.ops = &amb_pll_ops; + init.flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL; + init.parent_hws = &parent; + init.num_parents = 1; + + pll->hw.init = &init; + + ret = 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_pll.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 <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +enum { + CTRL_OFFSET = 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 -- 2.34.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 4/6] clk: ambarella: add CV75 CCU driver 2026-08-13 9:56 ` [PATCH v3 4/6] clk: ambarella: add CV75 CCU driver Long Zhao via B4 Relay @ 2026-08-13 10:10 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-08-13 10:10 UTC (permalink / raw) To: Long Zhao; +Cc: devicetree, robh, conor+dt, linux-clk Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: - [High] VCO range iteration underflows and corrupts the PLL CTRL3 register if `fvco_mhz` evaluates to 0. - [High] `amb_pll_calc_params` omits the feedback and output dividers (`fsdiv` and `fsout`) when determining parameters, resulting in drastically incorrect clock frequencies. - [High] Division by zero kernel panic in `amb_pll_calc_params()` when the parent clock rate is zero. - [Medium] Setting the rate of a non-ONE_BASED divider to 0 permanently disables the clock due to an incorrect mask. - [Medium] `amb_pll_determine_rate` unnecessarily restricts integer PLL rates to multiples of `parent_rate / 2`. - [Low] Passing a 64-bit divisor to `DIV_ROUND_CLOSEST_ULL` causes a build failure on 32-bit architectures. -- --- Patch [4]: [PATCH v3 4/6] clk: ambarella: add CV75 CCU driver --- commit 434160130974a0a15aae2d546aa85876cbc1fe59 Author: Long Zhao <longzhao@ambarella.com> clk: ambarella: add CV75 CCU driver Add the Ambarella CV75 clock driver covering the core PLL and the UART0 / AHB / APB clocks needed for early console bring-up. > diff --git a/drivers/clk/ambarella/ccu_mux_div.c b/drivers/clk/ambarella/ccu_mux_div.c > new file mode 100644 > --- /dev/null > +++ b/drivers/clk/ambarella/ccu_mux_div.c [ ... ] > +static int amb_div_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ [ ... ] > + if (!rate) { > + /* Assert the reset/disable bit above the divider field. */ > + val = BIT(div->width); > + mask = clk_div_mask(div->width + 1); > + } else { > + val = divider_get_val(rate, parent_rate, NULL, > + div->width, div->flags); > + if (val < 0) > + return val; > + > + mask = (div->flags & CLK_DIVIDER_ONE_BASED) ? > + clk_div_mask(div->width + 1) : clk_div_mask(div->width); [Severity: Medium] When setting the rate to 0, the driver asserts the reset/disable bit above the divider field. When a non-zero rate is subsequently requested, this logic uses a mask that strictly covers only the divider field. Will this exclude the higher disable bit during the update, leaving the clock permanently disabled? > + } > + > + regmap_update_bits(div->map, div->offset, mask << div->shift, > + val << div->shift); [ ... ] > diff --git a/drivers/clk/ambarella/ccu_pll.c b/drivers/clk/ambarella/ccu_pll.c > new file mode 100644 > --- /dev/null > +++ b/drivers/clk/ambarella/ccu_pll.c [ ... ] > +static int amb_pll_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + struct amb_pll *pll = to_amb_pll(hw); > + unsigned long half_refclk = req->best_parent_rate / 2; > + > + if (pll->frac_mode) > + return 0; > + > + if (!half_refclk) > + return -EINVAL; > + > + req->rate = roundup(req->rate, half_refclk); [Severity: Medium] Does clamping the requested rate to a multiple of half_refclk artificially restrict the hardware's capabilities? Because the hardware sout divider supports ranges up to 16, it looks like it could natively support a wide and fine-grained selection of exact integer frequencies instead of being clamped this way. > + > + 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) > +{ [ ... ] > + max_numerator = soc_data->vco_max_mhz; > + max_numerator = div_u64(max_numerator * 1000000ULL, parent_rate); [Severity: High] In amb_pll_determine_rate(), if the PLL is in frac_mode, it returns 0 immediately without validating if half_refclk is zero: static int amb_pll_determine_rate(...) { ... if (pll->frac_mode) return 0; if (!half_refclk) return -EINVAL; ... } If a zero-rate parent clock reaches amb_pll_calc_params(), will this division by zero trigger a kernel panic? > + max_numerator = div_u64(max_numerator, *vcodiv * *fsdiv); > + max_numerator = min(128UL, max_numerator); > + if (!max_numerator) > + return -EINVAL; > + > + max_denominator = AMB_PLL_MAX_SOUT; > + rate_tmp = rate; > + rational_best_approximation(rate_tmp, parent_rate, max_numerator, > + max_denominator, &intp_ul, &sout_ul); [Severity: High] The hardware frequency depends on fsdiv and fsout, but this call requests an approximation using just rate and parent_rate. If the hardware is left in a state where fsdiv and fsout are not identical, will the resulting frequency be scaled incorrectly and bypass the refinement loop? > + > + while (parent_rate * *fsdiv * intp_ul * *sdiv / *fsout / sout_ul > [ ... ] > +static int amb_pll_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ [ ... ] > + old_rate = amb_pll_recalc_rate(hw, parent_rate) * pll->fix_divider; > + rate_tmp = old_rate > rate ? 0 : rate - old_rate; > + if (rate_tmp && pll->frac_mode) { > + u64 dividend, divider; > + > + dividend = (u64)rate_tmp * sout * fsout; > + dividend <<= 32; > + divider = (u64)sdiv * fsdiv * parent_rate; > + frac_val = DIV_ROUND_CLOSEST_ULL(dividend, divider); [Severity: Low] The divider variable evaluates to a 64-bit integer, but DIV_ROUND_CLOSEST_ULL() invokes do_div() which expects a 32-bit divisor on 32-bit architectures. Will this trigger a __div64_32 link failure under COMPILE_TEST? > + regmap_write(pll->map, reg[FRAC_OFFSET], frac_val); > + ctrl_val |= CTRL_FRAC_MODE; > + } > + > + if (soc_data->pll_version >= 2) { [ ... ] > + } else { > + u32 fvco_mhz, range; > + > + fvco_mhz = amb_pll_calc_vco(pll, parent_rate) / 1000000UL; > + for (range = 0; range < ARRAY_SIZE(soc_data->vco_range); > + range++) { > + if (fvco_mhz > soc_data->vco_range[range]) > + break; > + } > + range = ARRAY_SIZE(soc_data->vco_range) - range - 1; > + > + regmap_read(pll->map, reg[CTRL3_OFFSET], &ctrl3_val); > + ctrl3_val &= ~CTRL3_VCO_RANGE_MASK; > + ctrl3_val |= range << 1; [Severity: High] If the PLL parent rate is exceptionally low (e.g., from a dummy clock), fvco_mhz can evaluate to 0. Since 0 is not strictly greater than any range minimum, the loop finishes at 4. Will the subsequent calculation 4 - 4 - 1 underflow to 0xFFFFFFFF and corrupt the CTRL3 register bits? > + regmap_write(pll->map, reg[CTRL3_OFFSET], ctrl3_val); [ ... ] -- Sashiko AI review · https://sashiko.dev/#/patchset/20260813-cv75-v3-a-v3-0-cfdd20b1b4dd@ambarella.com?part=4 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 5/6] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT 2026-08-13 9:56 [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT Long Zhao via B4 Relay ` (3 preceding siblings ...) 2026-08-13 9:56 ` [PATCH v3 4/6] clk: ambarella: add CV75 CCU driver Long Zhao via B4 Relay @ 2026-08-13 9:56 ` Long Zhao via B4 Relay 2026-08-13 10:07 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 6/6] MAINTAINERS: add ARM/AMBARELLA SoC support Long Zhao via B4 Relay 5 siblings, 1 reply; 11+ messages in thread From: Long Zhao via B4 Relay @ 2026-08-13 9:56 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michael Turquette, Stephen Boyd, Catalin Marinas, Will Deacon, Arnd Bergmann, Alexandre Belloni, Linus Walleij Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, soc, Long Zhao From: Long Zhao <longzhao@ambarella.com> Enable ARCH_AMBARELLA and add a minimal CV75 SoC / EVK device tree for console bring-up with the RCT clocks, UART, pinctrl and GPIO banks. Signed-off-by: Long Zhao <longzhao@ambarella.com> --- 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 | 178 +++++++++++++++++++++++++++++ arch/arm64/configs/defconfig | 1 + 6 files changed, 222 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. +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 += airoha subdir-y += allwinner subdir-y += altera subdir-y += amazon +subdir-y += ambarella subdir-y += amd subdir-y += amlogic subdir-y += apm diff --git a/arch/arm64/boot/dts/ambarella/Makefile b/arch/arm64/boot/dts/ambarella/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) += cv75-evk.dtb diff --git a/arch/arm64/boot/dts/ambarella/cv75-evk.dts b/arch/arm64/boot/dts/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 = "Ambarella CV75 EVK Board"; + compatible = "ambarella,cv75-evk", "ambarella,cv75"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = &uart0; + }; + + memory@200000 { + device_type = "memory"; + reg = <0x0 0x00200000 0x0 0x07e00000>; + }; +}; + +&uart0 { + status = "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..994eac6728ce --- /dev/null +++ b/arch/arm64/boot/dts/ambarella/cv75.dtsi @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Ambarella CV75 SoC + * + * Boot peripheral: UART0 console. + * Clocks: a single ambarella,cv75-rct provider. + */ + +#include <dt-bindings/clock/ambarella,cv75-rct.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +/ { + compatible = "ambarella,cv75"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a76"; + device_type = "cpu"; + reg = <0x000>; + enable-method = "psci"; + }; + + cpu@1 { + compatible = "arm,cortex-a76"; + device_type = "cpu"; + reg = <0x100>; + enable-method = "psci"; + }; + }; + + osc: oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "osc"; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + ahb@ffe0000000 { + compatible = "simple-bus"; + reg = <0xff 0xe0000000 0x0 0x01000000>; + ranges = <0xe0000000 0xff 0xe0000000 0x01000000>; + #address-cells = <1>; + #size-cells = <1>; + + s_scratchpad_syscon: scratchpad@e002e000 { + compatible = "ambarella,cv75-sec-scratchpad", "syscon"; + reg = <0xe002e000 0x1000>; + }; + }; + + apb@ffe4000000 { + compatible = "simple-bus"; + reg = <0xff 0xe4000000 0x0 0x01000000>; + ranges = <0xe4000000 0xff 0xe4000000 0x01000000>; + #address-cells = <1>; + #size-cells = <1>; + + uart0: serial@e4000000 { + compatible = "ambarella,cv75-uart", + "snps,dw-apb-uart"; + reg = <0xe4000000 0x1000>; + clocks = <&rct CV75_GCLK_UART0>; + interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + reg-io-width = <4>; + reg-shift = <2>; + + status = "disabled"; + }; + + pinctrl: pinctrl@e4010000 { + compatible = "ambarella,cv75-pinctrl"; + reg = <0xe4010000 0x1000>; + ambarella,drive-strength-syscon = <&rct>; + ambarella,pull-syscon = <&s_scratchpad_syscon>; + + uart0_pins: uart0-pins { + function = "uart0"; + groups = "uart0"; + }; + }; + + gpio0: gpio@e4013000 { + compatible = "ambarella,cv75-gpio", "arm,primecell"; + reg = <0xe4013000 0x1000>; + clocks = <&rct CV75_GCLK_APB>; + clock-names = "apb_pclk"; + interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 0 32>; + interrupt-controller; + #interrupt-cells = <2>; + arm,primecell-periphid = <0x00000061>; + }; + + gpio1: gpio@e4014000 { + compatible = "ambarella,cv75-gpio", "arm,primecell"; + reg = <0xe4014000 0x1000>; + clocks = <&rct CV75_GCLK_APB>; + clock-names = "apb_pclk"; + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 32 32>; + interrupt-controller; + #interrupt-cells = <2>; + arm,primecell-periphid = <0x00000061>; + }; + + gpio2: gpio@e4015000 { + compatible = "ambarella,cv75-gpio", "arm,primecell"; + reg = <0xe4015000 0x1000>; + clocks = <&rct CV75_GCLK_APB>; + clock-names = "apb_pclk"; + interrupts = <GIC_SPI 165 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 64 32>; + interrupt-controller; + #interrupt-cells = <2>; + arm,primecell-periphid = <0x00000061>; + }; + }; + + rct: clock-controller@ffed080000 { + compatible = "ambarella,cv75-rct", "syscon"; + reg = <0xff 0xed080000 0x0 0x1000>; + clocks = <&osc>; + clock-names = "osc"; + #clock-cells = <1>; + assigned-clocks = <&rct CV75_GCLK_UART0>; + assigned-clock-parents = <&osc>; + assigned-clock-rates = <24000000>; + }; + + gic: interrupt-controller@fff0101000 { + compatible = "arm,gic-400"; + reg = <0xff 0xf0101000 0x0 0x1000>, + <0xff 0xf0102000 0x0 0x2000>, + <0xff 0xf0104000 0x0 0x2000>, + <0xff 0xf0106000 0x0 0x2000>; + interrupt-controller; + #interrupt-cells = <3>; + #address-cells = <0>; + #size-cells = <1>; + interrupts = <GIC_PPI 9 + (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 12 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; + }; +}; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index ad3bd3d9507b..454f07a6a6a6 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -36,6 +36,7 @@ CONFIG_CRASH_DUMP=y CONFIG_ARCH_ACTIONS=y CONFIG_ARCH_AIROHA=y CONFIG_ARCH_SUNXI=y +CONFIG_ARCH_AMBARELLA=y CONFIG_ARCH_ALPINE=y CONFIG_ARCH_APPLE=y CONFIG_ARCH_ARTPEC=y -- 2.34.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 5/6] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT 2026-08-13 9:56 ` [PATCH v3 5/6] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay @ 2026-08-13 10:07 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-08-13 10:07 UTC (permalink / raw) To: Long Zhao; +Cc: robh, linux-clk, devicetree, conor+dt Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] ARCH_AMBARELLA selects non-existent and architecture-mismatched Kconfig symbols, violating Kconfig dependency rules. - [Low] The GIC interrupt controller node specifies `#size-cells = <1>` while `#address-cells = <0>`, which is structurally invalid in Devicetree. -- --- Patch [5]: [PATCH v3 5/6] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT --- commit cf8659acda73b48006f17101c5f47d5bf1d510a1 Author: Long Zhao <longzhao@ambarella.com> arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Enable ARCH_AMBARELLA and add a minimal CV75 SoC / EVK device tree for console bring-up with the RCT clocks, UART, pinctrl and GPIO banks. > diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms > index d2acfac730037..166430ddb3c93 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. > > +config ARCH_AMBARELLA > + bool "Ambarella SoC family" > + select ARM_PSCI > + select HAVE_ARM_ARCH_TIMER > + select PINCTRL > + select PINCTRL_AMB [Severity: High] Does PINCTRL_AMB exist in the kernel tree? Selecting a non-existent config symbol leaves intended features silently disabled when a user enables the SoC config. Also, are ARM_PSCI and HAVE_ARM_ARCH_TIMER the correct symbols for arm64? These appear to be 32-bit ARM-specific symbols. On arm64, it seems PSCI and the architected timer are managed by ARM_PSCI_FW and ARM_ARCH_TIMER, which are already selected by default. > + help > + This enables support for the ARM64 based Ambarella SoCs like CV75. > + [ ... ] > diff --git a/arch/arm64/boot/dts/ambarella/cv75.dtsi b/arch/arm64/boot/dts/ambarella/cv75.dtsi > new file mode 100644 > index 0000000000000..994eac6728ce8 > --- /dev/null > +++ b/arch/arm64/boot/dts/ambarella/cv75.dtsi [ ... ] > + gic: interrupt-controller@fff0101000 { > + compatible = "arm,gic-400"; > + reg = <0xff 0xf0101000 0x0 0x1000>, > + <0xff 0xf0102000 0x0 0x2000>, > + <0xff 0xf0104000 0x0 0x2000>, > + <0xff 0xf0106000 0x0 0x2000>; > + interrupt-controller; > + #interrupt-cells = <3>; > + #address-cells = <0>; > + #size-cells = <1>; [Severity: Low] Is this #size-cells value correct given that #address-cells is 0? When a node has an #address-cells value of 0, it does not define an address space for its children, and its #size-cells property should also be 0. While this will be ignored at runtime since there are no children, it will trigger a dtc compiler warning for being structurally invalid. > + interrupts = <GIC_PPI 9 > + (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>; > + }; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260813-cv75-v3-a-v3-0-cfdd20b1b4dd@ambarella.com?part=5 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 6/6] MAINTAINERS: add ARM/AMBARELLA SoC support 2026-08-13 9:56 [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT Long Zhao via B4 Relay ` (4 preceding siblings ...) 2026-08-13 9:56 ` [PATCH v3 5/6] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay @ 2026-08-13 9:56 ` Long Zhao via B4 Relay 5 siblings, 0 replies; 11+ messages in thread From: Long Zhao via B4 Relay @ 2026-08-13 9:56 UTC (permalink / raw) To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Michael Turquette, Stephen Boyd, Catalin Marinas, Will Deacon, Arnd Bergmann, Alexandre Belloni, Linus Walleij Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, soc, Long Zhao From: Long Zhao <longzhao@ambarella.com> Add a maintainer entry covering Ambarella CV75 bindings, DTS and related drivers. Signed-off-by: Long Zhao <longzhao@ambarella.com> --- MAINTAINERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 803b60ad628f..59e5b32d7a02 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2502,6 +2502,20 @@ F: drivers/clocksource/asm9260_timer.c F: drivers/rtc/rtc-asm9260.c F: drivers/watchdog/asm9260_wdt.c +ARM/AMBARELLA SOC SUPPORT +M: Long Zhao <longzhao@ambarella.com> +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: arch/arm64/boot/dts/ambarella/ +F: drivers/clk/ambarella/ +F: drivers/pinctrl/pinctrl-ambarella* +F: include/dt-bindings/clock/ambarella,cv75-rct.h +N: ambarella + ARM/AMD PENSANDO ARM64 ARCHITECTURE M: Brad Larson <blarson@amd.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -- 2.34.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-08-13 10:10 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-13 9:56 [PATCH v3 0/6] Ambarella CV75: bindings, RCT clocks, and DT Long Zhao via B4 Relay 2026-08-13 9:56 ` [PATCH v3 1/6] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay 2026-08-13 9:56 ` [PATCH v3 2/6] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Long Zhao via B4 Relay 2026-08-13 10:03 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 3/6] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay 2026-08-13 10:02 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 4/6] clk: ambarella: add CV75 CCU driver Long Zhao via B4 Relay 2026-08-13 10:10 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 5/6] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay 2026-08-13 10:07 ` sashiko-bot 2026-08-13 9:56 ` [PATCH v3 6/6] MAINTAINERS: add ARM/AMBARELLA SoC support Long Zhao via B4 Relay
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).