0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/bits.h>
0014 #include <linux/clk-provider.h>
0015 #include <linux/err.h>
0016 #include <linux/io.h>
0017 #include <linux/kernel.h>
0018 #include <linux/slab.h>
0019 #include <linux/types.h>
0020
0021 #include "clk.h"
0022
0023 #define PLLP_INDEX 4
0024 #define PLLX_INDEX 8
0025
0026 #define SUPER_CDIV_ENB BIT(31)
0027
0028 #define TSENSOR_SLOWDOWN BIT(23)
0029
0030 static struct tegra_clk_super_mux *cclk_super;
0031 static bool cclk_on_pllx;
0032
0033 static u8 cclk_super_get_parent(struct clk_hw *hw)
0034 {
0035 return tegra_clk_super_ops.get_parent(hw);
0036 }
0037
0038 static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
0039 {
0040 return tegra_clk_super_ops.set_parent(hw, index);
0041 }
0042
0043 static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
0044 unsigned long parent_rate)
0045 {
0046 return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
0047 }
0048
0049 static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
0050 unsigned long parent_rate)
0051 {
0052 struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
0053 u32 val = readl_relaxed(super->reg);
0054 unsigned int div2;
0055
0056
0057 if (val & TSENSOR_SLOWDOWN)
0058 div2 = 1;
0059 else
0060 div2 = 0;
0061
0062 if (cclk_super_get_parent(hw) == PLLX_INDEX)
0063 return parent_rate >> div2;
0064
0065 return tegra_clk_super_ops.recalc_rate(hw, parent_rate) >> div2;
0066 }
0067
0068 static int cclk_super_determine_rate(struct clk_hw *hw,
0069 struct clk_rate_request *req)
0070 {
0071 struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
0072 struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
0073 struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
0074 unsigned long pllp_rate;
0075 long rate = req->rate;
0076
0077 if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
0078 return -EINVAL;
0079
0080
0081
0082
0083
0084 pllp_rate = clk_hw_get_rate(pllp_hw);
0085
0086 if (rate <= pllp_rate) {
0087 if (super->flags & TEGRA20_SUPER_CLK)
0088 rate = pllp_rate;
0089 else
0090 rate = tegra_clk_super_ops.round_rate(hw, rate,
0091 &pllp_rate);
0092
0093 req->best_parent_rate = pllp_rate;
0094 req->best_parent_hw = pllp_hw;
0095 req->rate = rate;
0096 } else {
0097 rate = clk_hw_round_rate(pllx_hw, rate);
0098 req->best_parent_rate = rate;
0099 req->best_parent_hw = pllx_hw;
0100 req->rate = rate;
0101 }
0102
0103 if (WARN_ON_ONCE(rate <= 0))
0104 return -EINVAL;
0105
0106 return 0;
0107 }
0108
0109 static const struct clk_ops tegra_cclk_super_ops = {
0110 .get_parent = cclk_super_get_parent,
0111 .set_parent = cclk_super_set_parent,
0112 .set_rate = cclk_super_set_rate,
0113 .recalc_rate = cclk_super_recalc_rate,
0114 .determine_rate = cclk_super_determine_rate,
0115 };
0116
0117 static const struct clk_ops tegra_cclk_super_mux_ops = {
0118 .get_parent = cclk_super_get_parent,
0119 .set_parent = cclk_super_set_parent,
0120 .determine_rate = cclk_super_determine_rate,
0121 };
0122
0123 struct clk *tegra_clk_register_super_cclk(const char *name,
0124 const char * const *parent_names, u8 num_parents,
0125 unsigned long flags, void __iomem *reg, u8 clk_super_flags,
0126 spinlock_t *lock)
0127 {
0128 struct tegra_clk_super_mux *super;
0129 struct clk *clk;
0130 struct clk_init_data init;
0131 u32 val;
0132
0133 if (WARN_ON(cclk_super))
0134 return ERR_PTR(-EBUSY);
0135
0136 super = kzalloc(sizeof(*super), GFP_KERNEL);
0137 if (!super)
0138 return ERR_PTR(-ENOMEM);
0139
0140 init.name = name;
0141 init.flags = flags;
0142 init.parent_names = parent_names;
0143 init.num_parents = num_parents;
0144
0145 super->reg = reg;
0146 super->lock = lock;
0147 super->width = 4;
0148 super->flags = clk_super_flags;
0149 super->hw.init = &init;
0150
0151 if (super->flags & TEGRA20_SUPER_CLK) {
0152 init.ops = &tegra_cclk_super_mux_ops;
0153 } else {
0154 init.ops = &tegra_cclk_super_ops;
0155
0156 super->frac_div.reg = reg + 4;
0157 super->frac_div.shift = 16;
0158 super->frac_div.width = 8;
0159 super->frac_div.frac_width = 1;
0160 super->frac_div.lock = lock;
0161 super->div_ops = &tegra_clk_frac_div_ops;
0162 }
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187 val = readl_relaxed(reg + 4);
0188 val &= ~SUPER_CDIV_ENB;
0189 writel_relaxed(val, reg + 4);
0190
0191 clk = clk_register(NULL, &super->hw);
0192 if (IS_ERR(clk))
0193 kfree(super);
0194 else
0195 cclk_super = super;
0196
0197 return clk;
0198 }
0199
0200 int tegra_cclk_pre_pllx_rate_change(void)
0201 {
0202 if (IS_ERR_OR_NULL(cclk_super))
0203 return -EINVAL;
0204
0205 if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
0206 cclk_on_pllx = true;
0207 else
0208 cclk_on_pllx = false;
0209
0210
0211
0212
0213
0214 if (cclk_on_pllx)
0215 cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
0216
0217 return 0;
0218 }
0219
0220 void tegra_cclk_post_pllx_rate_change(void)
0221 {
0222 if (cclk_on_pllx)
0223 cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
0224 }