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 | ||
f6da46a3 | 49 | osc = clk_register_fixed_rate(NULL, "osc", NULL, 0, *osc_freq); |
63cc5a4d TR |
50 | |
51 | dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m, clks); | |
de4f30fd PDS |
52 | if (!dt_clk) |
53 | return 0; | |
54 | ||
63cc5a4d TR |
55 | clk = clk_register_fixed_factor(NULL, "clk_m", "osc", |
56 | 0, 1, clk_m_div); | |
de4f30fd PDS |
57 | *dt_clk = clk; |
58 | ||
59 | /* pll_ref */ | |
60 | val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; | |
61 | pll_ref_div = 1 << val; | |
63cc5a4d | 62 | dt_clk = tegra_lookup_dt_id(tegra_clk_pll_ref, clks); |
de4f30fd PDS |
63 | if (!dt_clk) |
64 | return 0; | |
65 | ||
63cc5a4d | 66 | clk = clk_register_fixed_factor(NULL, "pll_ref", "osc", |
de4f30fd PDS |
67 | 0, 1, pll_ref_div); |
68 | *dt_clk = clk; | |
69 | ||
70 | if (pll_ref_freq) | |
71 | *pll_ref_freq = *osc_freq / pll_ref_div; | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks) | |
77 | { | |
78 | struct clk *clk; | |
79 | struct clk **dt_clk; | |
80 | ||
81 | /* clk_32k */ | |
82 | dt_clk = tegra_lookup_dt_id(tegra_clk_clk_32k, tegra_clks); | |
83 | if (dt_clk) { | |
f6da46a3 | 84 | clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, 0, 32768); |
de4f30fd PDS |
85 | *dt_clk = clk; |
86 | } | |
87 | ||
88 | /* clk_m_div2 */ | |
89 | dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m_div2, tegra_clks); | |
90 | if (dt_clk) { | |
91 | clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m", | |
92 | CLK_SET_RATE_PARENT, 1, 2); | |
93 | *dt_clk = clk; | |
94 | } | |
95 | ||
96 | /* clk_m_div4 */ | |
97 | dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m_div4, tegra_clks); | |
98 | if (dt_clk) { | |
99 | clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m", | |
100 | CLK_SET_RATE_PARENT, 1, 4); | |
101 | *dt_clk = clk; | |
102 | } | |
103 | } | |
50d4da9b SK |
104 | |
105 | void tegra_clk_osc_resume(void __iomem *clk_base) | |
106 | { | |
107 | u32 val; | |
108 | ||
109 | val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK; | |
110 | val |= osc_ctrl_ctx; | |
111 | writel_relaxed(val, clk_base + OSC_CTRL); | |
112 | fence_udelay(2, clk_base); | |
113 | } |