* [PATCH 0/2] clk: add Ambarella CV75 RCT clock controller
@ 2026-10-09 11:00 Long Zhao via B4 Relay
2026-10-09 11:00 ` [PATCH 1/2] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay
2026-10-09 11:00 ` [PATCH 2/2] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
0 siblings, 2 replies; 5+ messages in thread
From: Long Zhao via B4 Relay @ 2026-10-09 11:00 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, linux-clk
Cc: Jerome Brunet, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
devicetree, Long Zhao, Long Zhao, linux-kernel
Split from the combined CV75 bring-up series.
CLK_AMBARELLA_CV75 depends on ARCH_AMBARELLA || COMPILE_TEST, so the
driver still builds with COMPILE_TEST when the SoC symbol is absent.
The binding uses the ambarella vendor prefix, which is added by the
SoC series and is not in mainline yet; dt_binding_check needs that
prefix. The MAINTAINERS entry for these files is in the SoC series.
Changes since the patches in the combined v9:
- drop the redundant linux/math.h include (Andy)
- use for (unsigned int i = 0; ...) (Andy)
- initialize the spinlock after the allocation succeeds (Andy)
- group the ctrl, frac and ctrl2 offsets in struct cv75_pll_regs (Andy)
Link: https://patch.msgid.link/20261001-cv75-v5-v9-0-486cc289a4b8@ambarella.com
To: Michael Turquette <mturquette@baylibre.com>
To: Stephen Boyd <sboyd@kernel.org>
To: linux-clk@vger.kernel.org
Cc: Jerome Brunet <jbrunet@baylibre.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: Long Zhao <longzhao@ambarella.com>
Cc: Long Zhao <zl020895@163.com>
Cc: linux-kernel@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
Long Zhao (2):
dt-bindings: clock: add Ambarella CV75 RCT
clk: ambarella: add CV75 RCT clock controller
.../bindings/clock/ambarella,cv75-rct.yaml | 64 ++++
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/ambarella/Kconfig | 9 +
drivers/clk/ambarella/Makefile | 3 +
drivers/clk/ambarella/clk-cv75.c | 361 +++++++++++++++++++++
include/dt-bindings/clock/ambarella,cv75-rct.h | 14 +
7 files changed, 453 insertions(+)
---
base-commit: 34cf6dafc47441dfb6b356a095b89c3585a93714
change-id: 20261007-cv75-clk-f74e1494576a
Best regards,
--
Long Zhao <longzhao@ambarella.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] dt-bindings: clock: add Ambarella CV75 RCT
2026-10-09 11:00 [PATCH 0/2] clk: add Ambarella CV75 RCT clock controller Long Zhao via B4 Relay
@ 2026-10-09 11:00 ` Long Zhao via B4 Relay
2026-10-09 11:32 ` Krzysztof Kozlowski
2026-10-09 11:00 ` [PATCH 2/2] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
1 sibling, 1 reply; 5+ messages in thread
From: Long Zhao via B4 Relay @ 2026-10-09 11:00 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, linux-clk
Cc: Jerome Brunet, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
devicetree, Long Zhao, Long Zhao, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Document the CV75 RCT clock controller and the clock ID header used by
DT consumers.
Document #reset-cells for the RCT reset-provider role. Soft-reset line
IDs and the reset provider driver are deferred; this series has no
resets= consumers.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
.../bindings/clock/ambarella,cv75-rct.yaml | 64 ++++++++++++++++++++++
include/dt-bindings/clock/ambarella,cv75-rct.h | 14 +++++
2 files changed, 78 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..be89bd75f9e2
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml
@@ -0,0 +1,64 @@
+# 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, dividers and soft-reset controls 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
+
+ "#reset-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - "#clock-cells"
+ - "#reset-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ clock-controller@ed080000 {
+ compatible = "ambarella,cv75-rct", "syscon";
+ reg = <0xed080000 0x1000>;
+ clocks = <&osc>;
+ clock-names = "osc";
+ #clock-cells = <1>;
+ #reset-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..b3e965b4ff2e
--- /dev/null
+++ b/include/dt-bindings/clock/ambarella,cv75-rct.h
@@ -0,0 +1,14 @@
+/* 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
+
+#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] 5+ messages in thread
* [PATCH 2/2] clk: ambarella: add CV75 RCT clock controller
2026-10-09 11:00 [PATCH 0/2] clk: add Ambarella CV75 RCT clock controller Long Zhao via B4 Relay
2026-10-09 11:00 ` [PATCH 1/2] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay
@ 2026-10-09 11:00 ` Long Zhao via B4 Relay
2026-10-09 17:02 ` Brian Masney
1 sibling, 1 reply; 5+ messages in thread
From: Long Zhao via B4 Relay @ 2026-10-09 11:00 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, linux-clk
Cc: Jerome Brunet, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
devicetree, Long Zhao, Long Zhao, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Add a table-driven CCF driver for the CV75 root clock tree. Register
the core PLL, AHB/APB fixed factors and UART0 composite clock, with
osc supplied via clk_parent_data.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/ambarella/Kconfig | 9 +
drivers/clk/ambarella/Makefile | 3 +
drivers/clk/ambarella/clk-cv75.c | 361 +++++++++++++++++++++++++++++++++++++++
5 files changed, 375 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..720eebdc5c9c
--- /dev/null
+++ b/drivers/clk/ambarella/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config CLK_AMBARELLA_CV75
+ bool "Ambarella CV75 RCT clock controller"
+ depends on ARCH_AMBARELLA || COMPILE_TEST
+ default ARCH_AMBARELLA
+ help
+ Say Y to enable the Ambarella CV75 RCT clock controller for
+ early bring-up clocks (core PLL, AHB, APB, UART0).
diff --git a/drivers/clk/ambarella/Makefile b/drivers/clk/ambarella/Makefile
new file mode 100644
index 000000000000..29555e452441
--- /dev/null
+++ b/drivers/clk/ambarella/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_CLK_AMBARELLA_CV75) += clk-cv75.o
diff --git a/drivers/clk/ambarella/clk-cv75.c b/drivers/clk/ambarella/clk-cv75.c
new file mode 100644
index 000000000000..7135402a36ec
--- /dev/null
+++ b/drivers/clk/ambarella/clk-cv75.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ambarella CV75 RCT clock controller
+ *
+ * Copyright (C) 2026 Ambarella, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/ambarella,cv75-rct.h>
+
+#define CV75_NUM_CLKS (CV75_GCLK_APB + 1)
+
+#define PLL_CTRL_BYPASS BIT(2)
+#define PLL_CTRL_FRAC_MODE BIT(3)
+#define PLL_CTRL_FORCE_RESET BIT(4)
+#define PLL_CTRL_POWER_DOWN BIT(5)
+#define PLL_CTRL_HALT_VCO BIT(6)
+#define PLL_CTRL_SDIV GENMASK(15, 12)
+#define PLL_CTRL_SOUT GENMASK(19, 16)
+#define PLL_CTRL_INTP GENMASK(30, 24)
+
+#define PLL_CTRL2_VCODIV_DIV2 BIT(8)
+#define PLL_CTRL2_FSDIV_DIV2 BIT(9)
+#define PLL_CTRL2_FSOUT_DIV2 BIT(11)
+#define PLL_CTRL2_BYPASS_HSDIV BIT(12)
+
+struct cv75_pll_regs {
+ u32 ctrl;
+ u32 frac;
+ u32 ctrl2;
+};
+
+struct cv75_pll_hw {
+ struct clk_hw hw;
+ struct cv75_pll_regs regs;
+};
+
+struct cv75_clk {
+ void __iomem *base;
+ spinlock_t lock;
+ struct cv75_pll_hw core;
+ struct cv75_pll_hw enet;
+ struct cv75_pll_hw sd;
+};
+
+struct cv75_pll {
+ const char *name;
+ struct cv75_pll_hw *hw;
+ unsigned long flags;
+ struct cv75_pll_regs regs;
+ int id;
+};
+
+struct cv75_fixed_factor {
+ const char *name;
+ u32 id;
+ struct u16_fract ratio;
+};
+
+struct cv75_composite {
+ const char *name;
+ const struct clk_parent_data *parents;
+ unsigned long flags;
+ u32 id;
+ u16 mux_reg;
+ u16 div_reg;
+ u8 num_parents;
+ u8 mux_shift;
+ u8 mux_width;
+ u8 div_shift;
+ u8 div_width;
+};
+
+struct cv75_composite_hw {
+ struct clk_mux mux;
+ struct clk_divider div;
+ struct clk_gate gate;
+};
+
+static struct cv75_clk cv75_ccu;
+
+#define CV75_PLL(_id, _name, _ctrl, _frac, _ctrl2, _flags, _hw) { \
+ .name = _name, \
+ .hw = _hw, \
+ .flags = _flags, \
+ .regs = { \
+ .ctrl = _ctrl, \
+ .frac = _frac, \
+ .ctrl2 = _ctrl2, \
+ }, \
+ .id = _id, \
+}
+
+static const struct cv75_pll cv75_plls[] = {
+ CV75_PLL(CV75_GCLK_CORE, "core", 0x000, 0x004, 0x100,
+ CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL, &cv75_ccu.core),
+ CV75_PLL(-1, "pll_enet", 0x520, 0x524, 0x528,
+ CLK_GET_RATE_NOCACHE, &cv75_ccu.enet),
+ CV75_PLL(-1, "pll_sd", 0x4ac, 0x4b0, 0x4b4,
+ CLK_GET_RATE_NOCACHE, &cv75_ccu.sd),
+};
+
+static const struct cv75_fixed_factor cv75_fixed_factors[] = {
+ { .id = CV75_GCLK_AHB, .name = "ahb", .ratio = { 1, 2 } },
+ { .id = CV75_GCLK_APB, .name = "apb", .ratio = { 1, 4 } },
+};
+
+static const struct clk_parent_data cv75_uart_parents[] = {
+ { .fw_name = "osc" },
+ { .hw = &cv75_ccu.core.hw },
+ { .hw = &cv75_ccu.enet.hw },
+ { .hw = &cv75_ccu.sd.hw },
+};
+
+#define CV75_COMPOSITE(_id, _name, _parents, _mreg, _mshift, _mwidth, \
+ _dreg, _dshift, _dwidth, _flags) \
+{ \
+ .id = _id, \
+ .name = _name, \
+ .parents = _parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ .mux_reg = _mreg, \
+ .mux_shift = _mshift, \
+ .mux_width = _mwidth, \
+ .div_reg = _dreg, \
+ .div_shift = _dshift, \
+ .div_width = _dwidth, \
+ .flags = _flags, \
+}
+
+static const struct cv75_composite cv75_composites[] = {
+ CV75_COMPOSITE(CV75_GCLK_UART0, "uart0", cv75_uart_parents,
+ 0x1c8, 0, 2, 0x038, 0, 24,
+ CLK_SET_RATE_NO_REPARENT),
+};
+
+#define to_cv75_pll(_hw) container_of(_hw, struct cv75_pll_hw, hw)
+
+static unsigned long cv75_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv75_pll_hw *pll = to_cv75_pll(hw);
+ u32 ctrl, ctrl2, frac_val;
+ u32 intp, sdiv, sout;
+ u32 vcodiv, fsdiv, fsout;
+ u64 vco, frac = 0;
+
+ ctrl = readl_relaxed(cv75_ccu.base + pll->regs.ctrl);
+ if (ctrl & (PLL_CTRL_POWER_DOWN | PLL_CTRL_HALT_VCO | PLL_CTRL_FORCE_RESET))
+ return 0;
+
+ if (ctrl & PLL_CTRL_BYPASS)
+ return parent_rate;
+
+ ctrl2 = readl_relaxed(cv75_ccu.base + pll->regs.ctrl2);
+ intp = FIELD_GET(PLL_CTRL_INTP, ctrl) + 1;
+ sdiv = FIELD_GET(PLL_CTRL_SDIV, ctrl) + 1;
+ sout = FIELD_GET(PLL_CTRL_SOUT, ctrl) + 1;
+ vcodiv = (ctrl2 & PLL_CTRL2_VCODIV_DIV2) ? 2 : 1;
+ fsdiv = (ctrl2 & PLL_CTRL2_FSDIV_DIV2) ? 2 : 1;
+ fsout = (ctrl2 & PLL_CTRL2_FSOUT_DIV2) ? 2 : 1;
+
+ vco = (u64)parent_rate * vcodiv * fsdiv * intp * sdiv;
+ if (ctrl & PLL_CTRL_FRAC_MODE) {
+ frac_val = readl_relaxed(cv75_ccu.base + pll->regs.frac);
+ frac = mul_u64_u32_shr((u64)parent_rate * vcodiv * fsdiv * sdiv,
+ frac_val, 32);
+ vco += frac;
+ }
+
+ if (!(ctrl2 & PLL_CTRL2_BYPASS_HSDIV))
+ vco = div_u64(vco, (u64)vcodiv * fsout * sout);
+
+ return vco;
+}
+
+static const struct clk_ops cv75_pll_ops = {
+ .recalc_rate = cv75_pll_recalc_rate,
+};
+
+static int cv75_register_plls(struct device *dev,
+ struct clk_hw_onecell_data *data)
+{
+ struct clk_parent_data parent_data = { .fw_name = "osc" };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(cv75_plls); i++) {
+ const struct cv75_pll *desc = &cv75_plls[i];
+ struct cv75_pll_hw *pll = desc->hw;
+ struct clk_init_data init = {};
+ int ret;
+
+ pll->regs = desc->regs;
+
+ init.name = desc->name;
+ init.ops = &cv75_pll_ops;
+ init.parent_data = &parent_data;
+ init.num_parents = 1;
+ init.flags = desc->flags;
+ pll->hw.init = &init;
+
+ ret = devm_clk_hw_register(dev, &pll->hw);
+ if (ret)
+ return ret;
+
+ if (desc->id >= 0)
+ data->hws[desc->id] = &pll->hw;
+ }
+
+ return 0;
+}
+
+static int cv75_register_fixed_factors(struct device *dev,
+ struct clk_hw_onecell_data *data)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(cv75_fixed_factors); i++) {
+ const struct cv75_fixed_factor *f = &cv75_fixed_factors[i];
+ struct clk_hw *hw;
+
+ hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, f->name,
+ &cv75_ccu.core.hw, 0,
+ f->ratio.numerator,
+ f->ratio.denominator);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ data->hws[f->id] = hw;
+ }
+
+ return 0;
+}
+
+static struct clk_hw *cv75_register_composite(struct device *dev,
+ const struct cv75_composite *c)
+{
+ const struct clk_ops *mux_ops = NULL, *div_ops = NULL, *gate_ops = NULL;
+ struct clk_hw *mux_hw = NULL, *div_hw = NULL, *gate_hw = NULL;
+ struct cv75_composite_hw *comp;
+
+ comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return ERR_PTR(-ENOMEM);
+
+ if (c->mux_width) {
+ comp->mux.reg = cv75_ccu.base + c->mux_reg;
+ comp->mux.shift = c->mux_shift;
+ comp->mux.mask = GENMASK(c->mux_width - 1, 0);
+ comp->mux.lock = &cv75_ccu.lock;
+ mux_hw = &comp->mux.hw;
+ mux_ops = &clk_mux_ops;
+ }
+
+ if (c->div_width) {
+ comp->div.reg = cv75_ccu.base + c->div_reg;
+ comp->div.shift = c->div_shift;
+ comp->div.width = c->div_width;
+ comp->div.flags = CLK_DIVIDER_ONE_BASED;
+ comp->div.lock = &cv75_ccu.lock;
+ div_hw = &comp->div.hw;
+ div_ops = &clk_divider_ops;
+
+ comp->gate.reg = cv75_ccu.base + c->div_reg;
+ comp->gate.bit_idx = c->div_shift + c->div_width;
+ comp->gate.flags = CLK_GATE_SET_TO_DISABLE;
+ comp->gate.lock = &cv75_ccu.lock;
+ gate_hw = &comp->gate.hw;
+ gate_ops = &clk_gate_ops;
+ }
+
+ return devm_clk_hw_register_composite_pdata(dev, c->name, c->parents,
+ c->num_parents,
+ mux_hw, mux_ops,
+ div_hw, div_ops,
+ gate_hw, gate_ops, c->flags);
+}
+
+static int cv75_register_composites(struct device *dev,
+ struct clk_hw_onecell_data *data)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(cv75_composites); i++) {
+ const struct cv75_composite *c = &cv75_composites[i];
+ struct clk_hw *hw;
+
+ hw = cv75_register_composite(dev, c);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ data->hws[c->id] = hw;
+ }
+
+ return 0;
+}
+
+static int cv75_rct_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ cv75_ccu.base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cv75_ccu.base))
+ return PTR_ERR(cv75_ccu.base);
+
+ data = devm_kzalloc(dev, struct_size(data, hws, CV75_NUM_CLKS), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->num = CV75_NUM_CLKS;
+
+ spin_lock_init(&cv75_ccu.lock);
+
+ ret = cv75_register_plls(dev, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register plls\n");
+
+ ret = cv75_register_fixed_factors(dev, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register fixed factors\n");
+
+ ret = cv75_register_composites(dev, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register composites\n");
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add clock provider\n");
+
+ return 0;
+}
+
+static const struct of_device_id cv75_rct_match[] = {
+ { .compatible = "ambarella,cv75-rct" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cv75_rct_match);
+
+static struct platform_driver cv75_rct_driver = {
+ .probe = cv75_rct_probe,
+ .driver = {
+ .name = "ambarella-cv75-rct",
+ .of_match_table = cv75_rct_match,
+ },
+};
+module_platform_driver(cv75_rct_driver);
+
+MODULE_AUTHOR("Cao Rongrong <rrcao@ambarella.com>");
+MODULE_DESCRIPTION("Ambarella CV75 RCT clock controller");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] dt-bindings: clock: add Ambarella CV75 RCT
2026-10-09 11:00 ` [PATCH 1/2] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay
@ 2026-10-09 11:32 ` Krzysztof Kozlowski
0 siblings, 0 replies; 5+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-09 11:32 UTC (permalink / raw)
To: longzhao, Michael Turquette, Stephen Boyd, linux-clk
Cc: Jerome Brunet, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Krzysztof Kozlowski, Conor Dooley, devicetree, Long Zhao,
linux-kernel
On 09/10/2026 13:00, Long Zhao via B4 Relay wrote:
> From: Long Zhao <longzhao@ambarella.com>
>
> Document the CV75 RCT clock controller and the clock ID header used by
> DT consumers.
>
> Document #reset-cells for the RCT reset-provider role. Soft-reset line
> IDs and the reset provider driver are deferred; this series has no
> resets= consumers.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Long Zhao <longzhao@ambarella.com>
> ---
> .../bindings/clock/ambarella,cv75-rct.yaml | 64 ++++++++++++++++++++++
> include/dt-bindings/clock/ambarella,cv75-rct.h | 14 +++++
> 2 files changed, 78 insertions(+)
This should not be split from original patchset.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] clk: ambarella: add CV75 RCT clock controller
2026-10-09 11:00 ` [PATCH 2/2] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
@ 2026-10-09 17:02 ` Brian Masney
0 siblings, 0 replies; 5+ messages in thread
From: Brian Masney @ 2026-10-09 17:02 UTC (permalink / raw)
To: Long Zhao
Cc: Michael Turquette, Stephen Boyd, linux-clk, Jerome Brunet,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
devicetree, Long Zhao, linux-kernel
Hi Long,
> Add a table-driven CCF driver for the CV75 root clock tree. Register
> the core PLL, AHB/APB fixed factors and UART0 composite clock, with
> osc supplied via clk_parent_data.
>
> Signed-off-by: Long Zhao <longzhao@ambarella.com>
The dt binding mentions a reset however I don't see any reference to it
in the driver.
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 37a8e35d4178..6bfa2c8fffe1 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -510,6 +510,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/anlogic/Kconfig"
> source "drivers/clk/aspeed/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 52d138d4ffc8..99377a13ebc5 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -125,6 +125,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 += anlogic/
> obj-y += aspeed/
> diff --git a/drivers/clk/ambarella/Kconfig b/drivers/clk/ambarella/Kconfig
> new file mode 100644
> index 000000000000..720eebdc5c9c
> --- /dev/null
> +++ b/drivers/clk/ambarella/Kconfig
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config CLK_AMBARELLA_CV75
> + bool "Ambarella CV75 RCT clock controller"
> + depends on ARCH_AMBARELLA || COMPILE_TEST
> + default ARCH_AMBARELLA
> + help
> + Say Y to enable the Ambarella CV75 RCT clock controller for
> + early bring-up clocks (core PLL, AHB, APB, UART0).
> diff --git a/drivers/clk/ambarella/Makefile b/drivers/clk/ambarella/Makefile
> new file mode 100644
> index 000000000000..29555e452441
> --- /dev/null
> +++ b/drivers/clk/ambarella/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +obj-$(CONFIG_CLK_AMBARELLA_CV75) += clk-cv75.o
> diff --git a/drivers/clk/ambarella/clk-cv75.c b/drivers/clk/ambarella/clk-cv75.c
> new file mode 100644
> index 000000000000..7135402a36ec
> --- /dev/null
> +++ b/drivers/clk/ambarella/clk-cv75.c
> @@ -0,0 +1,361 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Ambarella CV75 RCT clock controller
> + *
> + * Copyright (C) 2026 Ambarella, Inc.
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/clk-provider.h>
> +#include <linux/container_of.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/math64.h>
> +#include <linux/module.h>
> +#include <linux/overflow.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +
> +#include <dt-bindings/clock/ambarella,cv75-rct.h>
> +
> +#define CV75_NUM_CLKS (CV75_GCLK_APB + 1)
> +
> +#define PLL_CTRL_BYPASS BIT(2)
> +#define PLL_CTRL_FRAC_MODE BIT(3)
> +#define PLL_CTRL_FORCE_RESET BIT(4)
> +#define PLL_CTRL_POWER_DOWN BIT(5)
> +#define PLL_CTRL_HALT_VCO BIT(6)
> +#define PLL_CTRL_SDIV GENMASK(15, 12)
> +#define PLL_CTRL_SOUT GENMASK(19, 16)
> +#define PLL_CTRL_INTP GENMASK(30, 24)
> +
> +#define PLL_CTRL2_VCODIV_DIV2 BIT(8)
> +#define PLL_CTRL2_FSDIV_DIV2 BIT(9)
> +#define PLL_CTRL2_FSOUT_DIV2 BIT(11)
> +#define PLL_CTRL2_BYPASS_HSDIV BIT(12)
> +
> +struct cv75_pll_regs {
> + u32 ctrl;
> + u32 frac;
> + u32 ctrl2;
> +};
> +
> +struct cv75_pll_hw {
> + struct clk_hw hw;
> + struct cv75_pll_regs regs;
> +};
> +
> +struct cv75_clk {
> + void __iomem *base;
> + spinlock_t lock;
> + struct cv75_pll_hw core;
> + struct cv75_pll_hw enet;
> + struct cv75_pll_hw sd;
> +};
> +
> +struct cv75_pll {
> + const char *name;
> + struct cv75_pll_hw *hw;
> + unsigned long flags;
> + struct cv75_pll_regs regs;
> + int id;
> +};
> +
> +struct cv75_fixed_factor {
> + const char *name;
> + u32 id;
> + struct u16_fract ratio;
> +};
> +
> +struct cv75_composite {
> + const char *name;
> + const struct clk_parent_data *parents;
> + unsigned long flags;
> + u32 id;
> + u16 mux_reg;
> + u16 div_reg;
> + u8 num_parents;
> + u8 mux_shift;
> + u8 mux_width;
> + u8 div_shift;
> + u8 div_width;
> +};
> +
> +struct cv75_composite_hw {
> + struct clk_mux mux;
> + struct clk_divider div;
> + struct clk_gate gate;
> +};
> +
> +static struct cv75_clk cv75_ccu;
> +
> +#define CV75_PLL(_id, _name, _ctrl, _frac, _ctrl2, _flags, _hw) { \
> + .name = _name, \
> + .hw = _hw, \
> + .flags = _flags, \
> + .regs = { \
> + .ctrl = _ctrl, \
> + .frac = _frac, \
> + .ctrl2 = _ctrl2, \
> + }, \
> + .id = _id, \
> +}
> +
> +static const struct cv75_pll cv75_plls[] = {
> + CV75_PLL(CV75_GCLK_CORE, "core", 0x000, 0x004, 0x100,
> + CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL, &cv75_ccu.core),
> + CV75_PLL(-1, "pll_enet", 0x520, 0x524, 0x528,
> + CLK_GET_RATE_NOCACHE, &cv75_ccu.enet),
> + CV75_PLL(-1, "pll_sd", 0x4ac, 0x4b0, 0x4b4,
> + CLK_GET_RATE_NOCACHE, &cv75_ccu.sd),
Why is CLK_GET_RATE_NOCACHE set on all of these?
> +};
> +
> +static const struct cv75_fixed_factor cv75_fixed_factors[] = {
> + { .id = CV75_GCLK_AHB, .name = "ahb", .ratio = { 1, 2 } },
> + { .id = CV75_GCLK_APB, .name = "apb", .ratio = { 1, 4 } },
> +};
> +
> +static const struct clk_parent_data cv75_uart_parents[] = {
> + { .fw_name = "osc" },
> + { .hw = &cv75_ccu.core.hw },
> + { .hw = &cv75_ccu.enet.hw },
> + { .hw = &cv75_ccu.sd.hw },
> +};
> +
> +#define CV75_COMPOSITE(_id, _name, _parents, _mreg, _mshift, _mwidth, \
> + _dreg, _dshift, _dwidth, _flags) \
> +{ \
> + .id = _id, \
> + .name = _name, \
> + .parents = _parents, \
> + .num_parents = ARRAY_SIZE(_parents), \
> + .mux_reg = _mreg, \
> + .mux_shift = _mshift, \
> + .mux_width = _mwidth, \
> + .div_reg = _dreg, \
> + .div_shift = _dshift, \
> + .div_width = _dwidth, \
> + .flags = _flags, \
> +}
> +
> +static const struct cv75_composite cv75_composites[] = {
> + CV75_COMPOSITE(CV75_GCLK_UART0, "uart0", cv75_uart_parents,
> + 0x1c8, 0, 2, 0x038, 0, 24,
> + CLK_SET_RATE_NO_REPARENT),
> +};
> +
> +#define to_cv75_pll(_hw) container_of(_hw, struct cv75_pll_hw, hw)
> +
> +static unsigned long cv75_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct cv75_pll_hw *pll = to_cv75_pll(hw);
> + u32 ctrl, ctrl2, frac_val;
> + u32 intp, sdiv, sout;
> + u32 vcodiv, fsdiv, fsout;
> + u64 vco, frac = 0;
Put in reverse Christmas tree order.
> +
> + ctrl = readl_relaxed(cv75_ccu.base + pll->regs.ctrl);
> + if (ctrl & (PLL_CTRL_POWER_DOWN | PLL_CTRL_HALT_VCO | PLL_CTRL_FORCE_RESET))
> + return 0;
> +
> + if (ctrl & PLL_CTRL_BYPASS)
> + return parent_rate;
> +
> + ctrl2 = readl_relaxed(cv75_ccu.base + pll->regs.ctrl2);
> + intp = FIELD_GET(PLL_CTRL_INTP, ctrl) + 1;
> + sdiv = FIELD_GET(PLL_CTRL_SDIV, ctrl) + 1;
> + sout = FIELD_GET(PLL_CTRL_SOUT, ctrl) + 1;
> + vcodiv = (ctrl2 & PLL_CTRL2_VCODIV_DIV2) ? 2 : 1;
> + fsdiv = (ctrl2 & PLL_CTRL2_FSDIV_DIV2) ? 2 : 1;
> + fsout = (ctrl2 & PLL_CTRL2_FSOUT_DIV2) ? 2 : 1;
> +
> + vco = (u64)parent_rate * vcodiv * fsdiv * intp * sdiv;
> + if (ctrl & PLL_CTRL_FRAC_MODE) {
> + frac_val = readl_relaxed(cv75_ccu.base + pll->regs.frac);
> + frac = mul_u64_u32_shr((u64)parent_rate * vcodiv * fsdiv * sdiv,
> + frac_val, 32);
> + vco += frac;
> + }
> +
> + if (!(ctrl2 & PLL_CTRL2_BYPASS_HSDIV))
> + vco = div_u64(vco, (u64)vcodiv * fsout * sout);
> +
> + return vco;
> +}
> +
> +static const struct clk_ops cv75_pll_ops = {
> + .recalc_rate = cv75_pll_recalc_rate,
> +};
> +
> +static int cv75_register_plls(struct device *dev,
> + struct clk_hw_onecell_data *data)
> +{
> + struct clk_parent_data parent_data = { .fw_name = "osc" };
> +
> + for (unsigned int i = 0; i < ARRAY_SIZE(cv75_plls); i++) {
> + const struct cv75_pll *desc = &cv75_plls[i];
> + struct cv75_pll_hw *pll = desc->hw;
> + struct clk_init_data init = {};
> + int ret;
> +
> + pll->regs = desc->regs;
> +
> + init.name = desc->name;
> + init.ops = &cv75_pll_ops;
> + init.parent_data = &parent_data;
> + init.num_parents = 1;
> + init.flags = desc->flags;
> + pll->hw.init = &init;
> +
> + ret = devm_clk_hw_register(dev, &pll->hw);
> + if (ret)
> + return ret;
> +
> + if (desc->id >= 0)
> + data->hws[desc->id] = &pll->hw;
> + }
> +
> + return 0;
> +}
> +
> +static int cv75_register_fixed_factors(struct device *dev,
> + struct clk_hw_onecell_data *data)
> +{
> + for (unsigned int i = 0; i < ARRAY_SIZE(cv75_fixed_factors); i++) {
> + const struct cv75_fixed_factor *f = &cv75_fixed_factors[i];
> + struct clk_hw *hw;
> +
> + hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, f->name,
> + &cv75_ccu.core.hw, 0,
> + f->ratio.numerator,
> + f->ratio.denominator);
> + if (IS_ERR(hw))
> + return PTR_ERR(hw);
> +
> + data->hws[f->id] = hw;
> + }
> +
> + return 0;
> +}
> +
> +static struct clk_hw *cv75_register_composite(struct device *dev,
> + const struct cv75_composite *c)
> +{
> + const struct clk_ops *mux_ops = NULL, *div_ops = NULL, *gate_ops = NULL;
> + struct clk_hw *mux_hw = NULL, *div_hw = NULL, *gate_hw = NULL;
> + struct cv75_composite_hw *comp;
> +
> + comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
> + if (!comp)
> + return ERR_PTR(-ENOMEM);
> +
> + if (c->mux_width) {
> + comp->mux.reg = cv75_ccu.base + c->mux_reg;
> + comp->mux.shift = c->mux_shift;
> + comp->mux.mask = GENMASK(c->mux_width - 1, 0);
> + comp->mux.lock = &cv75_ccu.lock;
> + mux_hw = &comp->mux.hw;
> + mux_ops = &clk_mux_ops;
> + }
> +
> + if (c->div_width) {
> + comp->div.reg = cv75_ccu.base + c->div_reg;
> + comp->div.shift = c->div_shift;
> + comp->div.width = c->div_width;
> + comp->div.flags = CLK_DIVIDER_ONE_BASED;
> + comp->div.lock = &cv75_ccu.lock;
> + div_hw = &comp->div.hw;
> + div_ops = &clk_divider_ops;
> +
> + comp->gate.reg = cv75_ccu.base + c->div_reg;
> + comp->gate.bit_idx = c->div_shift + c->div_width;
> + comp->gate.flags = CLK_GATE_SET_TO_DISABLE;
> + comp->gate.lock = &cv75_ccu.lock;
> + gate_hw = &comp->gate.hw;
> + gate_ops = &clk_gate_ops;
> + }
> +
> + return devm_clk_hw_register_composite_pdata(dev, c->name, c->parents,
> + c->num_parents,
> + mux_hw, mux_ops,
> + div_hw, div_ops,
> + gate_hw, gate_ops, c->flags);
> +}
> +
> +static int cv75_register_composites(struct device *dev,
> + struct clk_hw_onecell_data *data)
> +{
> + for (unsigned int i = 0; i < ARRAY_SIZE(cv75_composites); i++) {
> + const struct cv75_composite *c = &cv75_composites[i];
> + struct clk_hw *hw;
> +
> + hw = cv75_register_composite(dev, c);
> + if (IS_ERR(hw))
> + return PTR_ERR(hw);
> +
> + data->hws[c->id] = hw;
> + }
> +
> + return 0;
> +}
If you think that you'll have other SoC designs in the future, then you
should split out this functionality above into it's own .c file that you
can use as a library.
Brian
--
Brian Masney <masneyb@kernel.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-09 17:02 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 11:00 [PATCH 0/2] clk: add Ambarella CV75 RCT clock controller Long Zhao via B4 Relay
2026-10-09 11:00 ` [PATCH 1/2] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay
2026-10-09 11:32 ` Krzysztof Kozlowski
2026-10-09 11:00 ` [PATCH 2/2] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
2026-10-09 17:02 ` Brian Masney
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox