diff --git a/Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml b/Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml new file mode 100644 index 00000000000000..7fa16526efcea8 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/tenstorrent,atlantis-prcm-rcpu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Tenstorrent Atlantis PRCM (Power, Reset, Clock Management) Module + +maintainers: + - Anirudh Srinivasan + +description: + Multifunctional register block found in Tenstorrent Atlantis SoC whose main + function is to control clocks and resets. This block is instantiated multiple + times in the SoC, each block controls clock and resets for a different + subsystem. RCPU prcm serves low speed IO interfaces. + +properties: + compatible: + enum: + - tenstorrent,atlantis-prcm-rcpu + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + "#clock-cells": + const: 1 + description: + See for valid indices. + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - "#clock-cells" + - "#reset-cells" + +additionalProperties: false + +examples: + - | + clock-controller@a8000000 { + compatible = "tenstorrent,atlantis-prcm-rcpu"; + reg = <0xa8000000 0x10000>; + clocks = <&osc_24m>; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 67db88b04537b4..cc5759e361e077 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22555,8 +22555,12 @@ M: Joel Stanley L: linux-riscv@lists.infradead.org S: Maintained 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 RISC-V THEAD SoC SUPPORT M: Drew Fustini diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3a1611008e48e9..64308488725764 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 61ec08404442b4..f88c116d315ff2 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 00000000000000..9d4391eeeae0fa --- /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 00000000000000..95d87bac7bf58e --- /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 00000000000000..6bedb3e5ec7ac3 --- /dev/null +++ b/drivers/clk/tenstorrent/atlantis-prcm.c @@ -0,0 +1,901 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Tenstorrent Atlantis PRCM Clock Driver + * + * Copyright (c) 2026 Tenstorrent + */ + +#include +#include +#include +#include +#include +#include +#include + +/* 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 + 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_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, 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_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]; + 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 int atlantis_prcm_probe(struct platform_device *pdev) +{ + const struct atlantis_prcm_data *data; + struct auxiliary_device *reset_adev; + struct regmap *regmap; + void __iomem *base; + struct device *dev = &pdev->dev; + int ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "Failed to map registers\n"); + + regmap = devm_regmap_init_mmio(dev, base, &atlantis_prcm_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + data = of_device_get_match_data(dev); + + ret = atlantis_prcm_clocks_register(dev, regmap, data); + if (ret) + return dev_err_probe(dev, ret, "failed to register clocks\n"); + + reset_adev = devm_auxiliary_device_create(dev, data->reset_name, NULL); + if (!reset_adev) + return dev_err_probe(dev, -ENODEV, "failed to register resets\n"); + + return 0; +} + +static const struct of_device_id atlantis_prcm_of_match[] = { + { + .compatible = "tenstorrent,atlantis-prcm-rcpu", + .data = &atlantis_prcm_rcpu_data, + }, + {} + +}; +MODULE_DEVICE_TABLE(of, atlantis_prcm_of_match); + +static struct platform_driver atlantis_prcm_driver = { + .probe = atlantis_prcm_probe, + .driver = { + .name = "atlantis-prcm", + .of_match_table = atlantis_prcm_of_match, + }, +}; +module_platform_driver(atlantis_prcm_driver); + +MODULE_DESCRIPTION("Tenstorrent Atlantis PRCM Clock Controller Driver"); +MODULE_AUTHOR("Anirudh Srinivasan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 6e5d6deffa7d36..cade777174928b 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -324,6 +324,17 @@ config RESET_SUNXI help This enables the reset driver for Allwinner SoCs. +config RESET_TENSTORRENT_ATLANTIS + tristate "Tenstorrent atlantis reset driver" + depends on ARCH_TENSTORRENT || COMPILE_TEST + select AUXILIARY_BUS + default ARCH_TENSTORRENT + help + This enables the driver for the reset controller + present in the Tenstorrent Atlantis SoC. + Enable this option to be able to use hardware + resets on Atalantis based systems. + config RESET_TH1520 tristate "T-HEAD TH1520 reset controller" depends on ARCH_THEAD || COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 9c3e484dfd81a4..a31959da0a8898 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_SPACEMIT) += reset-spacemit.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o +obj-$(CONFIG_RESET_TENSTORRENT_ATLANTIS) += reset-tenstorrent-atlantis.o obj-$(CONFIG_RESET_TH1520) += reset-th1520.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o diff --git a/drivers/reset/reset-tenstorrent-atlantis.c b/drivers/reset/reset-tenstorrent-atlantis.c new file mode 100644 index 00000000000000..ab8be52fdd5e41 --- /dev/null +++ b/drivers/reset/reset-tenstorrent-atlantis.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tenstorrent Atlantis PRCM Reset Driver + * + * Copyright (c) 2026 Tenstorrent + */ + +#include +#include +#include +#include + +/* RCPU Reset Register Offsets */ +#define RCPU_BLK_RST_REG 0x001c +#define LSIO_BLK_RST_REG 0x0020 +#define HSIO_BLK_RST_REG 0x000c +#define PCIE_SUBS_RST_REG 0x0000 +#define MM_RSTN_REG 0x0014 + +struct atlantis_reset_data { + u8 bit; + u16 reg; + bool active_low; +}; + +struct atlantis_reset_controller_data { + const struct atlantis_reset_data *reset_data; + size_t count; +}; + +struct atlantis_reset_controller { + struct reset_controller_dev rcdev; + const struct atlantis_reset_controller_data *data; + struct regmap *regmap; +}; + +static inline struct atlantis_reset_controller * +to_atlantis_reset_controller(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct atlantis_reset_controller, rcdev); +} + +#define RESET_DATA(_reg, _bit, _active_low) \ + { \ + .bit = _bit, .reg = _reg, .active_low = _active_low, \ + } + +static const struct atlantis_reset_data atlantis_rcpu_resets[] = { + [RST_SMNDMA0] = RESET_DATA(RCPU_BLK_RST_REG, 0, true), + [RST_SMNDMA1] = RESET_DATA(RCPU_BLK_RST_REG, 1, true), + [RST_WDT0] = RESET_DATA(RCPU_BLK_RST_REG, 2, true), + [RST_WDT1] = RESET_DATA(RCPU_BLK_RST_REG, 3, true), + [RST_TMR] = RESET_DATA(RCPU_BLK_RST_REG, 4, true), + [RST_PVTC] = RESET_DATA(RCPU_BLK_RST_REG, 12, true), + [RST_PMU] = RESET_DATA(RCPU_BLK_RST_REG, 13, true), + [RST_MAILBOX] = RESET_DATA(RCPU_BLK_RST_REG, 14, true), + [RST_SPACC] = RESET_DATA(RCPU_BLK_RST_REG, 26, true), + [RST_OTP] = RESET_DATA(RCPU_BLK_RST_REG, 28, true), + [RST_TRNG] = RESET_DATA(RCPU_BLK_RST_REG, 29, true), + [RST_CRC] = RESET_DATA(RCPU_BLK_RST_REG, 30, true), + [RST_QSPI] = RESET_DATA(LSIO_BLK_RST_REG, 0, true), + [RST_I2C0] = RESET_DATA(LSIO_BLK_RST_REG, 1, true), + [RST_I2C1] = RESET_DATA(LSIO_BLK_RST_REG, 2, true), + [RST_I2C2] = RESET_DATA(LSIO_BLK_RST_REG, 3, true), + [RST_I2C3] = RESET_DATA(LSIO_BLK_RST_REG, 4, true), + [RST_I2C4] = RESET_DATA(LSIO_BLK_RST_REG, 5, true), + [RST_UART0] = RESET_DATA(LSIO_BLK_RST_REG, 6, true), + [RST_UART1] = RESET_DATA(LSIO_BLK_RST_REG, 7, true), + [RST_UART2] = RESET_DATA(LSIO_BLK_RST_REG, 8, true), + [RST_UART3] = RESET_DATA(LSIO_BLK_RST_REG, 9, true), + [RST_UART4] = RESET_DATA(LSIO_BLK_RST_REG, 10, true), + [RST_SPI0] = RESET_DATA(LSIO_BLK_RST_REG, 11, true), + [RST_SPI1] = RESET_DATA(LSIO_BLK_RST_REG, 12, true), + [RST_SPI2] = RESET_DATA(LSIO_BLK_RST_REG, 13, true), + [RST_SPI3] = RESET_DATA(LSIO_BLK_RST_REG, 14, true), + [RST_GPIO] = RESET_DATA(LSIO_BLK_RST_REG, 15, true), + [RST_CAN0] = RESET_DATA(LSIO_BLK_RST_REG, 17, true), + [RST_CAN1] = RESET_DATA(LSIO_BLK_RST_REG, 18, true), + [RST_I2S0] = RESET_DATA(LSIO_BLK_RST_REG, 19, true), + [RST_I2S1] = RESET_DATA(LSIO_BLK_RST_REG, 20, true), + +}; + +static const struct atlantis_reset_controller_data atlantis_rcpu_reset_data = { + .reset_data = atlantis_rcpu_resets, + .count = ARRAY_SIZE(atlantis_rcpu_resets), +}; + +static int atlantis_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + unsigned int val; + struct atlantis_reset_controller *rst = + to_atlantis_reset_controller(rcdev); + const struct atlantis_reset_data *data = &rst->data->reset_data[id]; + unsigned int mask = BIT(data->bit); + struct regmap *regmap = rst->regmap; + + if (data->active_low ^ assert) + val = mask; + else + val = 0; + + return regmap_update_bits(regmap, data->reg, mask, val); +} + +static int atlantis_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return atlantis_reset_update(rcdev, id, true); +} + +static int atlantis_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return atlantis_reset_update(rcdev, id, false); +} + +static const struct reset_control_ops atlantis_reset_control_ops = { + .assert = atlantis_reset_assert, + .deassert = atlantis_reset_deassert, +}; + +static int +atlantis_reset_controller_register(struct device *dev, + struct atlantis_reset_controller *controller) +{ + struct reset_controller_dev *rcdev = &controller->rcdev; + + rcdev->ops = &atlantis_reset_control_ops; + rcdev->owner = THIS_MODULE; + rcdev->of_node = dev->of_node; + rcdev->nr_resets = controller->data->count; + + return devm_reset_controller_register(dev, &controller->rcdev); +} +static int atlantis_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct atlantis_reset_controller *controller; + struct device *dev = &adev->dev; + struct regmap *regmap; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); + if (!controller) + return -ENOMEM; + controller->data = + (const struct atlantis_reset_controller_data *)id->driver_data; + controller->regmap = regmap; + + return atlantis_reset_controller_register(dev, controller); +} + +static const struct auxiliary_device_id atlantis_reset_ids[] = { + { .name = "atlantis_prcm.rcpu-reset", + .driver_data = (kernel_ulong_t)&atlantis_rcpu_reset_data }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, atlantis_reset_ids); + +static struct auxiliary_driver atlantis_reset_driver = { + .probe = atlantis_reset_probe, + .id_table = atlantis_reset_ids, +}; +module_auxiliary_driver(atlantis_reset_driver); + +MODULE_AUTHOR("Anirudh Srinivasan "); +MODULE_DESCRIPTION("Atlantis PRCM reset controller driver"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h b/include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h new file mode 100644 index 00000000000000..c1c875e016f8e3 --- /dev/null +++ b/include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Tenstorrent Atlantis PRCM Clock and Reset Indices + * + * Copyright (c) 2026 Tenstorrent + */ + +#ifndef _DT_BINDINGS_ATLANTIS_PRCM_RCPU_H +#define _DT_BINDINGS_ATLANTIS_PRCM_RCPU_H + +/* + * RCPU Domain Clock IDs + */ +#define CLK_RCPU_PLL 0 +#define CLK_RCPU_ROOT 1 +#define CLK_RCPU_DIV2 2 +#define CLK_RCPU_DIV4 3 +#define CLK_RCPU_RTC 4 +#define CLK_SMNDMA0_ACLK 5 +#define CLK_SMNDMA1_ACLK 6 +#define CLK_WDT0_PCLK 7 +#define CLK_WDT1_PCLK 8 +#define CLK_TIMER_PCLK 9 +#define CLK_PVTC_PCLK 10 +#define CLK_PMU_PCLK 11 +#define CLK_MAILBOX_HCLK 12 +#define CLK_SEC_SPACC_HCLK 13 +#define CLK_SEC_OTP_HCLK 14 +#define CLK_TRNG_PCLK 15 +#define CLK_SEC_CRC_HCLK 16 +#define CLK_SMN_HCLK 17 +#define CLK_AHB0_HCLK 18 +#define CLK_SMN_PCLK 19 +#define CLK_SMN_CLK 20 +#define CLK_SCRATCHPAD_CLK 21 +#define CLK_RCPU_CORE_CLK 22 +#define CLK_RCPU_ROM_CLK 23 +#define CLK_OTP_LOAD_CLK 24 +#define CLK_NOC_PLL 25 +#define CLK_NOCC_CLK 26 +#define CLK_NOCC_DIV2 27 +#define CLK_NOCC_DIV4 28 +#define CLK_NOCC_RTC 29 +#define CLK_NOCC_CAN 30 +#define CLK_QSPI_SCLK 31 +#define CLK_QSPI_HCLK 32 +#define CLK_I2C0_PCLK 33 +#define CLK_I2C1_PCLK 34 +#define CLK_I2C2_PCLK 35 +#define CLK_I2C3_PCLK 36 +#define CLK_I2C4_PCLK 37 +#define CLK_UART0_PCLK 38 +#define CLK_UART1_PCLK 39 +#define CLK_UART2_PCLK 40 +#define CLK_UART3_PCLK 41 +#define CLK_UART4_PCLK 42 +#define CLK_SPI0_PCLK 43 +#define CLK_SPI1_PCLK 44 +#define CLK_SPI2_PCLK 45 +#define CLK_SPI3_PCLK 46 +#define CLK_GPIO_PCLK 47 +#define CLK_CAN0_HCLK 48 +#define CLK_CAN0_CLK 49 +#define CLK_CAN1_HCLK 50 +#define CLK_CAN1_CLK 51 +#define CLK_CAN0_TIMER_CLK 52 +#define CLK_CAN1_TIMER_CLK 53 + +/* RCPU domain reset */ +#define RST_SMNDMA0 0 +#define RST_SMNDMA1 1 +#define RST_WDT0 2 +#define RST_WDT1 3 +#define RST_TMR 4 +#define RST_PVTC 5 +#define RST_PMU 6 +#define RST_MAILBOX 7 +#define RST_SPACC 8 +#define RST_OTP 9 +#define RST_TRNG 10 +#define RST_CRC 11 +#define RST_QSPI 12 +#define RST_I2C0 13 +#define RST_I2C1 14 +#define RST_I2C2 15 +#define RST_I2C3 16 +#define RST_I2C4 17 +#define RST_UART0 18 +#define RST_UART1 19 +#define RST_UART2 20 +#define RST_UART3 21 +#define RST_UART4 22 +#define RST_SPI0 23 +#define RST_SPI1 24 +#define RST_SPI2 25 +#define RST_SPI3 26 +#define RST_GPIO 27 +#define RST_CAN0 28 +#define RST_CAN1 29 +#define RST_I2S0 30 +#define RST_I2S1 31 + +#endif /* _DT_BINDINGS_ATLANTIS_PRCM_RCPU_H */