Commit | Line | Data |
---|---|---|
9952f691 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
8f8f484b PG |
2 | /* |
3 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
8f8f484b PG |
4 | */ |
5 | ||
584ac4e9 | 6 | #include <linux/clkdev.h> |
8f8f484b PG |
7 | #include <linux/clk.h> |
8 | #include <linux/clk-provider.h> | |
4236e752 | 9 | #include <linux/delay.h> |
62e59c4e | 10 | #include <linux/io.h> |
61fd290d | 11 | #include <linux/of.h> |
b1bc04a2 | 12 | #include <linux/of_device.h> |
61fd290d | 13 | #include <linux/clk/tegra.h> |
b1bc04a2 DO |
14 | #include <linux/platform_device.h> |
15 | #include <linux/pm_runtime.h> | |
6d5b988e | 16 | #include <linux/reset-controller.h> |
b1bc04a2 | 17 | #include <linux/string.h> |
306a7f91 TR |
18 | |
19 | #include <soc/tegra/fuse.h> | |
8f8f484b PG |
20 | |
21 | #include "clk.h" | |
22 | ||
61fd290d | 23 | /* Global data of Tegra CPU CAR ops */ |
b1bc04a2 | 24 | static struct device_node *tegra_car_np; |
6a676fa0 PDS |
25 | static struct tegra_cpu_car_ops dummy_car_ops; |
26 | struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops; | |
61fd290d | 27 | |
343a607c | 28 | int *periph_clk_enb_refcnt; |
d5ff89a8 | 29 | static int periph_banks; |
535f296d | 30 | static u32 *periph_state_ctx; |
343a607c PDS |
31 | static struct clk **clks; |
32 | static int clk_num; | |
33 | static struct clk_onecell_data clk_data; | |
d5ff89a8 | 34 | |
66b6f3d0 MP |
35 | /* Handlers for SoC-specific reset lines */ |
36 | static int (*special_reset_assert)(unsigned long); | |
37 | static int (*special_reset_deassert)(unsigned long); | |
38 | static unsigned int num_special_reset; | |
39 | ||
7e14f223 | 40 | static const struct tegra_clk_periph_regs periph_regs[] = { |
d5ff89a8 PDS |
41 | [0] = { |
42 | .enb_reg = CLK_OUT_ENB_L, | |
43 | .enb_set_reg = CLK_OUT_ENB_SET_L, | |
44 | .enb_clr_reg = CLK_OUT_ENB_CLR_L, | |
45 | .rst_reg = RST_DEVICES_L, | |
46 | .rst_set_reg = RST_DEVICES_SET_L, | |
47 | .rst_clr_reg = RST_DEVICES_CLR_L, | |
48 | }, | |
49 | [1] = { | |
50 | .enb_reg = CLK_OUT_ENB_H, | |
51 | .enb_set_reg = CLK_OUT_ENB_SET_H, | |
52 | .enb_clr_reg = CLK_OUT_ENB_CLR_H, | |
53 | .rst_reg = RST_DEVICES_H, | |
54 | .rst_set_reg = RST_DEVICES_SET_H, | |
55 | .rst_clr_reg = RST_DEVICES_CLR_H, | |
56 | }, | |
57 | [2] = { | |
58 | .enb_reg = CLK_OUT_ENB_U, | |
59 | .enb_set_reg = CLK_OUT_ENB_SET_U, | |
60 | .enb_clr_reg = CLK_OUT_ENB_CLR_U, | |
61 | .rst_reg = RST_DEVICES_U, | |
62 | .rst_set_reg = RST_DEVICES_SET_U, | |
63 | .rst_clr_reg = RST_DEVICES_CLR_U, | |
64 | }, | |
65 | [3] = { | |
66 | .enb_reg = CLK_OUT_ENB_V, | |
67 | .enb_set_reg = CLK_OUT_ENB_SET_V, | |
68 | .enb_clr_reg = CLK_OUT_ENB_CLR_V, | |
69 | .rst_reg = RST_DEVICES_V, | |
70 | .rst_set_reg = RST_DEVICES_SET_V, | |
71 | .rst_clr_reg = RST_DEVICES_CLR_V, | |
72 | }, | |
73 | [4] = { | |
74 | .enb_reg = CLK_OUT_ENB_W, | |
75 | .enb_set_reg = CLK_OUT_ENB_SET_W, | |
76 | .enb_clr_reg = CLK_OUT_ENB_CLR_W, | |
77 | .rst_reg = RST_DEVICES_W, | |
78 | .rst_set_reg = RST_DEVICES_SET_W, | |
79 | .rst_clr_reg = RST_DEVICES_CLR_W, | |
80 | }, | |
2b239077 PDS |
81 | [5] = { |
82 | .enb_reg = CLK_OUT_ENB_X, | |
83 | .enb_set_reg = CLK_OUT_ENB_SET_X, | |
84 | .enb_clr_reg = CLK_OUT_ENB_CLR_X, | |
85 | .rst_reg = RST_DEVICES_X, | |
86 | .rst_set_reg = RST_DEVICES_SET_X, | |
87 | .rst_clr_reg = RST_DEVICES_CLR_X, | |
88 | }, | |
699b477a TR |
89 | [6] = { |
90 | .enb_reg = CLK_OUT_ENB_Y, | |
91 | .enb_set_reg = CLK_OUT_ENB_SET_Y, | |
92 | .enb_clr_reg = CLK_OUT_ENB_CLR_Y, | |
93 | .rst_reg = RST_DEVICES_Y, | |
94 | .rst_set_reg = RST_DEVICES_SET_Y, | |
95 | .rst_clr_reg = RST_DEVICES_CLR_Y, | |
96 | }, | |
d5ff89a8 PDS |
97 | }; |
98 | ||
6d5b988e SW |
99 | static void __iomem *clk_base; |
100 | ||
101 | static int tegra_clk_rst_assert(struct reset_controller_dev *rcdev, | |
102 | unsigned long id) | |
103 | { | |
104 | /* | |
105 | * If peripheral is on the APB bus then we must read the APB bus to | |
106 | * flush the write operation in apb bus. This will avoid peripheral | |
107 | * access after disabling clock. Since the reset driver has no | |
108 | * knowledge of which reset IDs represent which devices, simply do | |
109 | * this all the time. | |
110 | */ | |
111 | tegra_read_chipid(); | |
112 | ||
66b6f3d0 MP |
113 | if (id < periph_banks * 32) { |
114 | writel_relaxed(BIT(id % 32), | |
115 | clk_base + periph_regs[id / 32].rst_set_reg); | |
116 | return 0; | |
117 | } else if (id < periph_banks * 32 + num_special_reset) { | |
118 | return special_reset_assert(id); | |
119 | } | |
6d5b988e | 120 | |
66b6f3d0 | 121 | return -EINVAL; |
6d5b988e SW |
122 | } |
123 | ||
124 | static int tegra_clk_rst_deassert(struct reset_controller_dev *rcdev, | |
125 | unsigned long id) | |
126 | { | |
66b6f3d0 MP |
127 | if (id < periph_banks * 32) { |
128 | writel_relaxed(BIT(id % 32), | |
129 | clk_base + periph_regs[id / 32].rst_clr_reg); | |
130 | return 0; | |
131 | } else if (id < periph_banks * 32 + num_special_reset) { | |
132 | return special_reset_deassert(id); | |
133 | } | |
6d5b988e | 134 | |
66b6f3d0 | 135 | return -EINVAL; |
6d5b988e SW |
136 | } |
137 | ||
4236e752 MP |
138 | static int tegra_clk_rst_reset(struct reset_controller_dev *rcdev, |
139 | unsigned long id) | |
140 | { | |
141 | int err; | |
142 | ||
143 | err = tegra_clk_rst_assert(rcdev, id); | |
144 | if (err) | |
145 | return err; | |
146 | ||
147 | udelay(1); | |
148 | ||
149 | return tegra_clk_rst_deassert(rcdev, id); | |
150 | } | |
151 | ||
7e14f223 | 152 | const struct tegra_clk_periph_regs *get_reg_bank(int clkid) |
d5ff89a8 PDS |
153 | { |
154 | int reg_bank = clkid / 32; | |
155 | ||
156 | if (reg_bank < periph_banks) | |
157 | return &periph_regs[reg_bank]; | |
158 | else { | |
159 | WARN_ON(1); | |
160 | return NULL; | |
161 | } | |
162 | } | |
163 | ||
68a14a56 SK |
164 | void tegra_clk_set_pllp_out_cpu(bool enable) |
165 | { | |
166 | u32 val; | |
167 | ||
168 | val = readl_relaxed(clk_base + CLK_OUT_ENB_Y); | |
169 | if (enable) | |
170 | val |= CLK_ENB_PLLP_OUT_CPU; | |
171 | else | |
172 | val &= ~CLK_ENB_PLLP_OUT_CPU; | |
173 | ||
174 | writel_relaxed(val, clk_base + CLK_OUT_ENB_Y); | |
175 | } | |
176 | ||
535f296d SK |
177 | void tegra_clk_periph_suspend(void) |
178 | { | |
179 | unsigned int i, idx; | |
180 | ||
181 | idx = 0; | |
182 | for (i = 0; i < periph_banks; i++, idx++) | |
183 | periph_state_ctx[idx] = | |
184 | readl_relaxed(clk_base + periph_regs[i].enb_reg); | |
185 | ||
186 | for (i = 0; i < periph_banks; i++, idx++) | |
187 | periph_state_ctx[idx] = | |
188 | readl_relaxed(clk_base + periph_regs[i].rst_reg); | |
189 | } | |
190 | ||
191 | void tegra_clk_periph_resume(void) | |
192 | { | |
193 | unsigned int i, idx; | |
194 | ||
195 | idx = 0; | |
196 | for (i = 0; i < periph_banks; i++, idx++) | |
197 | writel_relaxed(periph_state_ctx[idx], | |
198 | clk_base + periph_regs[i].enb_reg); | |
199 | /* | |
200 | * All non-boot peripherals will be in reset state on resume. | |
201 | * Wait for 5us of reset propagation delay before de-asserting | |
202 | * the peripherals based on the saved context. | |
203 | */ | |
204 | fence_udelay(5, clk_base); | |
205 | ||
206 | for (i = 0; i < periph_banks; i++, idx++) | |
207 | writel_relaxed(periph_state_ctx[idx], | |
208 | clk_base + periph_regs[i].rst_reg); | |
209 | ||
210 | fence_udelay(2, clk_base); | |
211 | } | |
212 | ||
213 | static int tegra_clk_periph_ctx_init(int banks) | |
214 | { | |
215 | periph_state_ctx = kcalloc(2 * banks, sizeof(*periph_state_ctx), | |
216 | GFP_KERNEL); | |
217 | if (!periph_state_ctx) | |
218 | return -ENOMEM; | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
6d5b988e | 223 | struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks) |
d5ff89a8 | 224 | { |
6d5b988e SW |
225 | clk_base = regs; |
226 | ||
343a607c PDS |
227 | if (WARN_ON(banks > ARRAY_SIZE(periph_regs))) |
228 | return NULL; | |
229 | ||
6396bb22 KC |
230 | periph_clk_enb_refcnt = kcalloc(32 * banks, |
231 | sizeof(*periph_clk_enb_refcnt), | |
232 | GFP_KERNEL); | |
343a607c PDS |
233 | if (!periph_clk_enb_refcnt) |
234 | return NULL; | |
d5ff89a8 | 235 | |
343a607c | 236 | periph_banks = banks; |
d5ff89a8 | 237 | |
6396bb22 | 238 | clks = kcalloc(num, sizeof(struct clk *), GFP_KERNEL); |
fc666936 | 239 | if (!clks) { |
343a607c | 240 | kfree(periph_clk_enb_refcnt); |
fc666936 DO |
241 | return NULL; |
242 | } | |
343a607c PDS |
243 | |
244 | clk_num = num; | |
245 | ||
535f296d SK |
246 | if (IS_ENABLED(CONFIG_PM_SLEEP)) { |
247 | if (tegra_clk_periph_ctx_init(banks)) { | |
248 | kfree(periph_clk_enb_refcnt); | |
249 | kfree(clks); | |
250 | return NULL; | |
251 | } | |
252 | } | |
253 | ||
343a607c | 254 | return clks; |
d5ff89a8 PDS |
255 | } |
256 | ||
8f8f484b PG |
257 | void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, |
258 | struct clk *clks[], int clk_max) | |
259 | { | |
260 | struct clk *clk; | |
261 | ||
262 | for (; dup_list->clk_id < clk_max; dup_list++) { | |
263 | clk = clks[dup_list->clk_id]; | |
264 | dup_list->lookup.clk = clk; | |
265 | clkdev_add(&dup_list->lookup); | |
266 | } | |
267 | } | |
268 | ||
b1bc04a2 DO |
269 | void tegra_init_from_table(struct tegra_clk_init_table *tbl, |
270 | struct clk *clks[], int clk_max) | |
8f8f484b PG |
271 | { |
272 | struct clk *clk; | |
273 | ||
274 | for (; tbl->clk_id < clk_max; tbl++) { | |
275 | clk = clks[tbl->clk_id]; | |
b9e742c3 TV |
276 | if (IS_ERR_OR_NULL(clk)) { |
277 | pr_err("%s: invalid entry %ld in clks array for id %d\n", | |
278 | __func__, PTR_ERR(clk), tbl->clk_id); | |
279 | WARN_ON(1); | |
280 | ||
281 | continue; | |
282 | } | |
8f8f484b PG |
283 | |
284 | if (tbl->parent_id < clk_max) { | |
285 | struct clk *parent = clks[tbl->parent_id]; | |
286 | if (clk_set_parent(clk, parent)) { | |
287 | pr_err("%s: Failed to set parent %s of %s\n", | |
288 | __func__, __clk_get_name(parent), | |
289 | __clk_get_name(clk)); | |
290 | WARN_ON(1); | |
291 | } | |
292 | } | |
293 | ||
294 | if (tbl->rate) | |
295 | if (clk_set_rate(clk, tbl->rate)) { | |
296 | pr_err("%s: Failed to set rate %lu of %s\n", | |
297 | __func__, tbl->rate, | |
298 | __clk_get_name(clk)); | |
299 | WARN_ON(1); | |
300 | } | |
301 | ||
302 | if (tbl->state) | |
303 | if (clk_prepare_enable(clk)) { | |
304 | pr_err("%s: Failed to enable %s\n", __func__, | |
305 | __clk_get_name(clk)); | |
306 | WARN_ON(1); | |
307 | } | |
308 | } | |
309 | } | |
61fd290d | 310 | |
7ba256d2 | 311 | static const struct reset_control_ops rst_ops = { |
6d5b988e SW |
312 | .assert = tegra_clk_rst_assert, |
313 | .deassert = tegra_clk_rst_deassert, | |
4236e752 | 314 | .reset = tegra_clk_rst_reset, |
6d5b988e SW |
315 | }; |
316 | ||
317 | static struct reset_controller_dev rst_ctlr = { | |
318 | .ops = &rst_ops, | |
319 | .owner = THIS_MODULE, | |
320 | .of_reset_n_cells = 1, | |
321 | }; | |
322 | ||
5d797111 DO |
323 | void __init tegra_add_of_provider(struct device_node *np, |
324 | void *clk_src_onecell_get) | |
343a607c PDS |
325 | { |
326 | int i; | |
327 | ||
b1bc04a2 DO |
328 | tegra_car_np = np; |
329 | ||
343a607c PDS |
330 | for (i = 0; i < clk_num; i++) { |
331 | if (IS_ERR(clks[i])) { | |
332 | pr_err | |
333 | ("Tegra clk %d: register failed with %ld\n", | |
334 | i, PTR_ERR(clks[i])); | |
335 | } | |
336 | if (!clks[i]) | |
337 | clks[i] = ERR_PTR(-EINVAL); | |
338 | } | |
339 | ||
340 | clk_data.clks = clks; | |
341 | clk_data.clk_num = clk_num; | |
5d797111 | 342 | of_clk_add_provider(np, clk_src_onecell_get, &clk_data); |
6d5b988e SW |
343 | |
344 | rst_ctlr.of_node = np; | |
66b6f3d0 | 345 | rst_ctlr.nr_resets = periph_banks * 32 + num_special_reset; |
6d5b988e | 346 | reset_controller_register(&rst_ctlr); |
343a607c PDS |
347 | } |
348 | ||
66b6f3d0 MP |
349 | void __init tegra_init_special_resets(unsigned int num, |
350 | int (*assert)(unsigned long), | |
351 | int (*deassert)(unsigned long)) | |
352 | { | |
353 | num_special_reset = num; | |
354 | special_reset_assert = assert; | |
355 | special_reset_deassert = deassert; | |
356 | } | |
357 | ||
b1bc04a2 | 358 | void tegra_register_devclks(struct tegra_devclk *dev_clks, int num) |
73d37e4c PDS |
359 | { |
360 | int i; | |
361 | ||
362 | for (i = 0; i < num; i++, dev_clks++) | |
363 | clk_register_clkdev(clks[dev_clks->dt_id], dev_clks->con_id, | |
364 | dev_clks->dev_id); | |
9f0030c8 PDS |
365 | |
366 | for (i = 0; i < clk_num; i++) { | |
367 | if (!IS_ERR_OR_NULL(clks[i])) | |
368 | clk_register_clkdev(clks[i], __clk_get_name(clks[i]), | |
369 | "tegra-clk-debug"); | |
370 | } | |
73d37e4c PDS |
371 | } |
372 | ||
b8700d50 PDS |
373 | struct clk ** __init tegra_lookup_dt_id(int clk_id, |
374 | struct tegra_clk *tegra_clk) | |
375 | { | |
376 | if (tegra_clk[clk_id].present) | |
377 | return &clks[tegra_clk[clk_id].dt_id]; | |
378 | else | |
379 | return NULL; | |
380 | } | |
381 | ||
b1bc04a2 DO |
382 | static struct device_node *tegra_clk_get_of_node(struct clk_hw *hw) |
383 | { | |
384 | struct device_node *np; | |
385 | char *node_name; | |
386 | ||
387 | node_name = kstrdup(hw->init->name, GFP_KERNEL); | |
388 | if (!node_name) | |
389 | return NULL; | |
390 | ||
391 | strreplace(node_name, '_', '-'); | |
392 | ||
393 | for_each_child_of_node(tegra_car_np, np) { | |
394 | if (!strcmp(np->name, node_name)) | |
395 | break; | |
396 | } | |
397 | ||
398 | kfree(node_name); | |
399 | ||
400 | return np; | |
401 | } | |
402 | ||
403 | struct clk *tegra_clk_dev_register(struct clk_hw *hw) | |
404 | { | |
405 | struct platform_device *pdev, *parent; | |
406 | const char *dev_name = NULL; | |
407 | struct device *dev = NULL; | |
408 | struct device_node *np; | |
409 | ||
410 | np = tegra_clk_get_of_node(hw); | |
411 | ||
412 | if (!of_device_is_available(np)) | |
413 | goto put_node; | |
414 | ||
415 | dev_name = kasprintf(GFP_KERNEL, "tegra_clk_%s", hw->init->name); | |
416 | if (!dev_name) | |
417 | goto put_node; | |
418 | ||
419 | parent = of_find_device_by_node(tegra_car_np); | |
420 | if (parent) { | |
421 | pdev = of_platform_device_create(np, dev_name, &parent->dev); | |
422 | put_device(&parent->dev); | |
423 | ||
424 | if (!pdev) { | |
425 | pr_err("%s: failed to create device for %pOF\n", | |
426 | __func__, np); | |
427 | goto free_name; | |
428 | } | |
429 | ||
430 | dev = &pdev->dev; | |
431 | pm_runtime_enable(dev); | |
432 | } else { | |
433 | WARN(1, "failed to find device for %pOF\n", tegra_car_np); | |
434 | } | |
435 | ||
436 | free_name: | |
437 | kfree(dev_name); | |
438 | put_node: | |
439 | of_node_put(np); | |
440 | ||
441 | return clk_register(dev, hw); | |
442 | } | |
443 | ||
441f199a SW |
444 | tegra_clk_apply_init_table_func tegra_clk_apply_init_table; |
445 | ||
d0a57bd5 | 446 | static int __init tegra_clocks_apply_init_table(void) |
441f199a SW |
447 | { |
448 | if (!tegra_clk_apply_init_table) | |
d0a57bd5 | 449 | return 0; |
441f199a SW |
450 | |
451 | tegra_clk_apply_init_table(); | |
d0a57bd5 PDS |
452 | |
453 | return 0; | |
441f199a | 454 | } |
d0a57bd5 | 455 | arch_initcall(tegra_clocks_apply_init_table); |