* [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
* 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
* [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 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