]>
Commit | Line | Data |
---|---|---|
30b8e27e PW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2018-2019 SiFive, Inc. | |
4 | * Wesley Terpstra | |
5 | * Paul Walmsley | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * The FU540 PRCI implements clock and reset control for the SiFive | |
17 | * FU540-C000 chip. This driver assumes that it has sole control | |
18 | * over all PRCI resources. | |
19 | * | |
20 | * This driver is based on the PRCI driver written by Wesley Terpstra: | |
21 | * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60 | |
22 | * | |
23 | * References: | |
24 | * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset" | |
25 | */ | |
26 | ||
27 | #include <dt-bindings/clock/sifive-fu540-prci.h> | |
28 | #include <linux/clkdev.h> | |
29 | #include <linux/clk-provider.h> | |
30 | #include <linux/clk/analogbits-wrpll-cln28hpc.h> | |
31 | #include <linux/delay.h> | |
32 | #include <linux/err.h> | |
62e59c4e | 33 | #include <linux/io.h> |
30b8e27e PW |
34 | #include <linux/module.h> |
35 | #include <linux/of.h> | |
36 | #include <linux/of_clk.h> | |
37 | #include <linux/platform_device.h> | |
38 | #include <linux/slab.h> | |
39 | ||
40 | /* | |
41 | * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects: | |
42 | * hfclk and rtcclk | |
43 | */ | |
44 | #define EXPECTED_CLK_PARENT_COUNT 2 | |
45 | ||
46 | /* | |
47 | * Register offsets and bitmasks | |
48 | */ | |
49 | ||
50 | /* COREPLLCFG0 */ | |
51 | #define PRCI_COREPLLCFG0_OFFSET 0x4 | |
52 | # define PRCI_COREPLLCFG0_DIVR_SHIFT 0 | |
53 | # define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT) | |
54 | # define PRCI_COREPLLCFG0_DIVF_SHIFT 6 | |
55 | # define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT) | |
56 | # define PRCI_COREPLLCFG0_DIVQ_SHIFT 15 | |
57 | # define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT) | |
58 | # define PRCI_COREPLLCFG0_RANGE_SHIFT 18 | |
59 | # define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT) | |
60 | # define PRCI_COREPLLCFG0_BYPASS_SHIFT 24 | |
61 | # define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT) | |
62 | # define PRCI_COREPLLCFG0_FSE_SHIFT 25 | |
63 | # define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT) | |
64 | # define PRCI_COREPLLCFG0_LOCK_SHIFT 31 | |
65 | # define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT) | |
66 | ||
67 | /* DDRPLLCFG0 */ | |
68 | #define PRCI_DDRPLLCFG0_OFFSET 0xc | |
69 | # define PRCI_DDRPLLCFG0_DIVR_SHIFT 0 | |
70 | # define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT) | |
71 | # define PRCI_DDRPLLCFG0_DIVF_SHIFT 6 | |
72 | # define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT) | |
73 | # define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15 | |
74 | # define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT) | |
75 | # define PRCI_DDRPLLCFG0_RANGE_SHIFT 18 | |
76 | # define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT) | |
77 | # define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24 | |
78 | # define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT) | |
79 | # define PRCI_DDRPLLCFG0_FSE_SHIFT 25 | |
80 | # define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT) | |
81 | # define PRCI_DDRPLLCFG0_LOCK_SHIFT 31 | |
82 | # define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT) | |
83 | ||
84 | /* DDRPLLCFG1 */ | |
85 | #define PRCI_DDRPLLCFG1_OFFSET 0x10 | |
86 | # define PRCI_DDRPLLCFG1_CKE_SHIFT 24 | |
87 | # define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT) | |
88 | ||
89 | /* GEMGXLPLLCFG0 */ | |
90 | #define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c | |
91 | # define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0 | |
92 | # define PRCI_GEMGXLPLLCFG0_DIVR_MASK (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT) | |
93 | # define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6 | |
94 | # define PRCI_GEMGXLPLLCFG0_DIVF_MASK (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT) | |
95 | # define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15 | |
96 | # define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT) | |
97 | # define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18 | |
98 | # define PRCI_GEMGXLPLLCFG0_RANGE_MASK (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT) | |
99 | # define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24 | |
100 | # define PRCI_GEMGXLPLLCFG0_BYPASS_MASK (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT) | |
101 | # define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25 | |
102 | # define PRCI_GEMGXLPLLCFG0_FSE_MASK (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT) | |
103 | # define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31 | |
104 | # define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT) | |
105 | ||
106 | /* GEMGXLPLLCFG1 */ | |
107 | #define PRCI_GEMGXLPLLCFG1_OFFSET 0x20 | |
108 | # define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24 | |
109 | # define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT) | |
110 | ||
111 | /* CORECLKSEL */ | |
112 | #define PRCI_CORECLKSEL_OFFSET 0x24 | |
113 | # define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0 | |
114 | # define PRCI_CORECLKSEL_CORECLKSEL_MASK (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT) | |
115 | ||
116 | /* DEVICESRESETREG */ | |
117 | #define PRCI_DEVICESRESETREG_OFFSET 0x28 | |
118 | # define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0 | |
119 | # define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT) | |
120 | # define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1 | |
121 | # define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT) | |
122 | # define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2 | |
123 | # define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT) | |
124 | # define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3 | |
125 | # define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT) | |
126 | # define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5 | |
127 | # define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT) | |
128 | ||
129 | /* CLKMUXSTATUSREG */ | |
130 | #define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c | |
131 | # define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1 | |
132 | # define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT) | |
133 | ||
134 | /* | |
135 | * Private structures | |
136 | */ | |
137 | ||
138 | /** | |
139 | * struct __prci_data - per-device-instance data | |
140 | * @va: base virtual address of the PRCI IP block | |
141 | * @hw_clks: encapsulates struct clk_hw records | |
142 | * | |
143 | * PRCI per-device instance data | |
144 | */ | |
145 | struct __prci_data { | |
146 | void __iomem *va; | |
147 | struct clk_hw_onecell_data hw_clks; | |
148 | }; | |
149 | ||
150 | /** | |
151 | * struct __prci_wrpll_data - WRPLL configuration and integration data | |
152 | * @c: WRPLL current configuration record | |
153 | * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL) | |
154 | * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL) | |
155 | * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address | |
156 | * | |
157 | * @enable_bypass and @disable_bypass are used for WRPLL instances | |
158 | * that contain a separate external glitchless clock mux downstream | |
159 | * from the PLL. The WRPLL internal bypass mux is not glitchless. | |
160 | */ | |
161 | struct __prci_wrpll_data { | |
162 | struct wrpll_cfg c; | |
163 | void (*enable_bypass)(struct __prci_data *pd); | |
164 | void (*disable_bypass)(struct __prci_data *pd); | |
165 | u8 cfg0_offs; | |
166 | }; | |
167 | ||
168 | /** | |
169 | * struct __prci_clock - describes a clock device managed by PRCI | |
170 | * @name: user-readable clock name string - should match the manual | |
171 | * @parent_name: parent name for this clock | |
172 | * @ops: struct clk_ops for the Linux clock framework to use for control | |
173 | * @hw: Linux-private clock data | |
174 | * @pwd: WRPLL-specific data, associated with this clock (if not NULL) | |
175 | * @pd: PRCI-specific data associated with this clock (if not NULL) | |
176 | * | |
177 | * PRCI clock data. Used by the PRCI driver to register PRCI-provided | |
178 | * clocks to the Linux clock infrastructure. | |
179 | */ | |
180 | struct __prci_clock { | |
181 | const char *name; | |
182 | const char *parent_name; | |
183 | const struct clk_ops *ops; | |
184 | struct clk_hw hw; | |
185 | struct __prci_wrpll_data *pwd; | |
186 | struct __prci_data *pd; | |
187 | }; | |
188 | ||
189 | #define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw) | |
190 | ||
191 | /* | |
192 | * Private functions | |
193 | */ | |
194 | ||
195 | /** | |
196 | * __prci_readl() - read from a PRCI register | |
197 | * @pd: PRCI context | |
198 | * @offs: register offset to read from (in bytes, from PRCI base address) | |
199 | * | |
200 | * Read the register located at offset @offs from the base virtual | |
201 | * address of the PRCI register target described by @pd, and return | |
202 | * the value to the caller. | |
203 | * | |
204 | * Context: Any context. | |
205 | * | |
206 | * Return: the contents of the register described by @pd and @offs. | |
207 | */ | |
208 | static u32 __prci_readl(struct __prci_data *pd, u32 offs) | |
209 | { | |
210 | return readl_relaxed(pd->va + offs); | |
211 | } | |
212 | ||
213 | static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd) | |
214 | { | |
215 | writel_relaxed(v, pd->va + offs); | |
216 | } | |
217 | ||
218 | /* WRPLL-related private functions */ | |
219 | ||
220 | /** | |
221 | * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters | |
222 | * @c: ptr to a struct wrpll_cfg record to write config into | |
223 | * @r: value read from the PRCI PLL configuration register | |
224 | * | |
225 | * Given a value @r read from an FU540 PRCI PLL configuration register, | |
226 | * split it into fields and populate it into the WRPLL configuration record | |
227 | * pointed to by @c. | |
228 | * | |
229 | * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros | |
230 | * have the same register layout. | |
231 | * | |
232 | * Context: Any context. | |
233 | */ | |
234 | static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r) | |
235 | { | |
236 | u32 v; | |
237 | ||
238 | v = r & PRCI_COREPLLCFG0_DIVR_MASK; | |
239 | v >>= PRCI_COREPLLCFG0_DIVR_SHIFT; | |
240 | c->divr = v; | |
241 | ||
242 | v = r & PRCI_COREPLLCFG0_DIVF_MASK; | |
243 | v >>= PRCI_COREPLLCFG0_DIVF_SHIFT; | |
244 | c->divf = v; | |
245 | ||
246 | v = r & PRCI_COREPLLCFG0_DIVQ_MASK; | |
247 | v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT; | |
248 | c->divq = v; | |
249 | ||
250 | v = r & PRCI_COREPLLCFG0_RANGE_MASK; | |
251 | v >>= PRCI_COREPLLCFG0_RANGE_SHIFT; | |
252 | c->range = v; | |
253 | ||
254 | c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK | | |
255 | WRPLL_FLAGS_EXT_FEEDBACK_MASK); | |
256 | ||
257 | /* external feedback mode not supported */ | |
258 | c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK; | |
259 | } | |
260 | ||
261 | /** | |
262 | * __prci_wrpll_pack() - pack PLL configuration parameters into a register value | |
263 | * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg | |
264 | * | |
265 | * Using a set of WRPLL configuration values pointed to by @c, | |
266 | * assemble a PRCI PLL configuration register value, and return it to | |
267 | * the caller. | |
268 | * | |
269 | * Context: Any context. Caller must ensure that the contents of the | |
270 | * record pointed to by @c do not change during the execution | |
271 | * of this function. | |
272 | * | |
273 | * Returns: a value suitable for writing into a PRCI PLL configuration | |
274 | * register | |
275 | */ | |
276 | static u32 __prci_wrpll_pack(const struct wrpll_cfg *c) | |
277 | { | |
278 | u32 r = 0; | |
279 | ||
280 | r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT; | |
281 | r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT; | |
282 | r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT; | |
283 | r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT; | |
284 | ||
285 | /* external feedback mode not supported */ | |
286 | r |= PRCI_COREPLLCFG0_FSE_MASK; | |
287 | ||
288 | return r; | |
289 | } | |
290 | ||
291 | /** | |
292 | * __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI | |
293 | * @pd: PRCI context | |
294 | * @pwd: PRCI WRPLL metadata | |
295 | * | |
296 | * Read the current configuration of the PLL identified by @pwd from | |
297 | * the PRCI identified by @pd, and store it into the local configuration | |
298 | * cache in @pwd. | |
299 | * | |
300 | * Context: Any context. Caller must prevent the records pointed to by | |
301 | * @pd and @pwd from changing during execution. | |
302 | */ | |
303 | static void __prci_wrpll_read_cfg(struct __prci_data *pd, | |
304 | struct __prci_wrpll_data *pwd) | |
305 | { | |
306 | __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs)); | |
307 | } | |
308 | ||
309 | /** | |
310 | * __prci_wrpll_write_cfg() - write WRPLL configuration into the PRCI | |
311 | * @pd: PRCI context | |
312 | * @pwd: PRCI WRPLL metadata | |
313 | * @c: WRPLL configuration record to write | |
314 | * | |
315 | * Write the WRPLL configuration described by @c into the WRPLL | |
316 | * configuration register identified by @pwd in the PRCI instance | |
317 | * described by @c. Make a cached copy of the WRPLL's current | |
318 | * configuration so it can be used by other code. | |
319 | * | |
320 | * Context: Any context. Caller must prevent the records pointed to by | |
321 | * @pd and @pwd from changing during execution. | |
322 | */ | |
323 | static void __prci_wrpll_write_cfg(struct __prci_data *pd, | |
324 | struct __prci_wrpll_data *pwd, | |
325 | struct wrpll_cfg *c) | |
326 | { | |
327 | __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd); | |
328 | ||
329 | memcpy(&pwd->c, c, sizeof(*c)); | |
330 | } | |
331 | ||
332 | /* Core clock mux control */ | |
333 | ||
334 | /** | |
335 | * __prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK | |
336 | * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg | |
337 | * | |
338 | * Switch the CORECLK mux to the HFCLK input source; return once complete. | |
339 | * | |
340 | * Context: Any context. Caller must prevent concurrent changes to the | |
341 | * PRCI_CORECLKSEL_OFFSET register. | |
342 | */ | |
343 | static void __prci_coreclksel_use_hfclk(struct __prci_data *pd) | |
344 | { | |
345 | u32 r; | |
346 | ||
347 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); | |
348 | r |= PRCI_CORECLKSEL_CORECLKSEL_MASK; | |
349 | __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); | |
350 | ||
351 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ | |
352 | } | |
353 | ||
354 | /** | |
355 | * __prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL | |
356 | * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg | |
357 | * | |
358 | * Switch the CORECLK mux to the PLL output clock; return once complete. | |
359 | * | |
360 | * Context: Any context. Caller must prevent concurrent changes to the | |
361 | * PRCI_CORECLKSEL_OFFSET register. | |
362 | */ | |
363 | static void __prci_coreclksel_use_corepll(struct __prci_data *pd) | |
364 | { | |
365 | u32 r; | |
366 | ||
367 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); | |
368 | r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; | |
369 | __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); | |
370 | ||
371 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ | |
372 | } | |
373 | ||
374 | /* | |
375 | * Linux clock framework integration | |
376 | * | |
377 | * See the Linux clock framework documentation for more information on | |
378 | * these functions. | |
379 | */ | |
380 | ||
381 | static unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *hw, | |
382 | unsigned long parent_rate) | |
383 | { | |
384 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | |
385 | struct __prci_wrpll_data *pwd = pc->pwd; | |
386 | ||
387 | return wrpll_calc_output_rate(&pwd->c, parent_rate); | |
388 | } | |
389 | ||
390 | static long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *hw, | |
391 | unsigned long rate, | |
392 | unsigned long *parent_rate) | |
393 | { | |
394 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | |
395 | struct __prci_wrpll_data *pwd = pc->pwd; | |
396 | struct wrpll_cfg c; | |
397 | ||
398 | memcpy(&c, &pwd->c, sizeof(c)); | |
399 | ||
400 | wrpll_configure_for_rate(&c, rate, *parent_rate); | |
401 | ||
402 | return wrpll_calc_output_rate(&c, *parent_rate); | |
403 | } | |
404 | ||
405 | static int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *hw, | |
406 | unsigned long rate, | |
407 | unsigned long parent_rate) | |
408 | { | |
409 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | |
410 | struct __prci_wrpll_data *pwd = pc->pwd; | |
411 | struct __prci_data *pd = pc->pd; | |
412 | int r; | |
413 | ||
414 | r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate); | |
415 | if (r) | |
416 | return r; | |
417 | ||
418 | if (pwd->enable_bypass) | |
419 | pwd->enable_bypass(pd); | |
420 | ||
421 | __prci_wrpll_write_cfg(pd, pwd, &pwd->c); | |
422 | ||
423 | udelay(wrpll_calc_max_lock_us(&pwd->c)); | |
424 | ||
425 | if (pwd->disable_bypass) | |
426 | pwd->disable_bypass(pd); | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
431 | static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = { | |
432 | .set_rate = sifive_fu540_prci_wrpll_set_rate, | |
433 | .round_rate = sifive_fu540_prci_wrpll_round_rate, | |
434 | .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, | |
435 | }; | |
436 | ||
437 | static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = { | |
438 | .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, | |
439 | }; | |
440 | ||
441 | /* TLCLKSEL clock integration */ | |
442 | ||
443 | static unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *hw, | |
444 | unsigned long parent_rate) | |
445 | { | |
446 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | |
447 | struct __prci_data *pd = pc->pd; | |
448 | u32 v; | |
449 | u8 div; | |
450 | ||
451 | v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET); | |
452 | v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK; | |
453 | div = v ? 1 : 2; | |
454 | ||
455 | return div_u64(parent_rate, div); | |
456 | } | |
457 | ||
458 | static const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = { | |
459 | .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate, | |
460 | }; | |
461 | ||
462 | /* | |
463 | * PRCI integration data for each WRPLL instance | |
464 | */ | |
465 | ||
466 | static struct __prci_wrpll_data __prci_corepll_data = { | |
467 | .cfg0_offs = PRCI_COREPLLCFG0_OFFSET, | |
468 | .enable_bypass = __prci_coreclksel_use_hfclk, | |
469 | .disable_bypass = __prci_coreclksel_use_corepll, | |
470 | }; | |
471 | ||
472 | static struct __prci_wrpll_data __prci_ddrpll_data = { | |
473 | .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET, | |
474 | }; | |
475 | ||
476 | static struct __prci_wrpll_data __prci_gemgxlpll_data = { | |
477 | .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET, | |
478 | }; | |
479 | ||
480 | /* | |
481 | * List of clock controls provided by the PRCI | |
482 | */ | |
483 | ||
484 | static struct __prci_clock __prci_init_clocks[] = { | |
485 | [PRCI_CLK_COREPLL] = { | |
486 | .name = "corepll", | |
487 | .parent_name = "hfclk", | |
488 | .ops = &sifive_fu540_prci_wrpll_clk_ops, | |
489 | .pwd = &__prci_corepll_data, | |
490 | }, | |
491 | [PRCI_CLK_DDRPLL] = { | |
492 | .name = "ddrpll", | |
493 | .parent_name = "hfclk", | |
494 | .ops = &sifive_fu540_prci_wrpll_ro_clk_ops, | |
495 | .pwd = &__prci_ddrpll_data, | |
496 | }, | |
497 | [PRCI_CLK_GEMGXLPLL] = { | |
498 | .name = "gemgxlpll", | |
499 | .parent_name = "hfclk", | |
500 | .ops = &sifive_fu540_prci_wrpll_clk_ops, | |
501 | .pwd = &__prci_gemgxlpll_data, | |
502 | }, | |
503 | [PRCI_CLK_TLCLK] = { | |
504 | .name = "tlclk", | |
505 | .parent_name = "corepll", | |
506 | .ops = &sifive_fu540_prci_tlclksel_clk_ops, | |
507 | }, | |
508 | }; | |
509 | ||
510 | /** | |
511 | * __prci_register_clocks() - register clock controls in the PRCI with Linux | |
512 | * @dev: Linux struct device * | |
513 | * | |
514 | * Register the list of clock controls described in __prci_init_plls[] with | |
515 | * the Linux clock framework. | |
516 | * | |
517 | * Return: 0 upon success or a negative error code upon failure. | |
518 | */ | |
519 | static int __prci_register_clocks(struct device *dev, struct __prci_data *pd) | |
520 | { | |
521 | struct clk_init_data init = { }; | |
522 | struct __prci_clock *pic; | |
523 | int parent_count, i, r; | |
524 | ||
525 | parent_count = of_clk_get_parent_count(dev->of_node); | |
526 | if (parent_count != EXPECTED_CLK_PARENT_COUNT) { | |
527 | dev_err(dev, "expected only two parent clocks, found %d\n", | |
528 | parent_count); | |
529 | return -EINVAL; | |
530 | } | |
531 | ||
532 | /* Register PLLs */ | |
533 | for (i = 0; i < ARRAY_SIZE(__prci_init_clocks); ++i) { | |
534 | pic = &__prci_init_clocks[i]; | |
535 | ||
536 | init.name = pic->name; | |
537 | init.parent_names = &pic->parent_name; | |
538 | init.num_parents = 1; | |
539 | init.ops = pic->ops; | |
540 | pic->hw.init = &init; | |
541 | ||
542 | pic->pd = pd; | |
543 | ||
544 | if (pic->pwd) | |
545 | __prci_wrpll_read_cfg(pd, pic->pwd); | |
546 | ||
547 | r = devm_clk_hw_register(dev, &pic->hw); | |
548 | if (r) { | |
549 | dev_warn(dev, "Failed to register clock %s: %d\n", | |
550 | init.name, r); | |
551 | return r; | |
552 | } | |
553 | ||
554 | r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev)); | |
555 | if (r) { | |
556 | dev_warn(dev, "Failed to register clkdev for %s: %d\n", | |
557 | init.name, r); | |
558 | return r; | |
559 | } | |
560 | ||
561 | pd->hw_clks.hws[i] = &pic->hw; | |
562 | } | |
563 | ||
564 | pd->hw_clks.num = i; | |
565 | ||
566 | r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, | |
567 | &pd->hw_clks); | |
568 | if (r) { | |
569 | dev_err(dev, "could not add hw_provider: %d\n", r); | |
570 | return r; | |
571 | } | |
572 | ||
573 | return 0; | |
574 | } | |
575 | ||
576 | /* | |
577 | * Linux device model integration | |
578 | * | |
579 | * See the Linux device model documentation for more information about | |
580 | * these functions. | |
581 | */ | |
582 | static int sifive_fu540_prci_probe(struct platform_device *pdev) | |
583 | { | |
584 | struct device *dev = &pdev->dev; | |
585 | struct resource *res; | |
586 | struct __prci_data *pd; | |
587 | int r; | |
588 | ||
589 | pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); | |
590 | if (!pd) | |
591 | return -ENOMEM; | |
592 | ||
593 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
594 | pd->va = devm_ioremap_resource(dev, res); | |
595 | if (IS_ERR(pd->va)) | |
596 | return PTR_ERR(pd->va); | |
597 | ||
598 | r = __prci_register_clocks(dev, pd); | |
599 | if (r) { | |
600 | dev_err(dev, "could not register clocks: %d\n", r); | |
601 | return r; | |
602 | } | |
603 | ||
604 | dev_dbg(dev, "SiFive FU540 PRCI probed\n"); | |
605 | ||
606 | return 0; | |
607 | } | |
608 | ||
609 | static const struct of_device_id sifive_fu540_prci_of_match[] = { | |
610 | { .compatible = "sifive,fu540-c000-prci", }, | |
611 | {} | |
612 | }; | |
613 | MODULE_DEVICE_TABLE(of, sifive_fu540_prci_of_match); | |
614 | ||
615 | static struct platform_driver sifive_fu540_prci_driver = { | |
616 | .driver = { | |
617 | .name = "sifive-fu540-prci", | |
618 | .of_match_table = sifive_fu540_prci_of_match, | |
619 | }, | |
620 | .probe = sifive_fu540_prci_probe, | |
621 | }; | |
622 | ||
623 | static int __init sifive_fu540_prci_init(void) | |
624 | { | |
625 | return platform_driver_register(&sifive_fu540_prci_driver); | |
626 | } | |
627 | core_initcall(sifive_fu540_prci_init); |