Commit | Line | Data |
---|---|---|
9952f691 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
de4f30fd PDS |
2 | /* |
3 | * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. | |
de4f30fd PDS |
4 | */ |
5 | ||
6 | #include <linux/io.h> | |
de4f30fd PDS |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/of.h> | |
9 | #include <linux/of_address.h> | |
10 | #include <linux/delay.h> | |
11 | #include <linux/export.h> | |
12 | #include <linux/clk/tegra.h> | |
13 | ||
14 | #include "clk.h" | |
15 | #include "clk-id.h" | |
16 | ||
17 | #define OSC_CTRL 0x50 | |
18 | #define OSC_CTRL_OSC_FREQ_SHIFT 28 | |
19 | #define OSC_CTRL_PLL_REF_DIV_SHIFT 26 | |
50d4da9b SK |
20 | #define OSC_CTRL_MASK (0x3f2 | \ |
21 | (0xf << OSC_CTRL_OSC_FREQ_SHIFT)) | |
22 | ||
23 | static u32 osc_ctrl_ctx; | |
de4f30fd | 24 | |
63cc5a4d TR |
25 | int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks, |
26 | unsigned long *input_freqs, unsigned int num, | |
27 | unsigned int clk_m_div, unsigned long *osc_freq, | |
28 | unsigned long *pll_ref_freq) | |
de4f30fd | 29 | { |
63cc5a4d | 30 | struct clk *clk, *osc; |
de4f30fd PDS |
31 | struct clk **dt_clk; |
32 | u32 val, pll_ref_div; | |
33 | unsigned osc_idx; | |
34 | ||
35 | val = readl_relaxed(clk_base + OSC_CTRL); | |
50d4da9b | 36 | osc_ctrl_ctx = val & OSC_CTRL_MASK; |
de4f30fd PDS |
37 | osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT; |
38 | ||
39 | if (osc_idx < num) | |
40 | *osc_freq = input_freqs[osc_idx]; | |
41 | else | |
42 | *osc_freq = 0; | |
43 | ||
44 | if (!*osc_freq) { | |
45 | WARN_ON(1); | |
46 | return -EINVAL; | |
47 | } | |
48 | ||
2b50e49b SK |
49 | dt_clk = tegra_lookup_dt_id(tegra_clk_osc, clks); |
50 | if (!dt_clk) | |
51 | return 0; | |
52 | ||
f6da46a3 | 53 | osc = clk_register_fixed_rate(NULL, "osc", NULL, 0, *osc_freq); |
2b50e49b | 54 | *dt_clk = osc; |
63cc5a4d | 55 | |
9a85eb4d SK |
56 | /* osc_div2 */ |
57 | dt_clk = tegra_lookup_dt_id(tegra_clk_osc_div2, clks); | |
58 | if (dt_clk) { | |
59 | clk = clk_register_fixed_factor(NULL, "osc_div2", "osc", | |
60 | 0, 1, 2); | |
61 | *dt_clk = clk; | |
62 | } | |
63 | ||
64 | /* osc_div4 */ | |
65 | dt_clk = tegra_lookup_dt_id(tegra_clk_osc_div4, clks); | |
66 | if (dt_clk) { | |
67 | clk = clk_register_fixed_factor(NULL, "osc_div4", "osc", | |
68 | 0, 1, 4); | |
69 | *dt_clk = clk; | |
70 | } | |
71 | ||
63cc5a4d | 72 | dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m, clks); |
de4f30fd PDS |
73 | if (!dt_clk) |
74 | return 0; | |
75 | ||
63cc5a4d TR |
76 | clk = clk_register_fixed_factor(NULL, "clk_m", "osc", |
77 | 0, 1, clk_m_div); | |
de4f30fd PDS |
78 | *dt_clk = clk; |
79 | ||
80 | /* pll_ref */ | |
81 | val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; | |
82 | pll_ref_div = 1 << val; | |
63cc5a4d | 83 | dt_clk = tegra_lookup_dt_id(tegra_clk_pll_ref, clks); |
de4f30fd PDS |
84 | if (!dt_clk) |
85 | return 0; | |
86 | ||
63cc5a4d | 87 | clk = clk_register_fixed_factor(NULL, "pll_ref", "osc", |
de4f30fd PDS |
88 | 0, 1, pll_ref_div); |
89 | *dt_clk = clk; | |
90 | ||
91 | if (pll_ref_freq) | |
92 | *pll_ref_freq = *osc_freq / pll_ref_div; | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks) | |
98 | { | |
99 | struct clk *clk; | |
100 | struct clk **dt_clk; | |
101 | ||
102 | /* clk_32k */ | |
103 | dt_clk = tegra_lookup_dt_id(tegra_clk_clk_32k, tegra_clks); | |
104 | if (dt_clk) { | |
f6da46a3 | 105 | clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, 0, 32768); |
de4f30fd PDS |
106 | *dt_clk = clk; |
107 | } | |
de4f30fd | 108 | } |
50d4da9b SK |
109 | |
110 | void tegra_clk_osc_resume(void __iomem *clk_base) | |
111 | { | |
112 | u32 val; | |
113 | ||
114 | val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK; | |
115 | val |= osc_ctrl_ctx; | |
116 | writel_relaxed(val, clk_base + OSC_CTRL); | |
117 | fence_udelay(2, clk_base); | |
118 | } |