[PATCH v7 3/3] clk: tenstorrent: Add Atlantis clock controller driver
Brian Masney
bmasney at redhat.com
Tue Mar 3 14:32:23 PST 2026
Hi Anirudh,
Thanks for the patch. A few minor comments below with some minor
nitpicks, additional places to use FIELD_GET(), plus some suggestions
for additional regmap helpers to use.
On Tue, Mar 03, 2026 at 11:36:09AM -0600, Anirudh Srinivasan wrote:
> Add driver for clock controller in Tenstorrent Atlantis SoC. This version
> of the driver covers clocks from RCPU subsystem.
>
> 5 types of clocks generated by this controller: PLLs (PLLs
> with bypass functionality and an additional Gate clk at output), Shared
> Gates (Multiple Gate clks that share an enable bit), standard Muxes,
> Dividers and Gates. All clocks are implemented using custom clk ops and
> use the regmap interface associated with the syscon. All clocks are derived
> from a 24 Mhz oscillator.
>
> The reset controller is also setup as an auxiliary device of the clock
> controller.
>
> Signed-off-by: Anirudh Srinivasan <asrinivasan at oss.tenstorrent.com>
> ---
> MAINTAINERS | 1 +
> drivers/clk/Kconfig | 1 +
> drivers/clk/Makefile | 1 +
> drivers/clk/tenstorrent/Kconfig | 14 +
> drivers/clk/tenstorrent/Makefile | 3 +
> drivers/clk/tenstorrent/atlantis-prcm.c | 893 ++++++++++++++++++++++++++++++++
> 6 files changed, 913 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 493d007d3c65..a7783eb0a7de 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22812,6 +22812,7 @@ T: git https://github.com/tenstorrent/linux.git
> F: Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml
> F: Documentation/devicetree/bindings/riscv/tenstorrent.yaml
> F: arch/riscv/boot/dts/tenstorrent/
> +F: drivers/clk/tenstorrent/
> F: drivers/reset/reset-tenstorrent-atlantis.c
> F: include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 3d803b4cf5c1..8cc300b90b5f 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -531,6 +531,7 @@ source "drivers/clk/starfive/Kconfig"
> source "drivers/clk/sunxi/Kconfig"
> source "drivers/clk/sunxi-ng/Kconfig"
> source "drivers/clk/tegra/Kconfig"
> +source "drivers/clk/tenstorrent/Kconfig"
> source "drivers/clk/thead/Kconfig"
> source "drivers/clk/stm32/Kconfig"
> source "drivers/clk/ti/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index f7bce3951a30..f52cf3ac64fc 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -155,6 +155,7 @@ obj-y += starfive/
> obj-$(CONFIG_ARCH_SUNXI) += sunxi/
> obj-y += sunxi-ng/
> obj-$(CONFIG_ARCH_TEGRA) += tegra/
> +obj-y += tenstorrent/
> obj-$(CONFIG_ARCH_THEAD) += thead/
> obj-y += ti/
> obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
> diff --git a/drivers/clk/tenstorrent/Kconfig b/drivers/clk/tenstorrent/Kconfig
> new file mode 100644
> index 000000000000..9d4391eeeae0
> --- /dev/null
> +++ b/drivers/clk/tenstorrent/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config TENSTORRENT_ATLANTIS_PRCM
> + tristate "Support for Tenstorrent Atlantis PRCM Clock Controller"
> + depends on ARCH_TENSTORRENT || COMPILE_TEST
> + default ARCH_TENSTORRENT
> + select REGMAP_MMIO
> + select AUXILIARY_BUS
> + select MFD_SYSCON
> + help
> + Say yes here to support the different clock
> + controllers found in the Tenstorrent Atlantis SoC.
> + This includes the clocks from the RCPU, HSIO, MMIO
> + and PCIE domain.
> diff --git a/drivers/clk/tenstorrent/Makefile b/drivers/clk/tenstorrent/Makefile
> new file mode 100644
> index 000000000000..95d87bac7bf5
> --- /dev/null
> +++ b/drivers/clk/tenstorrent/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_TENSTORRENT_ATLANTIS_PRCM) += atlantis-prcm.o
> diff --git a/drivers/clk/tenstorrent/atlantis-prcm.c b/drivers/clk/tenstorrent/atlantis-prcm.c
> new file mode 100644
> index 000000000000..b0a8e3526901
> --- /dev/null
> +++ b/drivers/clk/tenstorrent/atlantis-prcm.c
> @@ -0,0 +1,893 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Tenstorrent Atlantis PRCM Clock Driver
> + *
> + * Copyright (c) 2026 Tenstorrent
> + */
> +
> +#include <dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h>
> +#include <linux/auxiliary_bus.h>
> +#include <linux/bitfield.h>
> +#include <linux/clk-provider.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +/* RCPU Clock Register Offsets */
> +#define RCPU_PLL_CFG_REG 0x0000
> +#define RCPU_NOCC_PLL_CFG_REG 0x0004
> +#define RCPU_NOCC_CLK_CFG_REG 0x0008
> +#define RCPU_DIV_CFG_REG 0x000C
> +#define RCPU_BLK_CG_REG 0x0014
> +#define LSIO_BLK_CG_REG 0x0018
> +#define PLL_RCPU_EN_REG 0x011c
> +#define PLL_NOCC_EN_REG 0x0120
> +#define BUS_CG_REG 0x01FC
> +
> +/* PLL Bit Definitions */
> +#define PLL_CFG_EN_BIT BIT(0)
> +#define PLL_CFG_BYPASS_BIT BIT(1)
> +#define PLL_CFG_REFDIV_MASK GENMASK(7, 2)
> +#define PLL_CFG_REFDIV_SHIFT 2
> +#define PLL_CFG_POSTDIV1_MASK GENMASK(10, 8)
> +#define PLL_CFG_POSTDIV1_SHIFT 8
> +#define PLL_CFG_POSTDIV2_MASK GENMASK(13, 11)
> +#define PLL_CFG_POSTDIV2_SHIFT 11
> +#define PLL_CFG_FBDIV_MASK GENMASK(25, 14)
> +#define PLL_CFG_FBDIV_SHIFT 14
> +#define PLL_CFG_LKDT_BIT BIT(30)
> +#define PLL_CFG_LOCK_BIT BIT(31)
> +#define PLL_LOCK_TIMEOUT_US 1000
> +#define PLL_BYPASS_WAIT_US 500
> +
> +struct atlantis_clk_common {
> + int clkid;
> + struct regmap *regmap;
> + struct clk_hw hw;
> +};
> +
> +static inline struct atlantis_clk_common *
> +hw_to_atlantis_clk_common(struct clk_hw *hw)
> +{
> + return container_of(hw, struct atlantis_clk_common, hw);
> +}
> +
> +struct atlantis_clk_mux_config {
> + u8 shift;
> + u8 width;
> + u32 reg_offset;
> +};
> +
> +struct atlantis_clk_mux {
> + struct atlantis_clk_common common;
> + struct atlantis_clk_mux_config config;
> +};
> +
> +struct atlantis_clk_gate_config {
> + u32 reg_offset;
> + u32 enable;
> +};
> +
> +struct atlantis_clk_gate {
> + struct atlantis_clk_common common;
> + struct atlantis_clk_gate_config config;
> +};
> +
> +struct atlantis_clk_divider_config {
> + u8 shift;
> + u8 width;
> + u32 flags;
> + u32 reg_offset;
> +};
> +
> +struct atlantis_clk_divider {
> + struct atlantis_clk_common common;
> + struct atlantis_clk_divider_config config;
> +};
> +
> +struct atlantis_clk_pll_config {
> + u32 tbl_num;
> + u32 reg_offset;
> + u32 en_reg_offset;
> + u32 cg_reg_offset;
> + u32 cg_reg_enable;
> +};
> +
> +/* Models a PLL with Bypass Functionality and Enable Bit + an optional Gate Clock at it's output */
> +struct atlantis_clk_pll {
> + struct atlantis_clk_common common;
> + struct atlantis_clk_pll_config config;
> +};
> +
> +struct atlantis_clk_gate_shared_config {
> + u32 reg_offset;
> + u32 enable;
> + unsigned int *share_count;
> + spinlock_t *refcount_lock;
> +};
> +
> +struct atlantis_clk_gate_shared {
> + struct atlantis_clk_common common;
> + struct atlantis_clk_gate_shared_config config;
> +};
> +
> +struct atlantis_clk_fixed_factor_config {
> + unsigned int mult;
> + unsigned int div;
> +};
> +
> +struct atlantis_clk_fixed_factor {
> + struct atlantis_clk_fixed_factor_config config;
> + struct atlantis_clk_common common;
> +};
> +
> +static inline struct atlantis_clk_mux *hw_to_atlantis_clk_mux(struct clk_hw *hw)
> +{
> + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw);
> +
> + return container_of(common, struct atlantis_clk_mux, common);
> +}
> +
> +static inline struct atlantis_clk_gate *
> +hw_to_atlantis_clk_gate(struct clk_hw *hw)
> +{
> + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw);
> +
> + return container_of(common, struct atlantis_clk_gate, common);
> +}
> +
> +static inline struct atlantis_clk_divider *
> +hw_to_atlantis_clk_divider(struct clk_hw *hw)
> +{
> + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw);
> +
> + return container_of(common, struct atlantis_clk_divider, common);
> +}
> +
> +static inline struct atlantis_clk_pll *hw_to_atlantis_pll(struct clk_hw *hw)
> +{
> + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw);
> +
> + return container_of(common, struct atlantis_clk_pll, common);
> +}
> +
> +static inline struct atlantis_clk_gate_shared *
> +hw_to_atlantis_clk_gate_shared(struct clk_hw *hw)
> +{
> + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw);
> +
> + return container_of(common, struct atlantis_clk_gate_shared, common);
> +}
> +
> +static inline struct atlantis_clk_fixed_factor *
> +hw_to_atlantis_clk_fixed_factor(struct clk_hw *hw)
> +{
> + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw);
> +
> + return container_of(common, struct atlantis_clk_fixed_factor, common);
> +}
> +
> +static u8 atlantis_clk_mux_get_parent(struct clk_hw *hw)
> +{
> + struct atlantis_clk_mux *mux = hw_to_atlantis_clk_mux(hw);
> + u32 val;
> +
> + regmap_read(mux->common.regmap, mux->config.reg_offset, &val);
> + val >>= mux->config.shift;
> + val &= (BIT(mux->config.width) - 1);
> +
> + return val;
FIELD_GET() ?
> +}
> +
> +static int atlantis_clk_mux_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct atlantis_clk_mux *mux = hw_to_atlantis_clk_mux(hw);
> + u32 val = index;
> +
> + return regmap_update_bits(mux->common.regmap, mux->config.reg_offset,
> + (BIT(mux->config.width) - 1)
> + << mux->config.shift,
This doesn't make the line that much longer, and is nicer to read:
(BIT(mux->config.width) - 1) << mux->config.shift,
> + val << mux->config.shift);
> +}
> +
> +static int atlantis_clk_mux_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + return clk_mux_determine_rate_flags(hw, req, hw->init->flags);
> +}
> +
> +static const struct clk_ops atlantis_clk_mux_ops = {
> + .get_parent = atlantis_clk_mux_get_parent,
> + .set_parent = atlantis_clk_mux_set_parent,
> + .determine_rate = atlantis_clk_mux_determine_rate,
> +};
> +
> +static void atlantis_clk_gate_endisable(struct clk_hw *hw, int enable)
Should this return an int? This looks like we should use this return
value below in atlantis_clk_gate_enable().
> +{
> + struct atlantis_clk_gate *gate = hw_to_atlantis_clk_gate(hw);
> + u32 val;
> +
> + if (enable)
> + val = gate->config.enable;
> + else
> + val = ~(gate->config.enable);
> +
> + regmap_update_bits(gate->common.regmap, gate->config.reg_offset,
> + gate->config.enable, val);
This chunk could be simplified to use regmap_set_bits() and
regmap_clear_bits().
> +}
> +
> +static int atlantis_clk_gate_enable(struct clk_hw *hw)
> +{
> + atlantis_clk_gate_endisable(hw, 1);
> +
> + return 0;
Follow up from above. Any reason why the return value of
regmap_update_bits() in atlantis_clk_gate_endisable() is discarded?
I know it's not used below in the disable().
> +}
> +
> +static void atlantis_clk_gate_disable(struct clk_hw *hw)
> +{
> + atlantis_clk_gate_endisable(hw, 0);
> +}
> +
> +static int atlantis_clk_gate_is_enabled(struct clk_hw *hw)
> +{
> + struct atlantis_clk_gate *gate = hw_to_atlantis_clk_gate(hw);
> + u32 val;
> +
> + regmap_read(gate->common.regmap, gate->config.reg_offset, &val);
> + val &= gate->config.enable;
> +
> + return !!val;
regmap_test_bits() can simplify this.
> +}
> +
> +static const struct clk_ops atlantis_clk_gate_ops = {
> + .enable = atlantis_clk_gate_enable,
> + .disable = atlantis_clk_gate_disable,
> + .is_enabled = atlantis_clk_gate_is_enabled,
> +};
> +
> +static unsigned long atlantis_clk_divider_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct atlantis_clk_divider *divider = hw_to_atlantis_clk_divider(hw);
> + u32 val;
> +
> + regmap_read(divider->common.regmap, divider->config.reg_offset, &val);
> +
> + val >>= divider->config.shift;
> + val &= ((1 << (divider->config.width)) - 1);
FIELD_GET() ?
> +
> + return DIV_ROUND_UP_ULL((u64)parent_rate, val + 1);
> +}
> +
> +static const struct clk_ops atlantis_clk_divider_ops = {
> + .recalc_rate = atlantis_clk_divider_recalc_rate,
> +};
> +
> +static unsigned long
> +atlantis_clk_fixed_factor_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct atlantis_clk_fixed_factor *factor =
> + hw_to_atlantis_clk_fixed_factor(hw);
> + unsigned long long rate;
> +
> + rate = (unsigned long long)parent_rate * factor->config.mult;
> + do_div(rate, factor->config.div);
newline
> + return (unsigned long)rate;
> +}
> +
> +static const struct clk_ops atlantis_clk_fixed_factor_ops = {
> + .recalc_rate = atlantis_clk_fixed_factor_recalc_rate,
> +};
> +
> +static int atlantis_clk_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw);
> + u32 val, en_val, cg_val;
> +
> + regmap_read(pll->common.regmap, pll->config.reg_offset, &val);
> + regmap_read(pll->common.regmap, pll->config.en_reg_offset, &en_val);
> + regmap_read(pll->common.regmap, pll->config.cg_reg_offset, &cg_val);
> +
> + /* Check if PLL is powered on, locked, not bypassed and Gate clk is enabled */
> + return !!(en_val & PLL_CFG_EN_BIT) && !!(val & PLL_CFG_LOCK_BIT) &&
> + (!pll->config.cg_reg_enable || (cg_val & pll->config.cg_reg_enable)) &&
> + !(val & PLL_CFG_BYPASS_BIT);
Could regmap_test_bits() make this a bit cleaner?
> +}
> +
> +static int atlantis_clk_pll_enable(struct clk_hw *hw)
> +{
> + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw);
> + u32 val, en_val, cg_val;
> + int ret;
> +
> + regmap_read(pll->common.regmap, pll->config.reg_offset, &val);
> + regmap_read(pll->common.regmap, pll->config.en_reg_offset, &en_val);
> + regmap_read(pll->common.regmap, pll->config.cg_reg_offset, &cg_val);
> +
> + /* Check if PLL is already enabled, locked, not bypassed and Gate clk is enabled */
> + if ((en_val & PLL_CFG_EN_BIT) && (val & PLL_CFG_LOCK_BIT) &&
> + (!pll->config.cg_reg_enable || (cg_val & pll->config.cg_reg_enable)) &&
> + !(val & PLL_CFG_BYPASS_BIT)) {
Same about regmap_test_bits() here.
> + return 0;
> + }
> +
> + /* Step 1: Set bypass mode first */
> + regmap_update_bits(pll->common.regmap, pll->config.reg_offset,
> + PLL_CFG_BYPASS_BIT, PLL_CFG_BYPASS_BIT);
> +
> + /* Step 2: Enable PLL (clear then set power bit) */
> + regmap_update_bits(pll->common.regmap, pll->config.en_reg_offset,
> + PLL_CFG_EN_BIT, 0);
> +
> + regmap_update_bits(pll->common.regmap, pll->config.en_reg_offset,
> + PLL_CFG_EN_BIT, PLL_CFG_EN_BIT);
> +
> + /* Step 3: Wait for PLL lock */
> + ret = regmap_read_poll_timeout(pll->common.regmap,
> + pll->config.reg_offset, val,
> + val & PLL_CFG_LOCK_BIT, 10,
> + PLL_BYPASS_WAIT_US);
Should the last two parameters be
PLL_BYPASS_WAIT_US, PLL_LOCK_TIMEOUT_US instead of
10, PLL_BYPASS_WAIT_US?
> + if (ret) {
> + pr_err("PLL failed to lock within timeout\n");
> + return ret;
> + }
> +
> + /* Step 4: Switch from bypass to PLL output */
> + regmap_update_bits(pll->common.regmap, pll->config.reg_offset,
> + PLL_CFG_BYPASS_BIT, 0);
> +
> + /* Enable Gate clk at PLL Output */
> + return regmap_update_bits(pll->common.regmap, pll->config.cg_reg_offset,
> + pll->config.cg_reg_enable,
> + pll->config.cg_reg_enable);
> +}
> +
> +static void atlantis_clk_pll_disable(struct clk_hw *hw)
> +{
> + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw);
> +
> + /* Step 1: Switch to bypass mode before disabling */
> + regmap_update_bits(pll->common.regmap, pll->config.reg_offset,
> + PLL_CFG_BYPASS_BIT, PLL_CFG_BYPASS_BIT);
> + /* Step 2: Power down PLL */
> + regmap_update_bits(pll->common.regmap, pll->config.en_reg_offset,
> + PLL_CFG_EN_BIT, 0);
> +}
> +
> +static unsigned long atlantis_clk_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw);
> +
> + u32 val, refdiv, fbdiv, postdiv1, postdiv2;
> + u64 fout;
> +
> + regmap_read(pll->common.regmap, pll->config.reg_offset, &val);
> +
> + if (val & PLL_CFG_BYPASS_BIT)
> + return parent_rate;
> +
> + refdiv = FIELD_GET(PLL_CFG_REFDIV_MASK, val);
> + fbdiv = FIELD_GET(PLL_CFG_FBDIV_MASK, val);
> + postdiv1 = FIELD_GET(PLL_CFG_POSTDIV1_MASK, val);
> + postdiv2 = FIELD_GET(PLL_CFG_POSTDIV2_MASK, val);
> +
> + if (!refdiv)
> + refdiv = 1;
> + if (!postdiv1)
> + postdiv1 = 1;
> + if (!postdiv2)
> + postdiv2 = 1;
> + if (!fbdiv)
> + return 0;
> +
> + fout = div64_u64((u64)parent_rate * fbdiv,
> + refdiv * postdiv1 * postdiv2);
> +
> + return fout;
> +}
> +
> +static const struct clk_ops atlantis_clk_pll_ops = {
> + .enable = atlantis_clk_pll_enable,
> + .disable = atlantis_clk_pll_disable,
> + .recalc_rate = atlantis_clk_pll_recalc_rate,
> + .is_enabled = atlantis_clk_pll_is_enabled,
> +};
> +
> +static int atlantis_clk_gate_shared_enable(struct clk_hw *hw)
> +{
> + struct atlantis_clk_gate_shared *gate =
> + hw_to_atlantis_clk_gate_shared(hw);
> + bool need_enable;
> + u32 reg;
> +
> + scoped_guard(spinlock_irqsave, gate->config.refcount_lock)
> + {
> + need_enable = (*gate->config.share_count)++ == 0;
> + if (need_enable) {
> + regmap_read(gate->common.regmap,
> + gate->config.reg_offset, ®);
> + reg |= gate->config.enable;
> + regmap_write(gate->common.regmap,
> + gate->config.reg_offset, reg);
Should this use regmap_set_bits()?
> + }
> + }
> +
> + if (need_enable) {
> + regmap_read(gate->common.regmap, gate->config.reg_offset, ®);
> +
> + if (!(reg & gate->config.enable)) {
regmap_test_bits() ?
> + pr_warn("%s: gate enable %d failed to enable\n",
> + clk_hw_get_name(hw), gate->config.enable);
> + return -EIO;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void atlantis_clk_gate_shared_disable(struct clk_hw *hw)
> +{
> + struct atlantis_clk_gate_shared *gate =
> + hw_to_atlantis_clk_gate_shared(hw);
> + u32 reg;
> +
> + scoped_guard(spinlock_irqsave, gate->config.refcount_lock)
> + {
> + if (WARN_ON(*gate->config.share_count == 0))
> + return;
> + if (--(*gate->config.share_count) > 0)
> + return;
> +
> + regmap_read(gate->common.regmap, gate->config.reg_offset, ®);
> + reg &= ~gate->config.enable;
> + regmap_write(gate->common.regmap, gate->config.reg_offset, reg);
Should this use regmap_clear_bits()?
> + }
> +}
> +
> +static int atlantis_clk_gate_shared_is_enabled(struct clk_hw *hw)
> +{
> + struct atlantis_clk_gate_shared *gate =
> + hw_to_atlantis_clk_gate_shared(hw);
> + u32 reg;
> +
> + regmap_read(gate->common.regmap, gate->config.reg_offset, ®);
> +
> + return !!(reg & gate->config.enable);
regmap_test_bits() ?
> +}
> +
> +static void atlantis_clk_gate_shared_disable_unused(struct clk_hw *hw)
> +{
> + struct atlantis_clk_gate_shared *gate =
> + hw_to_atlantis_clk_gate_shared(hw);
> +
> + u32 reg;
> +
> + scoped_guard(spinlock_irqsave, gate->config.refcount_lock)
> + {
> + if (*gate->config.share_count == 0) {
> + regmap_read(gate->common.regmap,
> + gate->config.reg_offset, ®);
> + reg &= ~gate->config.enable;
> + regmap_write(gate->common.regmap,
> + gate->config.reg_offset, reg);
regmap_clear_bits() ?
> + }
> + }
> +}
> +
> +static const struct clk_ops atlantis_clk_gate_shared_ops = {
> + .enable = atlantis_clk_gate_shared_enable,
> + .disable = atlantis_clk_gate_shared_disable,
> + .disable_unused = atlantis_clk_gate_shared_disable_unused,
> + .is_enabled = atlantis_clk_gate_shared_is_enabled,
> +};
> +
> +#define ATLANTIS_PLL_CONFIG(_reg_offset, _en_reg_offset, _cg_reg_offset, \
> + _cg_reg_enable) \
> + { \
> + .reg_offset = (_reg_offset), \
> + .en_reg_offset = (_en_reg_offset), \
> + .cg_reg_offset = (_cg_reg_offset), \
> + .cg_reg_enable = (_cg_reg_enable), \
> + }
> +
> +#define ATLANTIS_PLL_DEFINE(_clkid, _name, _parent, _reg_offset, \
> + _en_reg_offset, _cg_reg_offset, _cg_reg_enable, \
> + _flags) \
> + static struct atlantis_clk_pll _name = { \
> + .config = ATLANTIS_PLL_CONFIG(_reg_offset, _en_reg_offset, \
> + _cg_reg_offset, _cg_reg_enable), \
> + .common = { .clkid = _clkid, \
> + .hw.init = CLK_HW_INIT_PARENTS_DATA( \
> + #_name, _parent, &atlantis_clk_pll_ops, \
> + _flags) }, \
> + }
> +#define ATLANTIS_MUX_CONFIG(_shift, _width, _reg_offset) \
> + { \
> + .shift = _shift, .width = _width, .reg_offset = _reg_offset \
> + }
> +
> +#define ATLANTIS_MUX_DEFINE(_clkid, _name, _parents, _reg_offset, _shift, \
> + _width, _flags) \
> + static struct atlantis_clk_mux _name = { \
> + .config = ATLANTIS_MUX_CONFIG(_shift, _width, _reg_offset), \
> + .common = { .clkid = _clkid, \
> + .hw.init = CLK_HW_INIT_PARENTS_DATA( \
> + #_name, _parents, &atlantis_clk_mux_ops, \
> + _flags) } \
> + }
> +
> +#define ATLANTIS_DIVIDER_CONFIG(_shift, _width, _flags, _reg_offset) \
> + { \
> + .shift = _shift, .width = _width, .flags = _flags, \
> + .reg_offset = _reg_offset \
> + }
> +
> +#define ATLANTIS_DIVIDER_DEFINE(_clkid, _name, _parent, _reg_offset, _shift, \
> + _width, _divflags, _flags) \
> + static struct atlantis_clk_divider _name = { \
> + .config = ATLANTIS_DIVIDER_CONFIG(_shift, _width, _divflags, \
> + _reg_offset), \
> + .common = { .clkid = _clkid, \
> + .hw.init = CLK_HW_INIT_HW( \
> + #_name, &_parent.common.hw, \
> + &atlantis_clk_divider_ops, _flags) } \
> + }
> +#define ATLANTIS_GATE_CONFIG(_enable, _reg_offset) \
> + { \
> + .enable = _enable, .reg_offset = _reg_offset \
> + }
> +
> +#define ATLANTIS_GATE_DEFINE(_clkid, _name, _parent, _reg_offset, _enable, \
> + _flags) \
> + static struct atlantis_clk_gate _name = { \
> + .config = ATLANTIS_GATE_CONFIG(_enable, _reg_offset), \
> + .common = { .clkid = _clkid, \
> + .hw.init = CLK_HW_INIT_HW( \
> + #_name, &_parent.common.hw, \
> + &atlantis_clk_gate_ops, _flags) } \
> + }
> +#define ATLANTIS_GATE_SHARED_CONFIG(_reg_offset, _enable, _share_count) \
> + { \
> + .reg_offset = _reg_offset, .enable = _enable, \
> + .share_count = _share_count, .refcount_lock = &refcount_lock \
> + }
> +#define ATLANTIS_GATE_SHARED_DEFINE(_clkid, _name, _parent, _reg_offset, \
> + _enable, _share_count, _flags) \
> + static struct atlantis_clk_gate_shared _name = { \
> + .config = ATLANTIS_GATE_SHARED_CONFIG(_reg_offset, _enable, \
> + _share_count), \
> + .common = { .clkid = _clkid, \
> + .hw.init = CLK_HW_INIT_HW( \
> + #_name, &_parent.common.hw, \
> + &atlantis_clk_gate_shared_ops, _flags) } \
> + }
> +#define ATLANTIS_CLK_FIXED_FACTOR_DEFINE(_clkid, _name, _parent, _mult, _div, \
> + _flags) \
> + static struct atlantis_clk_fixed_factor _name = { \
> + .config = { .mult = _mult, .div = _div }, \
> + .common = { .clkid = _clkid, \
> + .hw.init = CLK_HW_INIT_HW( \
> + #_name, &_parent.common.hw, \
> + &atlantis_clk_fixed_factor_ops, _flags) } \
> + }
> +
> +static DEFINE_SPINLOCK(refcount_lock); /* Lock for refcount value accesses */
> +
> +static const struct regmap_config atlantis_prcm_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .max_register = 0xFFFC,
> + .cache_type = REGCACHE_NONE,
> +};
> +
> +struct atlantis_prcm_data {
> + struct clk_hw **hws;
> + size_t num;
> + const char *reset_name;
> +};
> +
> +static const struct clk_parent_data osc_24m_clk[] = {
> + { .index = 0 },
> +};
> +
> +ATLANTIS_PLL_DEFINE(CLK_RCPU_PLL, rcpu_pll_clk, osc_24m_clk, RCPU_PLL_CFG_REG,
> + PLL_RCPU_EN_REG, BUS_CG_REG, 0, /* No Gate Clk at Output */
> + CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL);
> +
> +static const struct clk_parent_data rcpu_root_parents[] = {
> + { .index = 0 },
> + { .hw = &rcpu_pll_clk.common.hw },
> +};
> +
> +ATLANTIS_MUX_DEFINE(CLK_RCPU_ROOT, rcpu_root_mux, rcpu_root_parents,
> + RCPU_DIV_CFG_REG, 0, 1, CLK_SET_RATE_NO_REPARENT);
> +
> +ATLANTIS_DIVIDER_DEFINE(CLK_RCPU_DIV2, rcpu_div2_clk, rcpu_root_mux,
> + RCPU_DIV_CFG_REG, 2, 4, 0, 0);
> +ATLANTIS_DIVIDER_DEFINE(CLK_RCPU_DIV4, rcpu_div4_clk, rcpu_root_mux,
> + RCPU_DIV_CFG_REG, 7, 4, 0, 0);
> +ATLANTIS_DIVIDER_DEFINE(CLK_RCPU_RTC, rcpu_rtc_clk, rcpu_div4_clk,
> + RCPU_DIV_CFG_REG, 12, 6, 0, 0);
> +
> +ATLANTIS_GATE_DEFINE(CLK_SMNDMA0_ACLK, smndma0_aclk, rcpu_div2_clk,
> + RCPU_BLK_CG_REG, BIT(0), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SMNDMA1_ACLK, smndma1_aclk, rcpu_div2_clk,
> + RCPU_BLK_CG_REG, BIT(1), 0);
> +ATLANTIS_GATE_DEFINE(CLK_WDT0_PCLK, wdt0_pclk, rcpu_div4_clk, RCPU_BLK_CG_REG,
> + BIT(2), 0);
> +ATLANTIS_GATE_DEFINE(CLK_WDT1_PCLK, wdt1_pclk, rcpu_div4_clk, RCPU_BLK_CG_REG,
> + BIT(3), 0);
> +ATLANTIS_GATE_DEFINE(CLK_TIMER_PCLK, timer_pclk, rcpu_div4_clk, RCPU_BLK_CG_REG,
> + BIT(4), 0);
> +ATLANTIS_GATE_DEFINE(CLK_PVTC_PCLK, pvtc_pclk, rcpu_div4_clk, RCPU_BLK_CG_REG,
> + BIT(12), 0);
> +ATLANTIS_GATE_DEFINE(CLK_PMU_PCLK, pmu_pclk, rcpu_div4_clk, RCPU_BLK_CG_REG,
> + BIT(13), 0);
> +ATLANTIS_GATE_DEFINE(CLK_MAILBOX_HCLK, mb_hclk, rcpu_div2_clk, RCPU_BLK_CG_REG,
> + BIT(14), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SEC_SPACC_HCLK, sec_spacc_hclk, rcpu_div2_clk,
> + RCPU_BLK_CG_REG, BIT(26), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SEC_OTP_HCLK, sec_otp_hclk, rcpu_div2_clk,
> + RCPU_BLK_CG_REG, BIT(28), 0);
> +ATLANTIS_GATE_DEFINE(CLK_TRNG_PCLK, trng_pclk, rcpu_div4_clk, RCPU_BLK_CG_REG,
> + BIT(29), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SEC_CRC_HCLK, sec_crc_hclk, rcpu_div2_clk,
> + RCPU_BLK_CG_REG, BIT(30), 0);
> +
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_SMN_HCLK, rcpu_smn_hclk, rcpu_div2_clk, 1,
> + 1, 0);
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_AHB0_HCLK, rcpu_ahb0_hclk, rcpu_div2_clk,
> + 1, 1, 0);
> +
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_SMN_PCLK, rcpu_smn_pclk, rcpu_div4_clk, 1,
> + 1, 0);
> +
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_SMN_CLK, rcpu_smn_clk, rcpu_root_mux, 1, 1,
> + 0);
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_SCRATCHPAD_CLK, rcpu_scratchpad_aclk,
> + rcpu_root_mux, 1, 1, 0);
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_RCPU_CORE_CLK, rcpu_core_clk,
> + rcpu_root_mux, 1, 1, 0);
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_RCPU_ROM_CLK, rcpu_rom_aclk, rcpu_root_mux,
> + 1, 1, 0);
> +
> +static struct atlantis_clk_fixed_factor
> + otp_load_clk = { .config = { .mult = 1, .div = 1 },
> + .common = {
> + .clkid = CLK_OTP_LOAD_CLK,
> + .hw.init = CLK_HW_INIT_PARENTS_DATA(
> + "otp_load_clk", osc_24m_clk,
> + &atlantis_clk_fixed_factor_ops,
> + CLK_SET_RATE_NO_REPARENT),
> + } };
> +
> +ATLANTIS_PLL_DEFINE(CLK_NOC_PLL, nocc_pll_clk, osc_24m_clk,
> + RCPU_NOCC_PLL_CFG_REG, PLL_NOCC_EN_REG, BUS_CG_REG, BIT(0),
> + CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL);
> +
> +static const struct clk_parent_data nocc_mux_parents[] = {
> + { .index = 0 },
> + { .hw = &nocc_pll_clk.common.hw },
> +};
> +
> +ATLANTIS_MUX_DEFINE(CLK_NOCC_CLK, nocc_clk_mux, nocc_mux_parents,
> + RCPU_NOCC_CLK_CFG_REG, 0, 1, CLK_SET_RATE_NO_REPARENT);
> +
> +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_DIV2, nocc_div2_clk, nocc_clk_mux,
> + RCPU_NOCC_CLK_CFG_REG, 1, 4, 0, 0);
> +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_DIV4, nocc_div4_clk, nocc_clk_mux,
> + RCPU_NOCC_CLK_CFG_REG, 5, 4, 0, 0);
> +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_RTC, nocc_rtc_clk, nocc_div4_clk,
> + RCPU_NOCC_CLK_CFG_REG, 9, 6, 0, 0);
> +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_CAN, nocc_can_div, nocc_clk_mux,
> + RCPU_NOCC_CLK_CFG_REG, 15, 4, 0, 0);
> +
> +static unsigned int refcnt_qspi;
> +ATLANTIS_GATE_SHARED_DEFINE(CLK_QSPI_SCLK, qspi_sclk, nocc_clk_mux,
> + LSIO_BLK_CG_REG, BIT(0), &refcnt_qspi, 0);
> +ATLANTIS_GATE_SHARED_DEFINE(CLK_QSPI_HCLK, qspi_hclk, nocc_div2_clk,
> + LSIO_BLK_CG_REG, BIT(0), &refcnt_qspi, 0);
> +ATLANTIS_GATE_DEFINE(CLK_I2C0_PCLK, i2c0_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(1), 0);
> +ATLANTIS_GATE_DEFINE(CLK_I2C1_PCLK, i2c1_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(2), 0);
> +ATLANTIS_GATE_DEFINE(CLK_I2C2_PCLK, i2c2_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(3), 0);
> +ATLANTIS_GATE_DEFINE(CLK_I2C3_PCLK, i2c3_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(4), 0);
> +ATLANTIS_GATE_DEFINE(CLK_I2C4_PCLK, i2c4_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(5), 0);
> +
> +ATLANTIS_GATE_DEFINE(CLK_UART0_PCLK, uart0_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(6), 0);
> +ATLANTIS_GATE_DEFINE(CLK_UART1_PCLK, uart1_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(7), 0);
> +ATLANTIS_GATE_DEFINE(CLK_UART2_PCLK, uart2_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(8), 0);
> +ATLANTIS_GATE_DEFINE(CLK_UART3_PCLK, uart3_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(9), 0);
> +ATLANTIS_GATE_DEFINE(CLK_UART4_PCLK, uart4_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(10), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SPI0_PCLK, spi0_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(11), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SPI1_PCLK, spi1_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(12), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SPI2_PCLK, spi2_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(13), 0);
> +ATLANTIS_GATE_DEFINE(CLK_SPI3_PCLK, spi3_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(14), 0);
> +ATLANTIS_GATE_DEFINE(CLK_GPIO_PCLK, gpio_pclk, nocc_div4_clk, LSIO_BLK_CG_REG,
> + BIT(15), 0);
> +
> +static unsigned int refcnt_can0;
> +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN0_HCLK, can0_hclk, nocc_div2_clk,
> + LSIO_BLK_CG_REG, BIT(17), &refcnt_can0, 0);
> +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN0_CLK, can0_clk, nocc_can_div,
> + LSIO_BLK_CG_REG, BIT(17), &refcnt_can0, 0);
> +
> +static unsigned int refcnt_can1;
> +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN1_HCLK, can1_hclk, nocc_div2_clk,
> + LSIO_BLK_CG_REG, BIT(18), &refcnt_can1, 0);
> +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN1_CLK, can1_clk, nocc_can_div,
> + LSIO_BLK_CG_REG, BIT(18), &refcnt_can1, 0);
> +
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_CAN0_TIMER_CLK, can0_timer_clk,
> + nocc_rtc_clk, 1, 1, 0);
> +ATLANTIS_CLK_FIXED_FACTOR_DEFINE(CLK_CAN1_TIMER_CLK, can1_timer_clk,
> + nocc_rtc_clk, 1, 1, 0);
> +
> +static struct clk_hw *atlantis_rcpu_clks[] = {
> + [CLK_RCPU_PLL] = &rcpu_pll_clk.common.hw,
> + [CLK_RCPU_ROOT] = &rcpu_root_mux.common.hw,
> + [CLK_RCPU_DIV2] = &rcpu_div2_clk.common.hw,
> + [CLK_RCPU_DIV4] = &rcpu_div4_clk.common.hw,
> + [CLK_RCPU_RTC] = &rcpu_rtc_clk.common.hw,
> + [CLK_SMNDMA0_ACLK] = &smndma0_aclk.common.hw,
> + [CLK_SMNDMA1_ACLK] = &smndma1_aclk.common.hw,
> + [CLK_WDT0_PCLK] = &wdt0_pclk.common.hw,
> + [CLK_WDT1_PCLK] = &wdt1_pclk.common.hw,
> + [CLK_TIMER_PCLK] = &timer_pclk.common.hw,
> + [CLK_PVTC_PCLK] = &pvtc_pclk.common.hw,
> + [CLK_PMU_PCLK] = &pmu_pclk.common.hw,
> + [CLK_MAILBOX_HCLK] = &mb_hclk.common.hw,
> + [CLK_SEC_SPACC_HCLK] = &sec_spacc_hclk.common.hw,
> + [CLK_SEC_OTP_HCLK] = &sec_otp_hclk.common.hw,
> + [CLK_TRNG_PCLK] = &trng_pclk.common.hw,
> + [CLK_SEC_CRC_HCLK] = &sec_crc_hclk.common.hw,
> + [CLK_SMN_HCLK] = &rcpu_smn_hclk.common.hw,
> + [CLK_AHB0_HCLK] = &rcpu_ahb0_hclk.common.hw,
> + [CLK_SMN_PCLK] = &rcpu_smn_pclk.common.hw,
> + [CLK_SMN_CLK] = &rcpu_smn_clk.common.hw,
> + [CLK_SCRATCHPAD_CLK] = &rcpu_scratchpad_aclk.common.hw,
> + [CLK_RCPU_CORE_CLK] = &rcpu_core_clk.common.hw,
> + [CLK_RCPU_ROM_CLK] = &rcpu_rom_aclk.common.hw,
> + [CLK_OTP_LOAD_CLK] = &otp_load_clk.common.hw,
> + [CLK_NOC_PLL] = &nocc_pll_clk.common.hw,
> + [CLK_NOCC_CLK] = &nocc_clk_mux.common.hw,
> + [CLK_NOCC_DIV2] = &nocc_div2_clk.common.hw,
> + [CLK_NOCC_DIV4] = &nocc_div4_clk.common.hw,
> + [CLK_NOCC_RTC] = &nocc_rtc_clk.common.hw,
> + [CLK_NOCC_CAN] = &nocc_can_div.common.hw,
> + [CLK_QSPI_SCLK] = &qspi_sclk.common.hw,
> + [CLK_QSPI_HCLK] = &qspi_hclk.common.hw,
> + [CLK_I2C0_PCLK] = &i2c0_pclk.common.hw,
> + [CLK_I2C1_PCLK] = &i2c1_pclk.common.hw,
> + [CLK_I2C2_PCLK] = &i2c2_pclk.common.hw,
> + [CLK_I2C3_PCLK] = &i2c3_pclk.common.hw,
> + [CLK_I2C4_PCLK] = &i2c4_pclk.common.hw,
> + [CLK_UART0_PCLK] = &uart0_pclk.common.hw,
> + [CLK_UART1_PCLK] = &uart1_pclk.common.hw,
> + [CLK_UART2_PCLK] = &uart2_pclk.common.hw,
> + [CLK_UART3_PCLK] = &uart3_pclk.common.hw,
> + [CLK_UART4_PCLK] = &uart4_pclk.common.hw,
> + [CLK_SPI0_PCLK] = &spi0_pclk.common.hw,
> + [CLK_SPI1_PCLK] = &spi1_pclk.common.hw,
> + [CLK_SPI2_PCLK] = &spi2_pclk.common.hw,
> + [CLK_SPI3_PCLK] = &spi3_pclk.common.hw,
> + [CLK_GPIO_PCLK] = &gpio_pclk.common.hw,
> + [CLK_CAN0_HCLK] = &can0_hclk.common.hw,
> + [CLK_CAN0_CLK] = &can0_clk.common.hw,
> + [CLK_CAN1_HCLK] = &can1_hclk.common.hw,
> + [CLK_CAN1_CLK] = &can1_clk.common.hw,
> + [CLK_CAN0_TIMER_CLK] = &can0_timer_clk.common.hw,
> + [CLK_CAN1_TIMER_CLK] = &can1_timer_clk.common.hw,
> +};
> +
> +static const struct atlantis_prcm_data atlantis_prcm_rcpu_data = {
> + .hws = atlantis_rcpu_clks,
> + .num = ARRAY_SIZE(atlantis_rcpu_clks),
> + .reset_name = "rcpu-reset"
> +};
> +
> +static int atlantis_prcm_clocks_register(struct device *dev,
> + struct regmap *regmap,
> + const struct atlantis_prcm_data *data)
> +{
> + struct clk_hw_onecell_data *clk_data;
> + int i, ret;
> + size_t num_clks = data->num;
> +
> + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, data->num),
> + GFP_KERNEL);
> + if (!clk_data)
> + return -ENOMEM;
> +
> + for (i = 0; i < data->num; i++) {
> + struct clk_hw *hw = data->hws[i];
> + struct atlantis_clk_common *common =
> + hw_to_atlantis_clk_common(hw);
> + common->regmap = regmap;
> +
> + ret = devm_clk_hw_register(dev, hw);
> +
Remove newline
> + if (ret)
> + return ret;
> +
> + clk_data->hws[common->clkid] = hw;
> + }
> +
> + clk_data->num = num_clks;
> +
> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
> +
> + return ret;
These 3 lines can be simplified to return devm_..();
Brian
More information about the linux-riscv
mailing list