Add driver for clock controller in Tenstorrent Atlantis SoC. This version
of the driver coves clocks from RCPU syscon.
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@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-ccu.c | 939 ++++++++++++++++++++++++++++++
include/soc/tenstorrent/atlantis-syscon.h | 27 +
7 files changed, 986 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index cf7c8e2153dc..31c3e5bcb32d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22537,6 +22537,7 @@ T: git https://github.com/tenstorrent/linux.git
F: Documentation/devicetree/bindings/riscv/tenstorrent.yaml
F: Documentation/devicetree/bindings/soc/tenstorrent/tenstorrent,atlantis-syscon.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-syscon.h
F: include/soc/tenstorrent/
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3a1611008e48..643084887257 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -542,6 +542,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 61ec08404442..f88c116d315f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -156,6 +156,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..6bcef9e4feb4
--- /dev/null
+++ b/drivers/clk/tenstorrent/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config TENSTORRENT_ATLANTIS_CCU
+ tristate "Support for Tenstorrent Atlantis Clock Controllers"
+ 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..cc4fc01df75b
--- /dev/null
+++ b/drivers/clk/tenstorrent/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_TENSTORRENT_ATLANTIS_CCU) += atlantis-ccu.o
diff --git a/drivers/clk/tenstorrent/atlantis-ccu.c b/drivers/clk/tenstorrent/atlantis-ccu.c
new file mode 100644
index 000000000000..a4ad2fc3b621
--- /dev/null
+++ b/drivers/clk/tenstorrent/atlantis-ccu.c
@@ -0,0 +1,939 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Tenstorrent
+ */
+
+#include <dt-bindings/clock/tenstorrent,atlantis-syscon.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <soc/tenstorrent/atlantis-syscon.h>
+
+struct atlantis_clk_common {
+ int clkid;
+ struct regmap *regmap;
+ struct clk_hw hw;
+};
+
+#define hw_to_atlantis_clk_common(_hw) \
+ 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 + a 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;
+}
+
+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,
+ 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);
+}
+
+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)
+{
+ 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);
+}
+
+static int atlantis_clk_gate_enable(struct clk_hw *hw)
+{
+ atlantis_clk_gate_endisable(hw, 1);
+
+ return 0;
+}
+
+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 ? 1 : 0;
+}
+
+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);
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, val + 1);
+}
+
+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);
+ return (unsigned long)rate;
+}
+
+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 and Gate clk is enabled */
+ return !!(en_val & PLL_CFG_EN_BIT) && !!(val & PLL_CFG_LOCK_BIT) &&
+ !!(cg_val && pll->config.cg_reg_enable);
+}
+
+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 and Gate clk is enabled */
+ if ((en_val & PLL_CFG_EN_BIT) && (val & PLL_CFG_LOCK_BIT) &&
+ (cg_val && pll->config.cg_reg_enable) &&
+ !(val & PLL_CFG_BYPASS_BIT)) {
+ 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);
+ 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;
+}
+
+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);
+ }
+ }
+
+ if (need_enable) {
+ regmap_read(gate->common.regmap, gate->config.reg_offset, ®);
+
+ if (!(reg & gate->config.enable)) {
+ 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);
+ }
+}
+
+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);
+}
+
+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);
+ }
+ }
+}
+
+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_ccu_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xFFFC,
+ .cache_type = REGCACHE_NONE,
+};
+struct atlantis_ccu {
+ struct device *dev;
+
+ struct regmap *regmap;
+ void __iomem *base;
+
+ struct clk_hw_onecell_data *clk_data;
+};
+
+struct atlantis_ccu_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, BIT(7),
+ CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_UNGATE |
+ 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_SET_RATE_NO_REPARENT | CLK_SET_RATE_UNGATE |
+ 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_ccu_data atlantis_ccu_rcpu_data = {
+ .hws = atlantis_rcpu_clks,
+ .num = ARRAY_SIZE(atlantis_rcpu_clks),
+ .reset_name = "rcpu-reset"
+};
+
+static int atlantis_ccu_clocks_register(struct device *dev,
+ struct atlantis_ccu *ccu,
+ const struct atlantis_ccu_data *data)
+{
+ struct regmap *regmap = ccu->regmap;
+ 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;
+
+ ccu->clk_data = clk_data;
+
+ for (i = 0; i < data->num; i++) {
+ struct clk_hw *hw = data->hws[i];
+ const char *name = hw->init->name;
+ struct atlantis_clk_common *common =
+ hw_to_atlantis_clk_common(hw);
+ common->regmap = regmap;
+
+ ret = devm_clk_hw_register(dev, hw);
+
+ if (ret) {
+ dev_err(dev, "Cannot register clock %d - %s\n", i,
+ name);
+ 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);
+ if (ret)
+ dev_err(dev, "failed to add clock hardware provider (%d)\n",
+ ret);
+
+ return ret;
+}
+
+static void atlantis_cadev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ kfree(to_atlantis_ccu_adev(adev));
+}
+
+static void atlantis_adev_unregister(void *data)
+{
+ struct auxiliary_device *adev = data;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static int atlantis_ccu_adev_register(struct device *dev,
+ struct atlantis_ccu *ccu,
+ const struct atlantis_ccu_data *data,
+ const char *adev_name)
+{
+ struct atlantis_ccu_adev *cadev;
+ struct auxiliary_device *adev;
+ int ret;
+
+ cadev = kzalloc(sizeof(*cadev), GFP_KERNEL);
+ if (!cadev)
+ return -ENOMEM;
+
+ cadev->regmap = ccu->regmap;
+
+ adev = &cadev->adev;
+ adev->name = adev_name;
+ adev->dev.parent = dev;
+ adev->dev.release = atlantis_cadev_release;
+ adev->dev.of_node = dev->of_node;
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ goto err_free_cadev;
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(dev, atlantis_adev_unregister, adev);
+
+err_free_cadev:
+ kfree(cadev);
+
+ return ret;
+}
+static int atlantis_ccu_probe(struct platform_device *pdev)
+{
+ const struct atlantis_ccu_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ struct atlantis_ccu *ccu = devm_kzalloc(dev, sizeof(*ccu), GFP_KERNEL);
+
+ if (!ccu)
+ return -ENOMEM;
+
+ ccu->dev = dev;
+
+ ccu->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ccu->base))
+ return dev_err_probe(dev, PTR_ERR(ccu->base),
+ "Failed to map registers\n");
+
+ ccu->regmap = devm_regmap_init_mmio(dev, ccu->base,
+ &atlantis_ccu_regmap_config);
+ if (IS_ERR(ccu->regmap))
+ return dev_err_probe(dev, PTR_ERR(ccu->regmap),
+ "Failed to init regmap\n");
+
+ data = of_device_get_match_data(dev);
+
+ ret = atlantis_ccu_clocks_register(dev, ccu, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register clocks\n");
+
+ ret = atlantis_ccu_adev_register(dev, ccu, data, data->reset_name);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register resets\n");
+
+ return 0;
+}
+
+static const struct of_device_id atlantis_ccu_of_match[] = {
+ {
+ .compatible = "tenstorrent,atlantis-syscon-rcpu",
+ .data = &atlantis_ccu_rcpu_data,
+ },
+ {}
+
+};
+MODULE_DEVICE_TABLE(of, atlantis_ccu_of_match);
+
+static struct platform_driver atlantis_ccu_driver = {
+ .probe = atlantis_ccu_probe,
+ .driver = {
+ .name = "atlantis-ccu",
+ .of_match_table = atlantis_ccu_of_match,
+ },
+};
+module_platform_driver(atlantis_ccu_driver);
+
+MODULE_DESCRIPTION("Tenstorrent Atlantis Clock Controller Driver");
+MODULE_AUTHOR("Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/soc/tenstorrent/atlantis-syscon.h b/include/soc/tenstorrent/atlantis-syscon.h
index 2c6387e5c21a..541b8c2f56c1 100644
--- a/include/soc/tenstorrent/atlantis-syscon.h
+++ b/include/soc/tenstorrent/atlantis-syscon.h
@@ -16,6 +16,17 @@ struct atlantis_ccu_adev {
#define to_atlantis_ccu_adev(_adev) \
container_of((_adev), struct atlantis_ccu_adev, adev)
+/* 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
+
/* RCPU Reset Register Offsets */
#define RCPU_BLK_RST_REG 0x001c
#define LSIO_BLK_RST_REG 0x0020
@@ -23,4 +34,20 @@ struct atlantis_ccu_adev {
#define PCIE_SUBS_RST_REG 0x0000
#define MM_RSTN_REG 0x0014
+/* 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
+
#endif
--
2.43.0
Hi Anirudh,
kernel test robot noticed the following build errors:
[auto build test ERROR on 9448598b22c50c8a5bb77a9103e2d49f134c9578]
url: https://github.com/intel-lab-lkp/linux/commits/Anirudh-Srinivasan/dt-bindings-soc-tenstorrent-Add-tenstorrent-atlantis-syscon/20260123-064135
base: 9448598b22c50c8a5bb77a9103e2d49f134c9578
patch link: https://lore.kernel.org/r/20260122-atlantis-clocks-v2-3-c66371639e66%40oss.tenstorrent.com
patch subject: [PATCH v2 3/3] clk: tenstorrent: Add Atlantis clock controller driver
config: um-allmodconfig (https://download.01.org/0day-ci/archive/20260123/202601231918.PQTTcbRG-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260123/202601231918.PQTTcbRG-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601231918.PQTTcbRG-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from drivers/clk/tenstorrent/atlantis-ccu.c:12:
In file included from include/linux/regmap.h:20:
In file included from include/linux/iopoll.h:14:
In file included from include/linux/io.h:12:
In file included from arch/um/include/asm/io.h:24:
include/asm-generic/io.h:1209:55: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
1209 | return (port > MMIO_UPPER_LIMIT) ? NULL : PCI_IOBASE + port;
| ~~~~~~~~~~ ^
>> drivers/clk/tenstorrent/atlantis-ccu.c:832:2: error: call to undeclared function 'kfree'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
832 | kfree(to_atlantis_ccu_adev(adev));
| ^
>> drivers/clk/tenstorrent/atlantis-ccu.c:852:10: error: call to undeclared function 'kzalloc'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
852 | cadev = kzalloc(sizeof(*cadev), GFP_KERNEL);
| ^
>> drivers/clk/tenstorrent/atlantis-ccu.c:852:8: error: incompatible integer to pointer conversion assigning to 'struct atlantis_ccu_adev *' from 'int' [-Wint-conversion]
852 | cadev = kzalloc(sizeof(*cadev), GFP_KERNEL);
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/clk/tenstorrent/atlantis-ccu.c:877:2: error: call to undeclared function 'kfree'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
877 | kfree(cadev);
| ^
1 warning and 4 errors generated.
vim +/kfree +832 drivers/clk/tenstorrent/atlantis-ccu.c
827
828 static void atlantis_cadev_release(struct device *dev)
829 {
830 struct auxiliary_device *adev = to_auxiliary_dev(dev);
831
> 832 kfree(to_atlantis_ccu_adev(adev));
833 }
834
835 static void atlantis_adev_unregister(void *data)
836 {
837 struct auxiliary_device *adev = data;
838
839 auxiliary_device_delete(adev);
840 auxiliary_device_uninit(adev);
841 }
842
843 static int atlantis_ccu_adev_register(struct device *dev,
844 struct atlantis_ccu *ccu,
845 const struct atlantis_ccu_data *data,
846 const char *adev_name)
847 {
848 struct atlantis_ccu_adev *cadev;
849 struct auxiliary_device *adev;
850 int ret;
851
> 852 cadev = kzalloc(sizeof(*cadev), GFP_KERNEL);
853 if (!cadev)
854 return -ENOMEM;
855
856 cadev->regmap = ccu->regmap;
857
858 adev = &cadev->adev;
859 adev->name = adev_name;
860 adev->dev.parent = dev;
861 adev->dev.release = atlantis_cadev_release;
862 adev->dev.of_node = dev->of_node;
863
864 ret = auxiliary_device_init(adev);
865 if (ret)
866 goto err_free_cadev;
867
868 ret = auxiliary_device_add(adev);
869 if (ret) {
870 auxiliary_device_uninit(adev);
871 return ret;
872 }
873
874 return devm_add_action_or_reset(dev, atlantis_adev_unregister, adev);
875
876 err_free_cadev:
877 kfree(cadev);
878
879 return ret;
880 }
881 static int atlantis_ccu_probe(struct platform_device *pdev)
882 {
883 const struct atlantis_ccu_data *data;
884 struct device *dev = &pdev->dev;
885 int ret;
886
887 struct atlantis_ccu *ccu = devm_kzalloc(dev, sizeof(*ccu), GFP_KERNEL);
888
889 if (!ccu)
890 return -ENOMEM;
891
892 ccu->dev = dev;
893
894 ccu->base = devm_platform_ioremap_resource(pdev, 0);
895 if (IS_ERR(ccu->base))
896 return dev_err_probe(dev, PTR_ERR(ccu->base),
897 "Failed to map registers\n");
898
899 ccu->regmap = devm_regmap_init_mmio(dev, ccu->base,
900 &atlantis_ccu_regmap_config);
901 if (IS_ERR(ccu->regmap))
902 return dev_err_probe(dev, PTR_ERR(ccu->regmap),
903 "Failed to init regmap\n");
904
905 data = of_device_get_match_data(dev);
906
907 ret = atlantis_ccu_clocks_register(dev, ccu, data);
908 if (ret)
909 return dev_err_probe(dev, ret, "failed to register clocks\n");
910
911 ret = atlantis_ccu_adev_register(dev, ccu, data, data->reset_name);
912 if (ret)
913 return dev_err_probe(dev, ret, "failed to register resets\n");
914
915 return 0;
916 }
917
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Anirudh,
kernel test robot noticed the following build errors:
[auto build test ERROR on 9448598b22c50c8a5bb77a9103e2d49f134c9578]
url: https://github.com/intel-lab-lkp/linux/commits/Anirudh-Srinivasan/dt-bindings-soc-tenstorrent-Add-tenstorrent-atlantis-syscon/20260123-064135
base: 9448598b22c50c8a5bb77a9103e2d49f134c9578
patch link: https://lore.kernel.org/r/20260122-atlantis-clocks-v2-3-c66371639e66%40oss.tenstorrent.com
patch subject: [PATCH v2 3/3] clk: tenstorrent: Add Atlantis clock controller driver
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20260123/202601231402.YbzFbWJb-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260123/202601231402.YbzFbWJb-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601231402.YbzFbWJb-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/clk/tenstorrent/atlantis-ccu.c: In function 'atlantis_cadev_release':
>> drivers/clk/tenstorrent/atlantis-ccu.c:832:9: error: implicit declaration of function 'kfree' [-Wimplicit-function-declaration]
832 | kfree(to_atlantis_ccu_adev(adev));
| ^~~~~
drivers/clk/tenstorrent/atlantis-ccu.c: In function 'atlantis_ccu_adev_register':
>> drivers/clk/tenstorrent/atlantis-ccu.c:852:17: error: implicit declaration of function 'kzalloc' [-Wimplicit-function-declaration]
852 | cadev = kzalloc(sizeof(*cadev), GFP_KERNEL);
| ^~~~~~~
>> drivers/clk/tenstorrent/atlantis-ccu.c:852:15: error: assignment to 'struct atlantis_ccu_adev *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
852 | cadev = kzalloc(sizeof(*cadev), GFP_KERNEL);
| ^
vim +/kfree +832 drivers/clk/tenstorrent/atlantis-ccu.c
827
828 static void atlantis_cadev_release(struct device *dev)
829 {
830 struct auxiliary_device *adev = to_auxiliary_dev(dev);
831
> 832 kfree(to_atlantis_ccu_adev(adev));
833 }
834
835 static void atlantis_adev_unregister(void *data)
836 {
837 struct auxiliary_device *adev = data;
838
839 auxiliary_device_delete(adev);
840 auxiliary_device_uninit(adev);
841 }
842
843 static int atlantis_ccu_adev_register(struct device *dev,
844 struct atlantis_ccu *ccu,
845 const struct atlantis_ccu_data *data,
846 const char *adev_name)
847 {
848 struct atlantis_ccu_adev *cadev;
849 struct auxiliary_device *adev;
850 int ret;
851
> 852 cadev = kzalloc(sizeof(*cadev), GFP_KERNEL);
853 if (!cadev)
854 return -ENOMEM;
855
856 cadev->regmap = ccu->regmap;
857
858 adev = &cadev->adev;
859 adev->name = adev_name;
860 adev->dev.parent = dev;
861 adev->dev.release = atlantis_cadev_release;
862 adev->dev.of_node = dev->of_node;
863
864 ret = auxiliary_device_init(adev);
865 if (ret)
866 goto err_free_cadev;
867
868 ret = auxiliary_device_add(adev);
869 if (ret) {
870 auxiliary_device_uninit(adev);
871 return ret;
872 }
873
874 return devm_add_action_or_reset(dev, atlantis_adev_unregister, adev);
875
876 err_free_cadev:
877 kfree(cadev);
878
879 return ret;
880 }
881 static int atlantis_ccu_probe(struct platform_device *pdev)
882 {
883 const struct atlantis_ccu_data *data;
884 struct device *dev = &pdev->dev;
885 int ret;
886
887 struct atlantis_ccu *ccu = devm_kzalloc(dev, sizeof(*ccu), GFP_KERNEL);
888
889 if (!ccu)
890 return -ENOMEM;
891
892 ccu->dev = dev;
893
894 ccu->base = devm_platform_ioremap_resource(pdev, 0);
895 if (IS_ERR(ccu->base))
896 return dev_err_probe(dev, PTR_ERR(ccu->base),
897 "Failed to map registers\n");
898
899 ccu->regmap = devm_regmap_init_mmio(dev, ccu->base,
900 &atlantis_ccu_regmap_config);
901 if (IS_ERR(ccu->regmap))
902 return dev_err_probe(dev, PTR_ERR(ccu->regmap),
903 "Failed to init regmap\n");
904
905 data = of_device_get_match_data(dev);
906
907 ret = atlantis_ccu_clocks_register(dev, ccu, data);
908 if (ret)
909 return dev_err_probe(dev, ret, "failed to register clocks\n");
910
911 ret = atlantis_ccu_adev_register(dev, ccu, data, data->reset_name);
912 if (ret)
913 return dev_err_probe(dev, ret, "failed to register resets\n");
914
915 return 0;
916 }
917
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.