1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/bits.h> |
3 | #include <linux/clk.h> |
4 | #include <linux/clk-provider.h> |
5 | #include <linux/err.h> |
6 | #include <linux/io.h> |
7 | #include <linux/module.h> |
8 | #include <linux/of.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/spinlock.h> |
11 | #include "clk.h" |
12 | |
13 | #define CCM_CCDR 0x4 |
14 | #define CCDR_MMDC_CH0_MASK BIT(17) |
15 | #define CCDR_MMDC_CH1_MASK BIT(16) |
16 | |
17 | DEFINE_SPINLOCK(imx_ccm_lock); |
18 | EXPORT_SYMBOL_GPL(imx_ccm_lock); |
19 | |
20 | bool mcore_booted; |
21 | EXPORT_SYMBOL_GPL(mcore_booted); |
22 | |
23 | void imx_unregister_hw_clocks(struct clk_hw *hws[], unsigned int count) |
24 | { |
25 | unsigned int i; |
26 | |
27 | for (i = 0; i < count; i++) |
28 | clk_hw_unregister(hw: hws[i]); |
29 | } |
30 | EXPORT_SYMBOL_GPL(imx_unregister_hw_clocks); |
31 | |
32 | void imx_mmdc_mask_handshake(void __iomem *ccm_base, |
33 | unsigned int chn) |
34 | { |
35 | unsigned int reg; |
36 | |
37 | reg = readl_relaxed(ccm_base + CCM_CCDR); |
38 | reg |= chn == 0 ? CCDR_MMDC_CH0_MASK : CCDR_MMDC_CH1_MASK; |
39 | writel_relaxed(reg, ccm_base + CCM_CCDR); |
40 | } |
41 | |
42 | void imx_check_clocks(struct clk *clks[], unsigned int count) |
43 | { |
44 | unsigned i; |
45 | |
46 | for (i = 0; i < count; i++) |
47 | if (IS_ERR(ptr: clks[i])) |
48 | pr_err("i.MX clk %u: register failed with %ld\n" , |
49 | i, PTR_ERR(clks[i])); |
50 | } |
51 | |
52 | void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count) |
53 | { |
54 | unsigned int i; |
55 | |
56 | for (i = 0; i < count; i++) |
57 | if (IS_ERR(ptr: clks[i])) |
58 | pr_err("i.MX clk %u: register failed with %ld\n" , |
59 | i, PTR_ERR(clks[i])); |
60 | } |
61 | EXPORT_SYMBOL_GPL(imx_check_clk_hws); |
62 | |
63 | static struct clk *imx_obtain_fixed_clock_from_dt(const char *name) |
64 | { |
65 | struct of_phandle_args phandle; |
66 | struct clk *clk = ERR_PTR(error: -ENODEV); |
67 | char *path; |
68 | |
69 | path = kasprintf(GFP_KERNEL, fmt: "/clocks/%s" , name); |
70 | if (!path) |
71 | return ERR_PTR(error: -ENOMEM); |
72 | |
73 | phandle.np = of_find_node_by_path(path); |
74 | kfree(objp: path); |
75 | |
76 | if (phandle.np) { |
77 | clk = of_clk_get_from_provider(clkspec: &phandle); |
78 | of_node_put(node: phandle.np); |
79 | } |
80 | return clk; |
81 | } |
82 | |
83 | struct clk *imx_obtain_fixed_clock( |
84 | const char *name, unsigned long rate) |
85 | { |
86 | struct clk *clk; |
87 | |
88 | clk = imx_obtain_fixed_clock_from_dt(name); |
89 | if (IS_ERR(ptr: clk)) |
90 | clk = imx_clk_fixed(name, rate); |
91 | return clk; |
92 | } |
93 | |
94 | struct clk_hw *imx_obtain_fixed_clock_hw( |
95 | const char *name, unsigned long rate) |
96 | { |
97 | struct clk *clk; |
98 | |
99 | clk = imx_obtain_fixed_clock_from_dt(name); |
100 | if (IS_ERR(ptr: clk)) |
101 | clk = imx_clk_fixed(name, rate); |
102 | return __clk_get_hw(clk); |
103 | } |
104 | |
105 | struct clk_hw *imx_obtain_fixed_of_clock(struct device_node *np, |
106 | const char *name, unsigned long rate) |
107 | { |
108 | struct clk *clk = of_clk_get_by_name(np, name); |
109 | struct clk_hw *hw; |
110 | |
111 | if (IS_ERR(ptr: clk)) |
112 | hw = imx_obtain_fixed_clock_hw(name, rate); |
113 | else |
114 | hw = __clk_get_hw(clk); |
115 | |
116 | return hw; |
117 | } |
118 | |
119 | struct clk_hw *imx_get_clk_hw_by_name(struct device_node *np, const char *name) |
120 | { |
121 | struct clk *clk; |
122 | |
123 | clk = of_clk_get_by_name(np, name); |
124 | if (IS_ERR(ptr: clk)) |
125 | return ERR_PTR(error: -ENOENT); |
126 | |
127 | return __clk_get_hw(clk); |
128 | } |
129 | EXPORT_SYMBOL_GPL(imx_get_clk_hw_by_name); |
130 | |
131 | /* |
132 | * This fixups the register CCM_CSCMR1 write value. |
133 | * The write/read/divider values of the aclk_podf field |
134 | * of that register have the relationship described by |
135 | * the following table: |
136 | * |
137 | * write value read value divider |
138 | * 3b'000 3b'110 7 |
139 | * 3b'001 3b'111 8 |
140 | * 3b'010 3b'100 5 |
141 | * 3b'011 3b'101 6 |
142 | * 3b'100 3b'010 3 |
143 | * 3b'101 3b'011 4 |
144 | * 3b'110 3b'000 1 |
145 | * 3b'111 3b'001 2(default) |
146 | * |
147 | * That's why we do the xor operation below. |
148 | */ |
149 | #define CSCMR1_FIXUP 0x00600000 |
150 | |
151 | void imx_cscmr1_fixup(u32 *val) |
152 | { |
153 | *val ^= CSCMR1_FIXUP; |
154 | return; |
155 | } |
156 | |
157 | #ifndef MODULE |
158 | |
159 | static bool imx_keep_uart_clocks; |
160 | static int imx_enabled_uart_clocks; |
161 | static struct clk **imx_uart_clocks; |
162 | |
163 | static int __init imx_keep_uart_clocks_param(char *str) |
164 | { |
165 | imx_keep_uart_clocks = 1; |
166 | |
167 | return 0; |
168 | } |
169 | __setup_param("earlycon" , imx_keep_uart_earlycon, |
170 | imx_keep_uart_clocks_param, 0); |
171 | __setup_param("earlyprintk" , imx_keep_uart_earlyprintk, |
172 | imx_keep_uart_clocks_param, 0); |
173 | |
174 | void imx_register_uart_clocks(void) |
175 | { |
176 | unsigned int num __maybe_unused; |
177 | |
178 | imx_enabled_uart_clocks = 0; |
179 | |
180 | /* i.MX boards use device trees now. For build tests without CONFIG_OF, do nothing */ |
181 | #ifdef CONFIG_OF |
182 | if (imx_keep_uart_clocks) { |
183 | int i; |
184 | |
185 | num = of_clk_get_parent_count(np: of_stdout); |
186 | if (!num) |
187 | return; |
188 | |
189 | if (!of_stdout) |
190 | return; |
191 | |
192 | imx_uart_clocks = kcalloc(n: num, size: sizeof(struct clk *), GFP_KERNEL); |
193 | if (!imx_uart_clocks) |
194 | return; |
195 | |
196 | for (i = 0; i < num; i++) { |
197 | imx_uart_clocks[imx_enabled_uart_clocks] = of_clk_get(np: of_stdout, index: i); |
198 | |
199 | /* Stop if there are no more of_stdout references */ |
200 | if (IS_ERR(ptr: imx_uart_clocks[imx_enabled_uart_clocks])) |
201 | return; |
202 | |
203 | /* Only enable the clock if it's not NULL */ |
204 | if (imx_uart_clocks[imx_enabled_uart_clocks]) |
205 | clk_prepare_enable(clk: imx_uart_clocks[imx_enabled_uart_clocks++]); |
206 | } |
207 | } |
208 | #endif |
209 | } |
210 | |
211 | static int __init imx_clk_disable_uart(void) |
212 | { |
213 | if (imx_keep_uart_clocks && imx_enabled_uart_clocks) { |
214 | int i; |
215 | |
216 | for (i = 0; i < imx_enabled_uart_clocks; i++) { |
217 | clk_disable_unprepare(clk: imx_uart_clocks[i]); |
218 | clk_put(clk: imx_uart_clocks[i]); |
219 | } |
220 | } |
221 | |
222 | kfree(objp: imx_uart_clocks); |
223 | |
224 | return 0; |
225 | } |
226 | late_initcall_sync(imx_clk_disable_uart); |
227 | #endif |
228 | |
229 | MODULE_LICENSE("GPL v2" ); |
230 | |