]> Git Repo - linux.git/blob - drivers/clk/tegra/clk-tegra210-emc.c
drm/nouveau/kms: Don't change EDID when it hasn't actually changed
[linux.git] / drivers / clk / tegra / clk-tegra210-emc.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2015-2020, NVIDIA CORPORATION.  All rights reserved.
4  */
5
6 #include <linux/bitfield.h>
7 #include <linux/clk.h>
8 #include <linux/clk-provider.h>
9 #include <linux/clk/tegra.h>
10 #include <linux/device.h>
11 #include <linux/module.h>
12 #include <linux/io.h>
13 #include <linux/slab.h>
14
15 #define CLK_SOURCE_EMC 0x19c
16 #define  CLK_SOURCE_EMC_2X_CLK_SRC GENMASK(31, 29)
17 #define  CLK_SOURCE_EMC_MC_EMC_SAME_FREQ BIT(16)
18 #define  CLK_SOURCE_EMC_2X_CLK_DIVISOR GENMASK(7, 0)
19
20 #define CLK_SRC_PLLM 0
21 #define CLK_SRC_PLLC 1
22 #define CLK_SRC_PLLP 2
23 #define CLK_SRC_CLK_M 3
24 #define CLK_SRC_PLLM_UD 4
25 #define CLK_SRC_PLLMB_UD 5
26 #define CLK_SRC_PLLMB 6
27 #define CLK_SRC_PLLP_UD 7
28
29 struct tegra210_clk_emc {
30         struct clk_hw hw;
31         void __iomem *regs;
32
33         struct tegra210_clk_emc_provider *provider;
34
35         struct clk *parents[8];
36 };
37
38 static inline struct tegra210_clk_emc *
39 to_tegra210_clk_emc(struct clk_hw *hw)
40 {
41         return container_of(hw, struct tegra210_clk_emc, hw);
42 }
43
44 static const char *tegra210_clk_emc_parents[] = {
45         "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_mb_ud",
46         "pll_mb", "pll_p_ud",
47 };
48
49 static u8 tegra210_clk_emc_get_parent(struct clk_hw *hw)
50 {
51         struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
52         u32 value;
53         u8 src;
54
55         value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
56         src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, value);
57
58         return src;
59 }
60
61 static unsigned long tegra210_clk_emc_recalc_rate(struct clk_hw *hw,
62                                                   unsigned long parent_rate)
63 {
64         struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
65         u32 value, div;
66
67         /*
68          * CCF assumes that neither the parent nor its rate will change during
69          * ->set_rate(), so the parent rate passed in here was cached from the
70          * parent before the ->set_rate() call.
71          *
72          * This can lead to wrong results being reported for the EMC clock if
73          * the parent and/or parent rate have changed as part of the EMC rate
74          * change sequence. Fix this by overriding the parent clock with what
75          * we know to be the correct value after the rate change.
76          */
77         parent_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
78
79         value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
80
81         div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, value);
82         div += 2;
83
84         return DIV_ROUND_UP(parent_rate * 2, div);
85 }
86
87 static long tegra210_clk_emc_round_rate(struct clk_hw *hw, unsigned long rate,
88                                         unsigned long *prate)
89 {
90         struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
91         struct tegra210_clk_emc_provider *provider = emc->provider;
92         unsigned int i;
93
94         if (!provider || !provider->configs || provider->num_configs == 0)
95                 return clk_hw_get_rate(hw);
96
97         for (i = 0; i < provider->num_configs; i++) {
98                 if (provider->configs[i].rate >= rate)
99                         return provider->configs[i].rate;
100         }
101
102         return provider->configs[i - 1].rate;
103 }
104
105 static struct clk *tegra210_clk_emc_find_parent(struct tegra210_clk_emc *emc,
106                                                 u8 index)
107 {
108         struct clk_hw *parent = clk_hw_get_parent_by_index(&emc->hw, index);
109         const char *name = clk_hw_get_name(parent);
110
111         /* XXX implement cache? */
112
113         return __clk_lookup(name);
114 }
115
116 static int tegra210_clk_emc_set_rate(struct clk_hw *hw, unsigned long rate,
117                                      unsigned long parent_rate)
118 {
119         struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
120         struct tegra210_clk_emc_provider *provider = emc->provider;
121         struct tegra210_clk_emc_config *config;
122         struct device *dev = provider->dev;
123         struct clk_hw *old, *new, *parent;
124         u8 old_idx, new_idx, index;
125         struct clk *clk;
126         unsigned int i;
127         int err;
128
129         if (!provider || !provider->configs || provider->num_configs == 0)
130                 return -EINVAL;
131
132         for (i = 0; i < provider->num_configs; i++) {
133                 if (provider->configs[i].rate >= rate) {
134                         config = &provider->configs[i];
135                         break;
136                 }
137         }
138
139         if (i == provider->num_configs)
140                 config = &provider->configs[i - 1];
141
142         old_idx = tegra210_clk_emc_get_parent(hw);
143         new_idx = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
144
145         old = clk_hw_get_parent_by_index(hw, old_idx);
146         new = clk_hw_get_parent_by_index(hw, new_idx);
147
148         /* if the rate has changed... */
149         if (config->parent_rate != clk_hw_get_rate(old)) {
150                 /* ... but the clock source remains the same ... */
151                 if (new_idx == old_idx) {
152                         /* ... switch to the alternative clock source. */
153                         switch (new_idx) {
154                         case CLK_SRC_PLLM:
155                                 new_idx = CLK_SRC_PLLMB;
156                                 break;
157
158                         case CLK_SRC_PLLM_UD:
159                                 new_idx = CLK_SRC_PLLMB_UD;
160                                 break;
161
162                         case CLK_SRC_PLLMB_UD:
163                                 new_idx = CLK_SRC_PLLM_UD;
164                                 break;
165
166                         case CLK_SRC_PLLMB:
167                                 new_idx = CLK_SRC_PLLM;
168                                 break;
169                         }
170
171                         /*
172                          * This should never happen because we can't deal with
173                          * it.
174                          */
175                         if (WARN_ON(new_idx == old_idx))
176                                 return -EINVAL;
177
178                         new = clk_hw_get_parent_by_index(hw, new_idx);
179                 }
180
181                 index = new_idx;
182                 parent = new;
183         } else {
184                 index = old_idx;
185                 parent = old;
186         }
187
188         clk = tegra210_clk_emc_find_parent(emc, index);
189         if (IS_ERR(clk)) {
190                 err = PTR_ERR(clk);
191                 dev_err(dev, "failed to get parent clock for index %u: %d\n",
192                         index, err);
193                 return err;
194         }
195
196         /* set the new parent clock to the required rate */
197         if (clk_get_rate(clk) != config->parent_rate) {
198                 err = clk_set_rate(clk, config->parent_rate);
199                 if (err < 0) {
200                         dev_err(dev, "failed to set rate %lu Hz for %pC: %d\n",
201                                 config->parent_rate, clk, err);
202                         return err;
203                 }
204         }
205
206         /* enable the new parent clock */
207         if (parent != old) {
208                 err = clk_prepare_enable(clk);
209                 if (err < 0) {
210                         dev_err(dev, "failed to enable parent clock %pC: %d\n",
211                                 clk, err);
212                         return err;
213                 }
214         }
215
216         /* update the EMC source configuration to reflect the new parent */
217         config->value &= ~CLK_SOURCE_EMC_2X_CLK_SRC;
218         config->value |= FIELD_PREP(CLK_SOURCE_EMC_2X_CLK_SRC, index);
219
220         /*
221          * Finally, switch the EMC programming with both old and new parent
222          * clocks enabled.
223          */
224         err = provider->set_rate(dev, config);
225         if (err < 0) {
226                 dev_err(dev, "failed to set EMC rate to %lu Hz: %d\n", rate,
227                         err);
228
229                 /*
230                  * If we're unable to switch to the new EMC frequency, we no
231                  * longer need the new parent to be enabled.
232                  */
233                 if (parent != old)
234                         clk_disable_unprepare(clk);
235
236                 return err;
237         }
238
239         /* reparent to new parent clock and disable the old parent clock */
240         if (parent != old) {
241                 clk = tegra210_clk_emc_find_parent(emc, old_idx);
242                 if (IS_ERR(clk)) {
243                         err = PTR_ERR(clk);
244                         dev_err(dev,
245                                 "failed to get parent clock for index %u: %d\n",
246                                 old_idx, err);
247                         return err;
248                 }
249
250                 clk_hw_reparent(hw, parent);
251                 clk_disable_unprepare(clk);
252         }
253
254         return err;
255 }
256
257 static const struct clk_ops tegra210_clk_emc_ops = {
258         .get_parent = tegra210_clk_emc_get_parent,
259         .recalc_rate = tegra210_clk_emc_recalc_rate,
260         .round_rate = tegra210_clk_emc_round_rate,
261         .set_rate = tegra210_clk_emc_set_rate,
262 };
263
264 struct clk *tegra210_clk_register_emc(struct device_node *np,
265                                       void __iomem *regs)
266 {
267         struct tegra210_clk_emc *emc;
268         struct clk_init_data init;
269         struct clk *clk;
270
271         emc = kzalloc(sizeof(*emc), GFP_KERNEL);
272         if (!emc)
273                 return ERR_PTR(-ENOMEM);
274
275         emc->regs = regs;
276
277         init.name = "emc";
278         init.ops = &tegra210_clk_emc_ops;
279         init.flags = CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE;
280         init.parent_names = tegra210_clk_emc_parents;
281         init.num_parents = ARRAY_SIZE(tegra210_clk_emc_parents);
282         emc->hw.init = &init;
283
284         clk = clk_register(NULL, &emc->hw);
285         if (IS_ERR(clk)) {
286                 kfree(emc);
287                 return clk;
288         }
289
290         return clk;
291 }
292
293 int tegra210_clk_emc_attach(struct clk *clk,
294                             struct tegra210_clk_emc_provider *provider)
295 {
296         struct clk_hw *hw = __clk_get_hw(clk);
297         struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
298         struct device *dev = provider->dev;
299         unsigned int i;
300         int err;
301
302         if (!try_module_get(provider->owner))
303                 return -ENODEV;
304
305         for (i = 0; i < provider->num_configs; i++) {
306                 struct tegra210_clk_emc_config *config = &provider->configs[i];
307                 struct clk_hw *parent;
308                 bool same_freq;
309                 u8 div, src;
310
311                 div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, config->value);
312                 src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
313
314                 /* do basic sanity checking on the EMC timings */
315                 if (div & 0x1) {
316                         dev_err(dev, "invalid odd divider %u for rate %lu Hz\n",
317                                 div, config->rate);
318                         err = -EINVAL;
319                         goto put;
320                 }
321
322                 same_freq = config->value & CLK_SOURCE_EMC_MC_EMC_SAME_FREQ;
323
324                 if (same_freq != config->same_freq) {
325                         dev_err(dev,
326                                 "ambiguous EMC to MC ratio for rate %lu Hz\n",
327                                 config->rate);
328                         err = -EINVAL;
329                         goto put;
330                 }
331
332                 parent = clk_hw_get_parent_by_index(hw, src);
333                 config->parent = src;
334
335                 if (src == CLK_SRC_PLLM || src == CLK_SRC_PLLM_UD) {
336                         config->parent_rate = config->rate * (1 + div / 2);
337                 } else {
338                         unsigned long rate = config->rate * (1 + div / 2);
339
340                         config->parent_rate = clk_hw_get_rate(parent);
341
342                         if (config->parent_rate != rate) {
343                                 dev_err(dev,
344                                         "rate %lu Hz does not match input\n",
345                                         config->rate);
346                                 err = -EINVAL;
347                                 goto put;
348                         }
349                 }
350         }
351
352         emc->provider = provider;
353
354         return 0;
355
356 put:
357         module_put(provider->owner);
358         return err;
359 }
360 EXPORT_SYMBOL_GPL(tegra210_clk_emc_attach);
361
362 void tegra210_clk_emc_detach(struct clk *clk)
363 {
364         struct tegra210_clk_emc *emc = to_tegra210_clk_emc(__clk_get_hw(clk));
365
366         module_put(emc->provider->owner);
367         emc->provider = NULL;
368 }
369 EXPORT_SYMBOL_GPL(tegra210_clk_emc_detach);
This page took 0.052585 seconds and 4 git commands to generate.