1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> |
4 | */ |
5 | |
6 | #include <linux/clk-provider.h> |
7 | #include <linux/clkdev.h> |
8 | #include <linux/clk/at91_pmc.h> |
9 | #include <linux/of.h> |
10 | #include <linux/mfd/syscon.h> |
11 | #include <linux/regmap.h> |
12 | #include <soc/at91/atmel-sfr.h> |
13 | |
14 | #include "pmc.h" |
15 | |
16 | /* |
17 | * The purpose of this clock is to generate a 480 MHz signal. A different |
18 | * rate can't be configured. |
19 | */ |
20 | #define UTMI_RATE 480000000 |
21 | |
22 | struct clk_utmi { |
23 | struct clk_hw hw; |
24 | struct regmap *regmap_pmc; |
25 | struct regmap *regmap_sfr; |
26 | struct at91_clk_pms pms; |
27 | }; |
28 | |
29 | #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) |
30 | |
31 | static inline bool clk_utmi_ready(struct regmap *regmap) |
32 | { |
33 | unsigned int status; |
34 | |
35 | regmap_read(map: regmap, AT91_PMC_SR, val: &status); |
36 | |
37 | return status & AT91_PMC_LOCKU; |
38 | } |
39 | |
40 | static int clk_utmi_prepare(struct clk_hw *hw) |
41 | { |
42 | struct clk_hw *hw_parent; |
43 | struct clk_utmi *utmi = to_clk_utmi(hw); |
44 | unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | |
45 | AT91_PMC_BIASEN; |
46 | unsigned int utmi_ref_clk_freq; |
47 | unsigned long parent_rate; |
48 | |
49 | /* |
50 | * If mainck rate is different from 12 MHz, we have to configure the |
51 | * FREQ field of the SFR_UTMICKTRIM register to generate properly |
52 | * the utmi clock. |
53 | */ |
54 | hw_parent = clk_hw_get_parent(hw); |
55 | parent_rate = clk_hw_get_rate(hw: hw_parent); |
56 | |
57 | switch (parent_rate) { |
58 | case 12000000: |
59 | utmi_ref_clk_freq = 0; |
60 | break; |
61 | case 16000000: |
62 | utmi_ref_clk_freq = 1; |
63 | break; |
64 | case 24000000: |
65 | utmi_ref_clk_freq = 2; |
66 | break; |
67 | /* |
68 | * Not supported on SAMA5D2 but it's not an issue since MAINCK |
69 | * maximum value is 24 MHz. |
70 | */ |
71 | case 48000000: |
72 | utmi_ref_clk_freq = 3; |
73 | break; |
74 | default: |
75 | pr_err("UTMICK: unsupported mainck rate\n" ); |
76 | return -EINVAL; |
77 | } |
78 | |
79 | if (utmi->regmap_sfr) { |
80 | regmap_update_bits(map: utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, |
81 | AT91_UTMICKTRIM_FREQ, val: utmi_ref_clk_freq); |
82 | } else if (utmi_ref_clk_freq) { |
83 | pr_err("UTMICK: sfr node required\n" ); |
84 | return -EINVAL; |
85 | } |
86 | |
87 | regmap_update_bits(map: utmi->regmap_pmc, AT91_CKGR_UCKR, mask: uckr, val: uckr); |
88 | |
89 | while (!clk_utmi_ready(regmap: utmi->regmap_pmc)) |
90 | cpu_relax(); |
91 | |
92 | return 0; |
93 | } |
94 | |
95 | static int clk_utmi_is_prepared(struct clk_hw *hw) |
96 | { |
97 | struct clk_utmi *utmi = to_clk_utmi(hw); |
98 | |
99 | return clk_utmi_ready(regmap: utmi->regmap_pmc); |
100 | } |
101 | |
102 | static void clk_utmi_unprepare(struct clk_hw *hw) |
103 | { |
104 | struct clk_utmi *utmi = to_clk_utmi(hw); |
105 | |
106 | regmap_update_bits(map: utmi->regmap_pmc, AT91_CKGR_UCKR, |
107 | AT91_PMC_UPLLEN, val: 0); |
108 | } |
109 | |
110 | static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, |
111 | unsigned long parent_rate) |
112 | { |
113 | /* UTMI clk rate is fixed. */ |
114 | return UTMI_RATE; |
115 | } |
116 | |
117 | static int clk_utmi_save_context(struct clk_hw *hw) |
118 | { |
119 | struct clk_utmi *utmi = to_clk_utmi(hw); |
120 | |
121 | utmi->pms.status = clk_utmi_is_prepared(hw); |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static void clk_utmi_restore_context(struct clk_hw *hw) |
127 | { |
128 | struct clk_utmi *utmi = to_clk_utmi(hw); |
129 | |
130 | if (utmi->pms.status) |
131 | clk_utmi_prepare(hw); |
132 | } |
133 | |
134 | static const struct clk_ops utmi_ops = { |
135 | .prepare = clk_utmi_prepare, |
136 | .unprepare = clk_utmi_unprepare, |
137 | .is_prepared = clk_utmi_is_prepared, |
138 | .recalc_rate = clk_utmi_recalc_rate, |
139 | .save_context = clk_utmi_save_context, |
140 | .restore_context = clk_utmi_restore_context, |
141 | }; |
142 | |
143 | static struct clk_hw * __init |
144 | at91_clk_register_utmi_internal(struct regmap *regmap_pmc, |
145 | struct regmap *regmap_sfr, |
146 | const char *name, const char *parent_name, |
147 | struct clk_hw *parent_hw, |
148 | const struct clk_ops *ops, unsigned long flags) |
149 | { |
150 | struct clk_utmi *utmi; |
151 | struct clk_hw *hw; |
152 | struct clk_init_data init = {}; |
153 | int ret; |
154 | |
155 | if (!(parent_name || parent_hw)) |
156 | return ERR_PTR(error: -EINVAL); |
157 | |
158 | utmi = kzalloc(size: sizeof(*utmi), GFP_KERNEL); |
159 | if (!utmi) |
160 | return ERR_PTR(error: -ENOMEM); |
161 | |
162 | init.name = name; |
163 | init.ops = ops; |
164 | if (parent_hw) |
165 | init.parent_hws = (const struct clk_hw **)&parent_hw; |
166 | else |
167 | init.parent_names = &parent_name; |
168 | init.num_parents = 1; |
169 | init.flags = flags; |
170 | |
171 | utmi->hw.init = &init; |
172 | utmi->regmap_pmc = regmap_pmc; |
173 | utmi->regmap_sfr = regmap_sfr; |
174 | |
175 | hw = &utmi->hw; |
176 | ret = clk_hw_register(NULL, hw: &utmi->hw); |
177 | if (ret) { |
178 | kfree(objp: utmi); |
179 | hw = ERR_PTR(error: ret); |
180 | } |
181 | |
182 | return hw; |
183 | } |
184 | |
185 | struct clk_hw * __init |
186 | at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, |
187 | const char *name, const char *parent_name, |
188 | struct clk_hw *parent_hw) |
189 | { |
190 | return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name, |
191 | parent_name, parent_hw, ops: &utmi_ops, CLK_SET_RATE_GATE); |
192 | } |
193 | |
194 | static int clk_utmi_sama7g5_prepare(struct clk_hw *hw) |
195 | { |
196 | struct clk_utmi *utmi = to_clk_utmi(hw); |
197 | struct clk_hw *hw_parent; |
198 | unsigned long parent_rate; |
199 | unsigned int val; |
200 | |
201 | hw_parent = clk_hw_get_parent(hw); |
202 | parent_rate = clk_hw_get_rate(hw: hw_parent); |
203 | |
204 | switch (parent_rate) { |
205 | case 16000000: |
206 | val = 0; |
207 | break; |
208 | case 20000000: |
209 | val = 2; |
210 | break; |
211 | case 24000000: |
212 | val = 3; |
213 | break; |
214 | case 32000000: |
215 | val = 5; |
216 | break; |
217 | default: |
218 | pr_err("UTMICK: unsupported main_xtal rate\n" ); |
219 | return -EINVAL; |
220 | } |
221 | |
222 | regmap_write(map: utmi->regmap_pmc, AT91_PMC_XTALF, val); |
223 | |
224 | return 0; |
225 | |
226 | } |
227 | |
228 | static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw) |
229 | { |
230 | struct clk_utmi *utmi = to_clk_utmi(hw); |
231 | struct clk_hw *hw_parent; |
232 | unsigned long parent_rate; |
233 | unsigned int val; |
234 | |
235 | hw_parent = clk_hw_get_parent(hw); |
236 | parent_rate = clk_hw_get_rate(hw: hw_parent); |
237 | |
238 | regmap_read(map: utmi->regmap_pmc, AT91_PMC_XTALF, val: &val); |
239 | switch (val & 0x7) { |
240 | case 0: |
241 | if (parent_rate == 16000000) |
242 | return 1; |
243 | break; |
244 | case 2: |
245 | if (parent_rate == 20000000) |
246 | return 1; |
247 | break; |
248 | case 3: |
249 | if (parent_rate == 24000000) |
250 | return 1; |
251 | break; |
252 | case 5: |
253 | if (parent_rate == 32000000) |
254 | return 1; |
255 | break; |
256 | default: |
257 | break; |
258 | } |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static int clk_utmi_sama7g5_save_context(struct clk_hw *hw) |
264 | { |
265 | struct clk_utmi *utmi = to_clk_utmi(hw); |
266 | |
267 | utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw); |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw) |
273 | { |
274 | struct clk_utmi *utmi = to_clk_utmi(hw); |
275 | |
276 | if (utmi->pms.status) |
277 | clk_utmi_sama7g5_prepare(hw); |
278 | } |
279 | |
280 | static const struct clk_ops sama7g5_utmi_ops = { |
281 | .prepare = clk_utmi_sama7g5_prepare, |
282 | .is_prepared = clk_utmi_sama7g5_is_prepared, |
283 | .recalc_rate = clk_utmi_recalc_rate, |
284 | .save_context = clk_utmi_sama7g5_save_context, |
285 | .restore_context = clk_utmi_sama7g5_restore_context, |
286 | }; |
287 | |
288 | struct clk_hw * __init |
289 | at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name, |
290 | const char *parent_name, struct clk_hw *parent_hw) |
291 | { |
292 | return at91_clk_register_utmi_internal(regmap_pmc, NULL, name, |
293 | parent_name, parent_hw, ops: &sama7g5_utmi_ops, flags: 0); |
294 | } |
295 | |