^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Based on clk-super.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Based on older tegra20-cpufreq driver by Colin Cross <ccross@google.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (C) 2010 Google, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Author: Dmitry Osipenko <digetx@gmail.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Copyright (C) 2019 GRATE-DRIVER project
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/bits.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include "clk.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define PLLP_INDEX 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define PLLX_INDEX 8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define SUPER_CDIV_ENB BIT(31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static struct tegra_clk_super_mux *cclk_super;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static bool cclk_on_pllx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) static u8 cclk_super_get_parent(struct clk_hw *hw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) return tegra_clk_super_ops.get_parent(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) return tegra_clk_super_ops.set_parent(hw, index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) if (cclk_super_get_parent(hw) == PLLX_INDEX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) return parent_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) return tegra_clk_super_ops.recalc_rate(hw, parent_rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) static int cclk_super_determine_rate(struct clk_hw *hw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) struct clk_rate_request *req)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) unsigned long pllp_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) long rate = req->rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) * Switch parent to PLLP for all CCLK rates that are suitable for PLLP.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) * PLLX will be disabled in this case, saving some power.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) pllp_rate = clk_hw_get_rate(pllp_hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) if (rate <= pllp_rate) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) if (super->flags & TEGRA20_SUPER_CLK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) rate = pllp_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) rate = tegra_clk_super_ops.round_rate(hw, rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) &pllp_rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) req->best_parent_rate = pllp_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) req->best_parent_hw = pllp_hw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) req->rate = rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) rate = clk_hw_round_rate(pllx_hw, rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) req->best_parent_rate = rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) req->best_parent_hw = pllx_hw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) req->rate = rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (WARN_ON_ONCE(rate <= 0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) static const struct clk_ops tegra_cclk_super_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) .get_parent = cclk_super_get_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .set_parent = cclk_super_set_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) .set_rate = cclk_super_set_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) .recalc_rate = cclk_super_recalc_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) .determine_rate = cclk_super_determine_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static const struct clk_ops tegra_cclk_super_mux_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) .get_parent = cclk_super_get_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) .set_parent = cclk_super_set_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) .determine_rate = cclk_super_determine_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) struct clk *tegra_clk_register_super_cclk(const char *name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) const char * const *parent_names, u8 num_parents,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) unsigned long flags, void __iomem *reg, u8 clk_super_flags,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) spinlock_t *lock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) struct tegra_clk_super_mux *super;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) struct clk *clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) struct clk_init_data init;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) u32 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (WARN_ON(cclk_super))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) return ERR_PTR(-EBUSY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) super = kzalloc(sizeof(*super), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) if (!super)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) return ERR_PTR(-ENOMEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) init.name = name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) init.flags = flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) init.parent_names = parent_names;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) init.num_parents = num_parents;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) super->reg = reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) super->lock = lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) super->width = 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) super->flags = clk_super_flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) super->hw.init = &init;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if (super->flags & TEGRA20_SUPER_CLK) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) init.ops = &tegra_cclk_super_mux_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) init.ops = &tegra_cclk_super_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) super->frac_div.reg = reg + 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) super->frac_div.shift = 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) super->frac_div.width = 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) super->frac_div.frac_width = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) super->frac_div.lock = lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) super->div_ops = &tegra_clk_frac_div_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) * Tegra30+ has the following CPUG clock topology:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) * +---+ +-------+ +-+ +-+ +-+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) * PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) * | | +-------+ | | | +---+ | | | | |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) * PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) * ... | | | | | | K | | | | +-------+ | |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) * PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) * +---+ +++ | P | +++ |SKIPPER| +++
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) * ^ | P | ^ +-------+ ^
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) * | | E | | |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) * PLLX_SEL+--+ | R | | OVERHEAT+--+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) * +---+ |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) * |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) * SUPER_CDIV_ENB+--+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) * Tegra20 is similar, but simpler. It doesn't have the divider and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) * thermal DIV2 skipper.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) * At least for now we're not going to use clock-skipper, hence let's
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) * ensure that it is disabled.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) val = readl_relaxed(reg + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) val &= ~SUPER_CDIV_ENB;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) writel_relaxed(val, reg + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) clk = clk_register(NULL, &super->hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) if (IS_ERR(clk))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) kfree(super);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) cclk_super = super;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) int tegra_cclk_pre_pllx_rate_change(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) if (IS_ERR_OR_NULL(cclk_super))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) cclk_on_pllx = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) cclk_on_pllx = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) * CPU needs to be temporarily re-parented away from PLLX if PLLX
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) * changes its rate. PLLP is a safe parent for CPU on all Tegra SoCs.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) if (cclk_on_pllx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) void tegra_cclk_post_pllx_rate_change(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) if (cclk_on_pllx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) }