1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/io.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/err.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/clk-provider.h> |
12 | |
13 | #include "clk.h" |
14 | |
15 | #define SUPER_STATE_IDLE 0 |
16 | #define SUPER_STATE_RUN 1 |
17 | #define SUPER_STATE_IRQ 2 |
18 | #define SUPER_STATE_FIQ 3 |
19 | |
20 | #define SUPER_STATE_SHIFT 28 |
21 | #define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \ |
22 | BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ)) \ |
23 | << SUPER_STATE_SHIFT) |
24 | |
25 | #define SUPER_LP_DIV2_BYPASS (1 << 16) |
26 | |
27 | #define super_state(s) (BIT(s) << SUPER_STATE_SHIFT) |
28 | #define super_state_to_src_shift(m, s) ((m->width * s)) |
29 | #define super_state_to_src_mask(m) (((1 << m->width) - 1)) |
30 | |
31 | #define CCLK_SRC_PLLP_OUT0 4 |
32 | #define CCLK_SRC_PLLP_OUT4 5 |
33 | |
34 | static u8 clk_super_get_parent(struct clk_hw *hw) |
35 | { |
36 | struct tegra_clk_super_mux *mux = to_clk_super_mux(hw); |
37 | u32 val, state; |
38 | u8 source, shift; |
39 | |
40 | val = readl_relaxed(mux->reg); |
41 | |
42 | state = val & SUPER_STATE_MASK; |
43 | |
44 | BUG_ON((state != super_state(SUPER_STATE_RUN)) && |
45 | (state != super_state(SUPER_STATE_IDLE))); |
46 | shift = (state == super_state(SUPER_STATE_IDLE)) ? |
47 | super_state_to_src_shift(mux, SUPER_STATE_IDLE) : |
48 | super_state_to_src_shift(mux, SUPER_STATE_RUN); |
49 | |
50 | source = (val >> shift) & super_state_to_src_mask(mux); |
51 | |
52 | /* |
53 | * If LP_DIV2_BYPASS is not set and PLLX is current parent then |
54 | * PLLX/2 is the input source to CCLKLP. |
55 | */ |
56 | if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) && |
57 | (source == mux->pllx_index)) |
58 | source = mux->div2_index; |
59 | |
60 | return source; |
61 | } |
62 | |
63 | static int clk_super_set_parent(struct clk_hw *hw, u8 index) |
64 | { |
65 | struct tegra_clk_super_mux *mux = to_clk_super_mux(hw); |
66 | u32 val, state; |
67 | int err = 0; |
68 | u8 parent_index, shift; |
69 | unsigned long flags = 0; |
70 | |
71 | if (mux->lock) |
72 | spin_lock_irqsave(mux->lock, flags); |
73 | |
74 | val = readl_relaxed(mux->reg); |
75 | state = val & SUPER_STATE_MASK; |
76 | BUG_ON((state != super_state(SUPER_STATE_RUN)) && |
77 | (state != super_state(SUPER_STATE_IDLE))); |
78 | shift = (state == super_state(SUPER_STATE_IDLE)) ? |
79 | super_state_to_src_shift(mux, SUPER_STATE_IDLE) : |
80 | super_state_to_src_shift(mux, SUPER_STATE_RUN); |
81 | |
82 | /* |
83 | * For LP mode super-clock switch between PLLX direct |
84 | * and divided-by-2 outputs is allowed only when other |
85 | * than PLLX clock source is current parent. |
86 | */ |
87 | if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) || |
88 | (index == mux->pllx_index))) { |
89 | parent_index = clk_super_get_parent(hw); |
90 | if ((parent_index == mux->div2_index) || |
91 | (parent_index == mux->pllx_index)) { |
92 | err = -EINVAL; |
93 | goto out; |
94 | } |
95 | |
96 | val ^= SUPER_LP_DIV2_BYPASS; |
97 | writel_relaxed(val, mux->reg); |
98 | udelay(2); |
99 | |
100 | if (index == mux->div2_index) |
101 | index = mux->pllx_index; |
102 | } |
103 | |
104 | /* enable PLLP branches to CPU before selecting PLLP source */ |
105 | if ((mux->flags & TEGRA210_CPU_CLK) && |
106 | (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4)) |
107 | tegra_clk_set_pllp_out_cpu(enable: true); |
108 | |
109 | val &= ~((super_state_to_src_mask(mux)) << shift); |
110 | val |= (index & (super_state_to_src_mask(mux))) << shift; |
111 | |
112 | writel_relaxed(val, mux->reg); |
113 | udelay(2); |
114 | |
115 | /* disable PLLP branches to CPU if not used */ |
116 | if ((mux->flags & TEGRA210_CPU_CLK) && |
117 | index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4) |
118 | tegra_clk_set_pllp_out_cpu(enable: false); |
119 | |
120 | out: |
121 | if (mux->lock) |
122 | spin_unlock_irqrestore(lock: mux->lock, flags); |
123 | |
124 | return err; |
125 | } |
126 | |
127 | static void clk_super_mux_restore_context(struct clk_hw *hw) |
128 | { |
129 | int parent_id; |
130 | |
131 | parent_id = clk_hw_get_parent_index(hw); |
132 | if (WARN_ON(parent_id < 0)) |
133 | return; |
134 | |
135 | clk_super_set_parent(hw, index: parent_id); |
136 | } |
137 | |
138 | static const struct clk_ops tegra_clk_super_mux_ops = { |
139 | .determine_rate = clk_hw_determine_rate_no_reparent, |
140 | .get_parent = clk_super_get_parent, |
141 | .set_parent = clk_super_set_parent, |
142 | .restore_context = clk_super_mux_restore_context, |
143 | }; |
144 | |
145 | static int clk_super_determine_rate(struct clk_hw *hw, |
146 | struct clk_rate_request *req) |
147 | { |
148 | struct tegra_clk_super_mux *super = to_clk_super_mux(hw); |
149 | struct clk_hw *div_hw = &super->frac_div.hw; |
150 | unsigned long rate; |
151 | |
152 | __clk_hw_set_clk(dst: div_hw, src: hw); |
153 | |
154 | rate = super->div_ops->round_rate(div_hw, req->rate, |
155 | &req->best_parent_rate); |
156 | if (rate < 0) |
157 | return rate; |
158 | |
159 | req->rate = rate; |
160 | return 0; |
161 | } |
162 | |
163 | static unsigned long clk_super_recalc_rate(struct clk_hw *hw, |
164 | unsigned long parent_rate) |
165 | { |
166 | struct tegra_clk_super_mux *super = to_clk_super_mux(hw); |
167 | struct clk_hw *div_hw = &super->frac_div.hw; |
168 | |
169 | __clk_hw_set_clk(dst: div_hw, src: hw); |
170 | |
171 | return super->div_ops->recalc_rate(div_hw, parent_rate); |
172 | } |
173 | |
174 | static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate, |
175 | unsigned long parent_rate) |
176 | { |
177 | struct tegra_clk_super_mux *super = to_clk_super_mux(hw); |
178 | struct clk_hw *div_hw = &super->frac_div.hw; |
179 | |
180 | __clk_hw_set_clk(dst: div_hw, src: hw); |
181 | |
182 | return super->div_ops->set_rate(div_hw, rate, parent_rate); |
183 | } |
184 | |
185 | static void clk_super_restore_context(struct clk_hw *hw) |
186 | { |
187 | struct tegra_clk_super_mux *super = to_clk_super_mux(hw); |
188 | struct clk_hw *div_hw = &super->frac_div.hw; |
189 | int parent_id; |
190 | |
191 | parent_id = clk_hw_get_parent_index(hw); |
192 | if (WARN_ON(parent_id < 0)) |
193 | return; |
194 | |
195 | super->div_ops->restore_context(div_hw); |
196 | clk_super_set_parent(hw, index: parent_id); |
197 | } |
198 | |
199 | const struct clk_ops tegra_clk_super_ops = { |
200 | .get_parent = clk_super_get_parent, |
201 | .set_parent = clk_super_set_parent, |
202 | .set_rate = clk_super_set_rate, |
203 | .determine_rate = clk_super_determine_rate, |
204 | .recalc_rate = clk_super_recalc_rate, |
205 | .restore_context = clk_super_restore_context, |
206 | }; |
207 | |
208 | struct clk *tegra_clk_register_super_mux(const char *name, |
209 | const char **parent_names, u8 num_parents, |
210 | unsigned long flags, void __iomem *reg, u8 clk_super_flags, |
211 | u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock) |
212 | { |
213 | struct tegra_clk_super_mux *super; |
214 | struct clk *clk; |
215 | struct clk_init_data init; |
216 | |
217 | super = kzalloc(size: sizeof(*super), GFP_KERNEL); |
218 | if (!super) |
219 | return ERR_PTR(error: -ENOMEM); |
220 | |
221 | init.name = name; |
222 | init.ops = &tegra_clk_super_mux_ops; |
223 | init.flags = flags; |
224 | init.parent_names = parent_names; |
225 | init.num_parents = num_parents; |
226 | |
227 | super->reg = reg; |
228 | super->pllx_index = pllx_index; |
229 | super->div2_index = div2_index; |
230 | super->lock = lock; |
231 | super->width = width; |
232 | super->flags = clk_super_flags; |
233 | |
234 | /* Data in .init is copied by clk_register(), so stack variable OK */ |
235 | super->hw.init = &init; |
236 | |
237 | clk = tegra_clk_dev_register(hw: &super->hw); |
238 | if (IS_ERR(ptr: clk)) |
239 | kfree(objp: super); |
240 | |
241 | return clk; |
242 | } |
243 | |
244 | struct clk *tegra_clk_register_super_clk(const char *name, |
245 | const char * const *parent_names, u8 num_parents, |
246 | unsigned long flags, void __iomem *reg, u8 clk_super_flags, |
247 | spinlock_t *lock) |
248 | { |
249 | struct tegra_clk_super_mux *super; |
250 | struct clk *clk; |
251 | struct clk_init_data init; |
252 | |
253 | super = kzalloc(size: sizeof(*super), GFP_KERNEL); |
254 | if (!super) |
255 | return ERR_PTR(error: -ENOMEM); |
256 | |
257 | init.name = name; |
258 | init.ops = &tegra_clk_super_ops; |
259 | init.flags = flags; |
260 | init.parent_names = parent_names; |
261 | init.num_parents = num_parents; |
262 | |
263 | super->reg = reg; |
264 | super->lock = lock; |
265 | super->width = 4; |
266 | super->flags = clk_super_flags; |
267 | super->frac_div.reg = reg + 4; |
268 | super->frac_div.shift = 16; |
269 | super->frac_div.width = 8; |
270 | super->frac_div.frac_width = 1; |
271 | super->frac_div.lock = lock; |
272 | super->div_ops = &tegra_clk_frac_div_ops; |
273 | |
274 | /* Data in .init is copied by clk_register(), so stack variable OK */ |
275 | super->hw.init = &init; |
276 | |
277 | clk = clk_register(NULL, hw: &super->hw); |
278 | if (IS_ERR(ptr: clk)) |
279 | kfree(objp: super); |
280 | |
281 | return clk; |
282 | } |
283 | |