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