Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
f090fb37 BB |
2 | /* |
3 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | |
f090fb37 BB |
4 | */ |
5 | ||
6 | #include <linux/clk-provider.h> | |
7 | #include <linux/clkdev.h> | |
8 | #include <linux/clk/at91_pmc.h> | |
f090fb37 | 9 | #include <linux/of.h> |
1bdf0232 BB |
10 | #include <linux/mfd/syscon.h> |
11 | #include <linux/regmap.h> | |
92041a9f | 12 | #include <soc/at91/atmel-sfr.h> |
f090fb37 BB |
13 | |
14 | #include "pmc.h" | |
15 | ||
92041a9f LD |
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 | |
f090fb37 BB |
21 | |
22 | struct clk_utmi { | |
23 | struct clk_hw hw; | |
92041a9f LD |
24 | struct regmap *regmap_pmc; |
25 | struct regmap *regmap_sfr; | |
36971566 | 26 | struct at91_clk_pms pms; |
f090fb37 BB |
27 | }; |
28 | ||
29 | #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) | |
30 | ||
1bdf0232 BB |
31 | static inline bool clk_utmi_ready(struct regmap *regmap) |
32 | { | |
33 | unsigned int status; | |
34 | ||
35 | regmap_read(regmap, AT91_PMC_SR, &status); | |
36 | ||
37 | return status & AT91_PMC_LOCKU; | |
38 | } | |
39 | ||
f090fb37 BB |
40 | static int clk_utmi_prepare(struct clk_hw *hw) |
41 | { | |
92041a9f | 42 | struct clk_hw *hw_parent; |
f090fb37 | 43 | struct clk_utmi *utmi = to_clk_utmi(hw); |
1bdf0232 BB |
44 | unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | |
45 | AT91_PMC_BIASEN; | |
92041a9f LD |
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_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 | } | |
f090fb37 | 78 | |
92041a9f LD |
79 | if (utmi->regmap_sfr) { |
80 | regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, | |
81 | AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); | |
82 | } else if (utmi_ref_clk_freq) { | |
83 | pr_err("UTMICK: sfr node required\n"); | |
84 | return -EINVAL; | |
85 | } | |
f090fb37 | 86 | |
92041a9f LD |
87 | regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); |
88 | ||
89 | while (!clk_utmi_ready(utmi->regmap_pmc)) | |
99a81706 | 90 | cpu_relax(); |
f090fb37 BB |
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); | |
f090fb37 | 98 | |
92041a9f | 99 | return clk_utmi_ready(utmi->regmap_pmc); |
f090fb37 BB |
100 | } |
101 | ||
102 | static void clk_utmi_unprepare(struct clk_hw *hw) | |
103 | { | |
104 | struct clk_utmi *utmi = to_clk_utmi(hw); | |
f090fb37 | 105 | |
92041a9f LD |
106 | regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, |
107 | AT91_PMC_UPLLEN, 0); | |
f090fb37 BB |
108 | } |
109 | ||
110 | static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, | |
111 | unsigned long parent_rate) | |
112 | { | |
92041a9f LD |
113 | /* UTMI clk rate is fixed. */ |
114 | return UTMI_RATE; | |
f090fb37 BB |
115 | } |
116 | ||
36971566 CB |
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 | ||
f090fb37 BB |
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, | |
36971566 CB |
139 | .save_context = clk_utmi_save_context, |
140 | .restore_context = clk_utmi_restore_context, | |
f090fb37 BB |
141 | }; |
142 | ||
ef396df9 CB |
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, | |
077782e3 | 147 | struct clk_hw *parent_hw, |
ef396df9 | 148 | const struct clk_ops *ops, unsigned long flags) |
f090fb37 | 149 | { |
f090fb37 | 150 | struct clk_utmi *utmi; |
f5644f10 | 151 | struct clk_hw *hw; |
077782e3 | 152 | struct clk_init_data init = {}; |
f5644f10 | 153 | int ret; |
f090fb37 | 154 | |
077782e3 CB |
155 | if (!(parent_name || parent_hw)) |
156 | return ERR_PTR(-EINVAL); | |
157 | ||
f090fb37 BB |
158 | utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); |
159 | if (!utmi) | |
160 | return ERR_PTR(-ENOMEM); | |
161 | ||
162 | init.name = name; | |
ef396df9 | 163 | init.ops = ops; |
077782e3 CB |
164 | if (parent_hw) { |
165 | init.parent_hws = parent_hw ? (const struct clk_hw **)&parent_hw : NULL; | |
166 | init.num_parents = parent_hw ? 1 : 0; | |
167 | } else { | |
168 | init.parent_names = parent_name ? &parent_name : NULL; | |
169 | init.num_parents = parent_name ? 1 : 0; | |
170 | } | |
ef396df9 | 171 | init.flags = flags; |
f090fb37 BB |
172 | |
173 | utmi->hw.init = &init; | |
92041a9f LD |
174 | utmi->regmap_pmc = regmap_pmc; |
175 | utmi->regmap_sfr = regmap_sfr; | |
f090fb37 | 176 | |
f5644f10 SB |
177 | hw = &utmi->hw; |
178 | ret = clk_hw_register(NULL, &utmi->hw); | |
179 | if (ret) { | |
f090fb37 | 180 | kfree(utmi); |
f5644f10 SB |
181 | hw = ERR_PTR(ret); |
182 | } | |
f090fb37 | 183 | |
f5644f10 | 184 | return hw; |
f090fb37 | 185 | } |
ef396df9 CB |
186 | |
187 | struct clk_hw * __init | |
188 | at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, | |
077782e3 CB |
189 | const char *name, const char *parent_name, |
190 | struct clk_hw *parent_hw) | |
ef396df9 CB |
191 | { |
192 | return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name, | |
077782e3 | 193 | parent_name, parent_hw, &utmi_ops, CLK_SET_RATE_GATE); |
ef396df9 CB |
194 | } |
195 | ||
196 | static int clk_utmi_sama7g5_prepare(struct clk_hw *hw) | |
197 | { | |
198 | struct clk_utmi *utmi = to_clk_utmi(hw); | |
199 | struct clk_hw *hw_parent; | |
200 | unsigned long parent_rate; | |
201 | unsigned int val; | |
202 | ||
203 | hw_parent = clk_hw_get_parent(hw); | |
204 | parent_rate = clk_hw_get_rate(hw_parent); | |
205 | ||
206 | switch (parent_rate) { | |
207 | case 16000000: | |
208 | val = 0; | |
209 | break; | |
210 | case 20000000: | |
211 | val = 2; | |
212 | break; | |
213 | case 24000000: | |
214 | val = 3; | |
215 | break; | |
216 | case 32000000: | |
217 | val = 5; | |
218 | break; | |
219 | default: | |
220 | pr_err("UTMICK: unsupported main_xtal rate\n"); | |
221 | return -EINVAL; | |
222 | } | |
223 | ||
224 | regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val); | |
225 | ||
226 | return 0; | |
227 | ||
228 | } | |
229 | ||
230 | static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw) | |
231 | { | |
232 | struct clk_utmi *utmi = to_clk_utmi(hw); | |
233 | struct clk_hw *hw_parent; | |
234 | unsigned long parent_rate; | |
235 | unsigned int val; | |
236 | ||
237 | hw_parent = clk_hw_get_parent(hw); | |
238 | parent_rate = clk_hw_get_rate(hw_parent); | |
239 | ||
240 | regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val); | |
241 | switch (val & 0x7) { | |
242 | case 0: | |
243 | if (parent_rate == 16000000) | |
244 | return 1; | |
245 | break; | |
246 | case 2: | |
247 | if (parent_rate == 20000000) | |
248 | return 1; | |
249 | break; | |
250 | case 3: | |
251 | if (parent_rate == 24000000) | |
252 | return 1; | |
253 | break; | |
254 | case 5: | |
255 | if (parent_rate == 32000000) | |
256 | return 1; | |
257 | break; | |
258 | default: | |
259 | break; | |
260 | } | |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
36971566 CB |
265 | static int clk_utmi_sama7g5_save_context(struct clk_hw *hw) |
266 | { | |
267 | struct clk_utmi *utmi = to_clk_utmi(hw); | |
268 | ||
269 | utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw); | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
274 | static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw) | |
275 | { | |
276 | struct clk_utmi *utmi = to_clk_utmi(hw); | |
277 | ||
278 | if (utmi->pms.status) | |
279 | clk_utmi_sama7g5_prepare(hw); | |
280 | } | |
281 | ||
ef396df9 CB |
282 | static const struct clk_ops sama7g5_utmi_ops = { |
283 | .prepare = clk_utmi_sama7g5_prepare, | |
284 | .is_prepared = clk_utmi_sama7g5_is_prepared, | |
285 | .recalc_rate = clk_utmi_recalc_rate, | |
36971566 CB |
286 | .save_context = clk_utmi_sama7g5_save_context, |
287 | .restore_context = clk_utmi_sama7g5_restore_context, | |
ef396df9 CB |
288 | }; |
289 | ||
290 | struct clk_hw * __init | |
291 | at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name, | |
077782e3 | 292 | const char *parent_name, struct clk_hw *parent_hw) |
ef396df9 CB |
293 | { |
294 | return at91_clk_register_utmi_internal(regmap_pmc, NULL, name, | |
077782e3 | 295 | parent_name, parent_hw, &sama7g5_utmi_ops, 0); |
ef396df9 | 296 | } |