1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Based on drivers/clk/tegra/clk-emc.c |
4 | * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. |
5 | * |
6 | * Author: Dmitry Osipenko <digetx@gmail.com> |
7 | * Copyright (C) 2019 GRATE-DRIVER project |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) "tegra-emc-clk: " fmt |
11 | |
12 | #include <linux/bits.h> |
13 | #include <linux/clk-provider.h> |
14 | #include <linux/clk/tegra.h> |
15 | #include <linux/err.h> |
16 | #include <linux/export.h> |
17 | #include <linux/io.h> |
18 | #include <linux/kernel.h> |
19 | #include <linux/slab.h> |
20 | |
21 | #include "clk.h" |
22 | |
23 | #define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK GENMASK(7, 0) |
24 | #define CLK_SOURCE_EMC_2X_CLK_SRC_MASK GENMASK(31, 30) |
25 | #define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT 30 |
26 | |
27 | #define MC_EMC_SAME_FREQ BIT(16) |
28 | #define USE_PLLM_UD BIT(29) |
29 | |
30 | #define EMC_SRC_PLL_M 0 |
31 | #define EMC_SRC_PLL_C 1 |
32 | #define EMC_SRC_PLL_P 2 |
33 | #define EMC_SRC_CLK_M 3 |
34 | |
35 | static const char * const emc_parent_clk_names[] = { |
36 | "pll_m" , "pll_c" , "pll_p" , "clk_m" , |
37 | }; |
38 | |
39 | struct tegra_clk_emc { |
40 | struct clk_hw hw; |
41 | void __iomem *reg; |
42 | bool mc_same_freq; |
43 | bool want_low_jitter; |
44 | |
45 | tegra20_clk_emc_round_cb *round_cb; |
46 | void *cb_arg; |
47 | }; |
48 | |
49 | static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw) |
50 | { |
51 | return container_of(hw, struct tegra_clk_emc, hw); |
52 | } |
53 | |
54 | static unsigned long emc_recalc_rate(struct clk_hw *hw, |
55 | unsigned long parent_rate) |
56 | { |
57 | struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); |
58 | u32 val, div; |
59 | |
60 | val = readl_relaxed(emc->reg); |
61 | div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; |
62 | |
63 | return DIV_ROUND_UP(parent_rate * 2, div + 2); |
64 | } |
65 | |
66 | static u8 emc_get_parent(struct clk_hw *hw) |
67 | { |
68 | struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); |
69 | |
70 | return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; |
71 | } |
72 | |
73 | static int emc_set_parent(struct clk_hw *hw, u8 index) |
74 | { |
75 | struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); |
76 | u32 val, div; |
77 | |
78 | val = readl_relaxed(emc->reg); |
79 | val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK; |
80 | val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; |
81 | |
82 | div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; |
83 | |
84 | if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter) |
85 | val |= USE_PLLM_UD; |
86 | else |
87 | val &= ~USE_PLLM_UD; |
88 | |
89 | if (emc->mc_same_freq) |
90 | val |= MC_EMC_SAME_FREQ; |
91 | else |
92 | val &= ~MC_EMC_SAME_FREQ; |
93 | |
94 | writel_relaxed(val, emc->reg); |
95 | |
96 | fence_udelay(1, emc->reg); |
97 | |
98 | return 0; |
99 | } |
100 | |
101 | static int emc_set_rate(struct clk_hw *hw, unsigned long rate, |
102 | unsigned long parent_rate) |
103 | { |
104 | struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); |
105 | unsigned int index; |
106 | u32 val, div; |
107 | |
108 | div = div_frac_get(rate, parent_rate, width: 8, frac_width: 1, flags: 0); |
109 | |
110 | val = readl_relaxed(emc->reg); |
111 | val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; |
112 | val |= div; |
113 | |
114 | index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; |
115 | |
116 | if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter) |
117 | val |= USE_PLLM_UD; |
118 | else |
119 | val &= ~USE_PLLM_UD; |
120 | |
121 | if (emc->mc_same_freq) |
122 | val |= MC_EMC_SAME_FREQ; |
123 | else |
124 | val &= ~MC_EMC_SAME_FREQ; |
125 | |
126 | writel_relaxed(val, emc->reg); |
127 | |
128 | fence_udelay(1, emc->reg); |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static int emc_set_rate_and_parent(struct clk_hw *hw, |
134 | unsigned long rate, |
135 | unsigned long parent_rate, |
136 | u8 index) |
137 | { |
138 | struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); |
139 | u32 val, div; |
140 | |
141 | div = div_frac_get(rate, parent_rate, width: 8, frac_width: 1, flags: 0); |
142 | |
143 | val = readl_relaxed(emc->reg); |
144 | |
145 | val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK; |
146 | val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; |
147 | |
148 | val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; |
149 | val |= div; |
150 | |
151 | if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter) |
152 | val |= USE_PLLM_UD; |
153 | else |
154 | val &= ~USE_PLLM_UD; |
155 | |
156 | if (emc->mc_same_freq) |
157 | val |= MC_EMC_SAME_FREQ; |
158 | else |
159 | val &= ~MC_EMC_SAME_FREQ; |
160 | |
161 | writel_relaxed(val, emc->reg); |
162 | |
163 | fence_udelay(1, emc->reg); |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) |
169 | { |
170 | struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); |
171 | struct clk_hw *parent_hw; |
172 | unsigned long divided_rate; |
173 | unsigned long parent_rate; |
174 | unsigned int i; |
175 | long emc_rate; |
176 | int div; |
177 | |
178 | emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate, |
179 | emc->cb_arg); |
180 | if (emc_rate < 0) |
181 | return emc_rate; |
182 | |
183 | for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) { |
184 | parent_hw = clk_hw_get_parent_by_index(hw, index: i); |
185 | |
186 | if (req->best_parent_hw == parent_hw) |
187 | parent_rate = req->best_parent_rate; |
188 | else |
189 | parent_rate = clk_hw_get_rate(hw: parent_hw); |
190 | |
191 | if (emc_rate > parent_rate) |
192 | continue; |
193 | |
194 | div = div_frac_get(rate: emc_rate, parent_rate, width: 8, frac_width: 1, flags: 0); |
195 | divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2); |
196 | |
197 | if (divided_rate != emc_rate) |
198 | continue; |
199 | |
200 | req->best_parent_rate = parent_rate; |
201 | req->best_parent_hw = parent_hw; |
202 | req->rate = emc_rate; |
203 | break; |
204 | } |
205 | |
206 | if (i == ARRAY_SIZE(emc_parent_clk_names)) { |
207 | pr_err_once("can't find parent for rate %lu emc_rate %lu\n" , |
208 | req->rate, emc_rate); |
209 | return -EINVAL; |
210 | } |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static const struct clk_ops tegra_clk_emc_ops = { |
216 | .recalc_rate = emc_recalc_rate, |
217 | .get_parent = emc_get_parent, |
218 | .set_parent = emc_set_parent, |
219 | .set_rate = emc_set_rate, |
220 | .set_rate_and_parent = emc_set_rate_and_parent, |
221 | .determine_rate = emc_determine_rate, |
222 | }; |
223 | |
224 | void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb, |
225 | void *cb_arg) |
226 | { |
227 | struct clk *clk = __clk_lookup(name: "emc" ); |
228 | struct tegra_clk_emc *emc; |
229 | struct clk_hw *hw; |
230 | |
231 | if (clk) { |
232 | hw = __clk_get_hw(clk); |
233 | emc = to_tegra_clk_emc(hw); |
234 | |
235 | emc->round_cb = round_cb; |
236 | emc->cb_arg = cb_arg; |
237 | } |
238 | } |
239 | EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback); |
240 | |
241 | bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw) |
242 | { |
243 | return to_tegra_clk_emc(hw: emc_hw)->round_cb != NULL; |
244 | } |
245 | |
246 | struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter) |
247 | { |
248 | struct tegra_clk_emc *emc; |
249 | struct clk_init_data init; |
250 | struct clk *clk; |
251 | |
252 | emc = kzalloc(size: sizeof(*emc), GFP_KERNEL); |
253 | if (!emc) |
254 | return NULL; |
255 | |
256 | /* |
257 | * EMC stands for External Memory Controller. |
258 | * |
259 | * We don't want EMC clock to be disabled ever by gating its |
260 | * parent and whatnot because system is busted immediately in that |
261 | * case, hence the clock is marked as critical. |
262 | */ |
263 | init.name = "emc" ; |
264 | init.ops = &tegra_clk_emc_ops; |
265 | init.flags = CLK_IS_CRITICAL; |
266 | init.parent_names = emc_parent_clk_names; |
267 | init.num_parents = ARRAY_SIZE(emc_parent_clk_names); |
268 | |
269 | emc->reg = ioaddr; |
270 | emc->hw.init = &init; |
271 | emc->want_low_jitter = low_jitter; |
272 | |
273 | clk = clk_register(NULL, hw: &emc->hw); |
274 | if (IS_ERR(ptr: clk)) { |
275 | kfree(objp: emc); |
276 | return NULL; |
277 | } |
278 | |
279 | return clk; |
280 | } |
281 | |
282 | int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same) |
283 | { |
284 | struct tegra_clk_emc *emc; |
285 | struct clk_hw *hw; |
286 | |
287 | if (!emc_clk) |
288 | return -EINVAL; |
289 | |
290 | hw = __clk_get_hw(clk: emc_clk); |
291 | emc = to_tegra_clk_emc(hw); |
292 | emc->mc_same_freq = same; |
293 | |
294 | return 0; |
295 | } |
296 | EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq); |
297 | |