blob: de55504b5c90785f8d5f1f66eb914847feb4a495 [file] [log] [blame]
Green Wanecefa5f2021-05-27 06:52:08 -07001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018-2021 SiFive, Inc.
4 * Wesley Terpstra
5 * Paul Walmsley
6 * Zong Li
7 * Pragnesh Patel
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 PRCI implements clock and reset control for the SiFive chip.
19 * This driver assumes that it has sole control over all PRCI resources.
20 *
21 * This driver is based on the PRCI driver written by Wesley Terpstra:
22 * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60
23 */
24
Green Wanecefa5f2021-05-27 06:52:08 -070025#include <clk-uclass.h>
26#include <clk.h>
27#include <dm.h>
28#include <dm/device_compat.h>
29#include <reset.h>
30#include <asm/io.h>
31#include <asm/arch/reset.h>
32#include <linux/delay.h>
33#include <linux/math64.h>
34#include <dt-bindings/clock/sifive-fu740-prci.h>
35
Heinrich Schuchardt77d8dc62024-02-17 00:18:04 +010036#include "sifive-prci.h"
Green Wanecefa5f2021-05-27 06:52:08 -070037
38/*
39 * Private functions
40 */
41
42/**
43 * __prci_readl() - read from a PRCI register
44 * @pd: PRCI context
45 * @offs: register offset to read from (in bytes, from PRCI base address)
46 *
47 * Read the register located at offset @offs from the base virtual
48 * address of the PRCI register target described by @pd, and return
49 * the value to the caller.
50 *
51 * Context: Any context.
52 *
53 * Return: the contents of the register described by @pd and @offs.
54 */
55static u32 __prci_readl(struct __prci_data *pd, u32 offs)
56{
57 return readl(pd->va + offs);
58}
59
60static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
61{
62 writel(v, pd->va + offs);
63}
64
65/* WRPLL-related private functions */
66
67/**
68 * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters
69 * @c: ptr to a struct wrpll_cfg record to write config into
70 * @r: value read from the PRCI PLL configuration register
71 *
72 * Given a value @r read from an FU540 PRCI PLL configuration register,
73 * split it into fields and populate it into the WRPLL configuration record
74 * pointed to by @c.
75 *
76 * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros
77 * have the same register layout.
78 *
79 * Context: Any context.
80 */
81static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r)
82{
83 u32 v;
84
85 v = r & PRCI_COREPLLCFG0_DIVR_MASK;
86 v >>= PRCI_COREPLLCFG0_DIVR_SHIFT;
87 c->divr = v;
88
89 v = r & PRCI_COREPLLCFG0_DIVF_MASK;
90 v >>= PRCI_COREPLLCFG0_DIVF_SHIFT;
91 c->divf = v;
92
93 v = r & PRCI_COREPLLCFG0_DIVQ_MASK;
94 v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT;
95 c->divq = v;
96
97 v = r & PRCI_COREPLLCFG0_RANGE_MASK;
98 v >>= PRCI_COREPLLCFG0_RANGE_SHIFT;
99 c->range = v;
100
101 c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK |
102 WRPLL_FLAGS_EXT_FEEDBACK_MASK);
103
104 /* external feedback mode not supported */
105 c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK;
106}
107
108/**
109 * __prci_wrpll_pack() - pack PLL configuration parameters into a register value
110 * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg
111 *
112 * Using a set of WRPLL configuration values pointed to by @c,
113 * assemble a PRCI PLL configuration register value, and return it to
114 * the caller.
115 *
116 * Context: Any context. Caller must ensure that the contents of the
117 * record pointed to by @c do not change during the execution
118 * of this function.
119 *
120 * Returns: a value suitable for writing into a PRCI PLL configuration
121 * register
122 */
123static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
124{
125 u32 r = 0;
126
127 r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT;
128 r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT;
129 r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT;
130 r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT;
131
132 /* external feedback mode not supported */
133 r |= PRCI_COREPLLCFG0_FSE_MASK;
134
135 return r;
136}
137
138/**
139 * __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI
140 * @pd: PRCI context
141 * @pwd: PRCI WRPLL metadata
142 *
143 * Read the current configuration of the PLL identified by @pwd from
144 * the PRCI identified by @pd, and store it into the local configuration
145 * cache in @pwd.
146 *
147 * Context: Any context. Caller must prevent the records pointed to by
148 * @pd and @pwd from changing during execution.
149 */
150static void __prci_wrpll_read_cfg0(struct __prci_data *pd,
151 struct __prci_wrpll_data *pwd)
152{
153 __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
154}
155
156/**
157 * __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI
158 * @pd: PRCI context
159 * @pwd: PRCI WRPLL metadata
160 * @c: WRPLL configuration record to write
161 *
162 * Write the WRPLL configuration described by @c into the WRPLL
163 * configuration register identified by @pwd in the PRCI instance
164 * described by @c. Make a cached copy of the WRPLL's current
165 * configuration so it can be used by other code.
166 *
167 * Context: Any context. Caller must prevent the records pointed to by
168 * @pd and @pwd from changing during execution.
169 */
170static void __prci_wrpll_write_cfg0(struct __prci_data *pd,
171 struct __prci_wrpll_data *pwd,
172 struct wrpll_cfg *c)
173{
174 __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd);
175
176 memcpy(&pwd->c, c, sizeof(*c));
177}
178
179/**
180 * __prci_wrpll_write_cfg1() - write Clock enable/disable configuration
181 * into the PRCI
182 * @pd: PRCI context
183 * @pwd: PRCI WRPLL metadata
184 * @enable: Clock enable or disable value
185 */
186static void __prci_wrpll_write_cfg1(struct __prci_data *pd,
187 struct __prci_wrpll_data *pwd,
188 u32 enable)
189{
190 __prci_writel(enable, pwd->cfg1_offs, pd);
191}
192
193unsigned long sifive_prci_wrpll_recalc_rate(struct __prci_clock *pc,
194 unsigned long parent_rate)
195{
196 struct __prci_wrpll_data *pwd = pc->pwd;
197
198 return wrpll_calc_output_rate(&pwd->c, parent_rate);
199}
200
201unsigned long sifive_prci_wrpll_round_rate(struct __prci_clock *pc,
202 unsigned long rate,
203 unsigned long *parent_rate)
204{
205 struct __prci_wrpll_data *pwd = pc->pwd;
206 struct wrpll_cfg c;
207
208 memcpy(&c, &pwd->c, sizeof(c));
209
210 wrpll_configure_for_rate(&c, rate, *parent_rate);
211
212 return wrpll_calc_output_rate(&c, *parent_rate);
213}
214
215int sifive_prci_wrpll_set_rate(struct __prci_clock *pc,
216 unsigned long rate,
217 unsigned long parent_rate)
218{
219 struct __prci_wrpll_data *pwd = pc->pwd;
220 struct __prci_data *pd = pc->pd;
221 int r;
222
223 r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate);
224 if (r)
225 return r;
226
227 if (pwd->enable_bypass)
228 pwd->enable_bypass(pd);
229
230 __prci_wrpll_write_cfg0(pd, pwd, &pwd->c);
231
232 udelay(wrpll_calc_max_lock_us(&pwd->c));
233
234 return 0;
235}
236
237int sifive_prci_clock_enable(struct __prci_clock *pc, bool enable)
238{
239 struct __prci_wrpll_data *pwd = pc->pwd;
240 struct __prci_data *pd = pc->pd;
241
242 if (enable) {
243 __prci_wrpll_write_cfg1(pd, pwd, PRCI_COREPLLCFG1_CKE_MASK);
244
245 if (pwd->disable_bypass)
246 pwd->disable_bypass(pd);
247
248 if (pwd->release_reset)
249 pwd->release_reset(pd);
250 } else {
251 u32 r;
252
253 if (pwd->enable_bypass)
254 pwd->enable_bypass(pd);
255
256 r = __prci_readl(pd, pwd->cfg1_offs);
257 r &= ~PRCI_COREPLLCFG1_CKE_MASK;
258
259 __prci_wrpll_write_cfg1(pd, pwd, r);
260 }
261
262 return 0;
263}
264
265/* TLCLKSEL clock integration */
266
267unsigned long sifive_prci_tlclksel_recalc_rate(struct __prci_clock *pc,
268 unsigned long parent_rate)
269{
270 struct __prci_data *pd = pc->pd;
271 u32 v;
272 u8 div;
273
274 v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET);
275 v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK;
276 div = v ? 1 : 2;
277
278 return div_u64(parent_rate, div);
279}
280
281/* HFPCLK clock integration */
282
283unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct __prci_clock *pc,
284 unsigned long parent_rate)
285{
286 struct __prci_data *pd = pc->pd;
287 u32 div = __prci_readl(pd, PRCI_HFPCLKPLLDIV_OFFSET);
288
289 return div_u64(parent_rate, div + 2);
290}
291
292/**
293 * sifive_prci_coreclksel_use_final_corepll() - switch the CORECLK mux to output
294 * FINAL_COREPLL
295 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
296 *
297 * Switch the CORECLK mux to the final COREPLL output clock; return once
298 * complete.
299 *
300 * Context: Any context. Caller must prevent concurrent changes to the
301 * PRCI_CORECLKSEL_OFFSET register.
302 */
303void sifive_prci_coreclksel_use_final_corepll(struct __prci_data *pd)
304{
305 u32 r;
306
307 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
308 r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
309 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
310
311 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
312}
313
314/**
315 * sifive_prci_corepllsel_use_dvfscorepll() - switch the COREPLL mux to
316 * output DVFS_COREPLL
317 * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
318 *
319 * Switch the COREPLL mux to the DVFSCOREPLL output clock; return once complete.
320 *
321 * Context: Any context. Caller must prevent concurrent changes to the
322 * PRCI_COREPLLSEL_OFFSET register.
323 */
324void sifive_prci_corepllsel_use_dvfscorepll(struct __prci_data *pd)
325{
326 u32 r;
327
328 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);
329 r |= PRCI_COREPLLSEL_COREPLLSEL_MASK;
330 __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd);
331
332 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */
333}
334
335/**
336 * sifive_prci_corepllsel_use_corepll() - switch the COREPLL mux to
337 * output COREPLL
338 * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
339 *
340 * Switch the COREPLL mux to the COREPLL output clock; return once complete.
341 *
342 * Context: Any context. Caller must prevent concurrent changes to the
343 * PRCI_COREPLLSEL_OFFSET register.
344 */
345void sifive_prci_corepllsel_use_corepll(struct __prci_data *pd)
346{
347 u32 r;
348
349 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);
350 r &= ~PRCI_COREPLLSEL_COREPLLSEL_MASK;
351 __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd);
352
353 r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */
354}
355
356/**
357 * sifive_prci_hfpclkpllsel_use_hfclk() - switch the HFPCLKPLL mux to
358 * output HFCLK
359 * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
360 *
361 * Switch the HFPCLKPLL mux to the HFCLK input source; return once complete.
362 *
363 * Context: Any context. Caller must prevent concurrent changes to the
364 * PRCI_HFPCLKPLLSEL_OFFSET register.
365 */
366void sifive_prci_hfpclkpllsel_use_hfclk(struct __prci_data *pd)
367{
368 u32 r;
369
370 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET);
371 r |= PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK;
372 __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd);
373
374 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */
375}
376
377/**
378 * sifive_prci_hfpclkpllsel_use_hfpclkpll() - switch the HFPCLKPLL mux to
379 * output HFPCLKPLL
380 * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
381 *
382 * Switch the HFPCLKPLL mux to the HFPCLKPLL output clock; return once complete.
383 *
384 * Context: Any context. Caller must prevent concurrent changes to the
385 * PRCI_HFPCLKPLLSEL_OFFSET register.
386 */
387void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd)
388{
389 u32 r;
390
391 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET);
392 r &= ~PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK;
393 __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd);
394
395 r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */
396}
397
398static int __prci_consumer_reset(const char *rst_name, bool trigger)
399{
400 struct udevice *dev;
401 struct reset_ctl rst_sig;
402 int ret;
403
404 ret = uclass_get_device_by_driver(UCLASS_RESET,
405 DM_DRIVER_GET(sifive_reset),
406 &dev);
407 if (ret) {
408 dev_err(dev, "Reset driver not found: %d\n", ret);
409 return ret;
410 }
411
412 ret = reset_get_by_name(dev, rst_name, &rst_sig);
413 if (ret) {
414 dev_err(dev, "failed to get %s reset\n", rst_name);
415 return ret;
416 }
417
418 if (reset_valid(&rst_sig)) {
419 if (trigger)
420 ret = reset_deassert(&rst_sig);
421 else
422 ret = reset_assert(&rst_sig);
423 if (ret) {
424 dev_err(dev, "failed to trigger reset id = %ld\n",
425 rst_sig.id);
426 return ret;
427 }
428 }
429
430 return ret;
431}
432
433/**
434 * sifive_prci_ddr_release_reset() - Release DDR reset
435 * @pd: struct __prci_data * for the PRCI containing the DDRCLK mux reg
436 *
437 */
438void sifive_prci_ddr_release_reset(struct __prci_data *pd)
439{
440 /* Release DDR ctrl reset */
441 __prci_consumer_reset("ddr_ctrl", true);
442
443 /* HACK to get the '1 full controller clock cycle'. */
444 asm volatile ("fence");
445
446 /* Release DDR AXI reset */
447 __prci_consumer_reset("ddr_axi", true);
448
449 /* Release DDR AHB reset */
450 __prci_consumer_reset("ddr_ahb", true);
451
452 /* Release DDR PHY reset */
453 __prci_consumer_reset("ddr_phy", true);
454
455 /* HACK to get the '1 full controller clock cycle'. */
456 asm volatile ("fence");
457
458 /*
459 * These take like 16 cycles to actually propagate. We can't go sending
460 * stuff before they come out of reset. So wait.
461 */
462 for (int i = 0; i < 256; i++)
463 asm volatile ("nop");
464}
465
466/**
467 * sifive_prci_ethernet_release_reset() - Release ethernet reset
468 * @pd: struct __prci_data * for the PRCI containing the Ethernet CLK mux reg
469 *
470 */
471void sifive_prci_ethernet_release_reset(struct __prci_data *pd)
472{
473 /* Release GEMGXL reset */
474 __prci_consumer_reset("gemgxl_reset", true);
475
476 /* Procmon => core clock */
477 __prci_writel(PRCI_PROCMONCFG_CORE_CLOCK_MASK, PRCI_PROCMONCFG_OFFSET,
478 pd);
479
480 /* Release Chiplink reset */
481 __prci_consumer_reset("cltx_reset", true);
482}
483
484/**
485 * sifive_prci_cltx_release_reset() - Release cltx reset
486 * @pd: struct __prci_data * for the PRCI containing the Ethernet CLK mux reg
487 *
488 */
489void sifive_prci_cltx_release_reset(struct __prci_data *pd)
490{
491 /* Release CLTX reset */
492 __prci_consumer_reset("cltx_reset", true);
493}
494
495/* Core clock mux control */
496
497/**
498 * sifive_prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
499 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
500 *
501 * Switch the CORECLK mux to the HFCLK input source; return once complete.
502 *
503 * Context: Any context. Caller must prevent concurrent changes to the
504 * PRCI_CORECLKSEL_OFFSET register.
505 */
506void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd)
507{
508 u32 r;
509
510 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
511 r |= PRCI_CORECLKSEL_CORECLKSEL_MASK;
512 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
513
514 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
515}
516
517/**
518 * sifive_prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL
519 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
520 *
521 * Switch the CORECLK mux to the PLL output clock; return once complete.
522 *
523 * Context: Any context. Caller must prevent concurrent changes to the
524 * PRCI_CORECLKSEL_OFFSET register.
525 */
526void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd)
527{
528 u32 r;
529
530 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
531 r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
532 __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
533
534 r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
535}
536
537static ulong sifive_prci_parent_rate(struct __prci_clock *pc, struct prci_clk_desc *data)
538{
539 ulong parent_rate;
540 ulong i;
541 struct __prci_clock *p;
542
543 if (strcmp(pc->parent_name, "corepll") == 0 ||
544 strcmp(pc->parent_name, "hfpclkpll") == 0) {
545 for (i = 0; i < data->num_clks; i++) {
546 if (strcmp(pc->parent_name, data->clks[i].name) == 0)
547 break;
548 }
549
550 if (i >= data->num_clks)
551 return -ENXIO;
552
553 p = &data->clks[i];
554 if (!p->pd || !p->ops->recalc_rate)
555 return -ENXIO;
556
557 return p->ops->recalc_rate(p, sifive_prci_parent_rate(p, data));
558 }
559
560 if (strcmp(pc->parent_name, "rtcclk") == 0)
561 parent_rate = clk_get_rate(&pc->pd->parent_rtcclk);
562 else
563 parent_rate = clk_get_rate(&pc->pd->parent_hfclk);
564
565 return parent_rate;
566}
567
568static ulong sifive_prci_get_rate(struct clk *clk)
569{
570 struct __prci_clock *pc;
571 struct prci_clk_desc *data =
572 (struct prci_clk_desc *)dev_get_driver_data(clk->dev);
573
574 if (data->num_clks <= clk->id)
575 return -ENXIO;
576
577 pc = &data->clks[clk->id];
578 if (!pc->pd || !pc->ops->recalc_rate)
579 return -ENXIO;
580
581 return pc->ops->recalc_rate(pc, sifive_prci_parent_rate(pc, data));
582}
583
584static ulong sifive_prci_set_rate(struct clk *clk, ulong rate)
585{
586 int err;
587 struct __prci_clock *pc;
588 struct prci_clk_desc *data =
589 (struct prci_clk_desc *)dev_get_driver_data(clk->dev);
590
591 if (data->num_clks <= clk->id)
592 return -ENXIO;
593
594 pc = &data->clks[clk->id];
595 if (!pc->pd || !pc->ops->set_rate)
596 return -ENXIO;
597
598 err = pc->ops->set_rate(pc, rate, sifive_prci_parent_rate(pc, data));
599 if (err)
600 return err;
601
602 return rate;
603}
604
605static int sifive_prci_enable(struct clk *clk)
606{
607 struct __prci_clock *pc;
608 int ret = 0;
609 struct prci_clk_desc *data =
610 (struct prci_clk_desc *)dev_get_driver_data(clk->dev);
611
612 if (data->num_clks <= clk->id)
613 return -ENXIO;
614
615 pc = &data->clks[clk->id];
616 if (!pc->pd)
617 return -ENXIO;
618
619 if (pc->ops->enable_clk)
620 ret = pc->ops->enable_clk(pc, 1);
621
622 return ret;
623}
624
625static int sifive_prci_disable(struct clk *clk)
626{
627 struct __prci_clock *pc;
628 int ret = 0;
629 struct prci_clk_desc *data =
630 (struct prci_clk_desc *)dev_get_driver_data(clk->dev);
631
632 if (data->num_clks <= clk->id)
633 return -ENXIO;
634
635 pc = &data->clks[clk->id];
636 if (!pc->pd)
637 return -ENXIO;
638
639 if (pc->ops->enable_clk)
640 ret = pc->ops->enable_clk(pc, 0);
641
642 return ret;
643}
644
645static int sifive_prci_probe(struct udevice *dev)
646{
647 int i, err;
648 struct __prci_clock *pc;
649 struct __prci_data *pd = dev_get_priv(dev);
650
651 struct prci_clk_desc *data =
652 (struct prci_clk_desc *)dev_get_driver_data(dev);
653
Bin Mengd0dcb752021-09-12 11:15:09 +0800654 pd->va = dev_read_addr_ptr(dev);
655 if (!pd->va)
656 return -EINVAL;
Green Wanecefa5f2021-05-27 06:52:08 -0700657
658 err = clk_get_by_index(dev, 0, &pd->parent_hfclk);
659 if (err)
660 return err;
661
662 err = clk_get_by_index(dev, 1, &pd->parent_rtcclk);
663 if (err)
664 return err;
665
666 for (i = 0; i < data->num_clks; ++i) {
667 pc = &data->clks[i];
668 pc->pd = pd;
669 if (pc->pwd)
670 __prci_wrpll_read_cfg0(pd, pc->pwd);
671 }
672
Simon Glass7ec24132024-09-29 19:49:48 -0600673 if (IS_ENABLED(CONFIG_XPL_BUILD)) {
Green Wanecefa5f2021-05-27 06:52:08 -0700674 if (device_is_compatible(dev, "sifive,fu740-c000-prci")) {
675 u32 prci_pll_reg;
676 unsigned long parent_rate;
677
678 prci_pll_reg = readl(pd->va + PRCI_PRCIPLL_OFFSET);
679
680 if (prci_pll_reg & PRCI_PRCIPLL_HFPCLKPLL) {
681 /*
682 * Only initialize the HFPCLK PLL. In this
683 * case the design uses hfpclk to drive
684 * Chiplink
685 */
Icenowy Zheng13d71702022-08-25 16:11:18 +0800686 pc = &data->clks[FU740_PRCI_CLK_HFPCLKPLL];
Green Wanecefa5f2021-05-27 06:52:08 -0700687 parent_rate = sifive_prci_parent_rate(pc, data);
688 sifive_prci_wrpll_set_rate(pc, 260000000,
689 parent_rate);
690 pc->ops->enable_clk(pc, 1);
691 } else if (prci_pll_reg & PRCI_PRCIPLL_CLTXPLL) {
692 /* CLTX pll init */
Icenowy Zheng13d71702022-08-25 16:11:18 +0800693 pc = &data->clks[FU740_PRCI_CLK_CLTXPLL];
Green Wanecefa5f2021-05-27 06:52:08 -0700694 parent_rate = sifive_prci_parent_rate(pc, data);
695 sifive_prci_wrpll_set_rate(pc, 260000000,
696 parent_rate);
697 pc->ops->enable_clk(pc, 1);
698 }
699 }
700 }
701
702 return 0;
703}
704
705static struct clk_ops sifive_prci_ops = {
706 .set_rate = sifive_prci_set_rate,
707 .get_rate = sifive_prci_get_rate,
708 .enable = sifive_prci_enable,
709 .disable = sifive_prci_disable,
710};
711
712static int sifive_clk_bind(struct udevice *dev)
713{
714 return sifive_reset_bind(dev, PRCI_DEVICERESETCNT);
715}
716
717static const struct udevice_id sifive_prci_ids[] = {
718 { .compatible = "sifive,fu540-c000-prci", .data = (ulong)&prci_clk_fu540 },
719 { .compatible = "sifive,fu740-c000-prci", .data = (ulong)&prci_clk_fu740 },
720 { }
721};
722
723U_BOOT_DRIVER(sifive_prci) = {
724 .name = "sifive-prci",
725 .id = UCLASS_CLK,
726 .of_match = sifive_prci_ids,
727 .probe = sifive_prci_probe,
728 .ops = &sifive_prci_ops,
729 .priv_auto = sizeof(struct __prci_data),
730 .bind = sifive_clk_bind,
731};