1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2020 SiFive, Inc.
4 * Copyright (C) 2020 Zong Li
7 #include <linux/delay.h>
9 #include <linux/module.h>
11 #include "sifive-prci.h"
12 #include "fu540-prci.h"
13 #include "fu740-prci.h"
20 * __prci_readl() - read from a PRCI register
22 * @offs: register offset to read from (in bytes, from PRCI base address)
24 * Read the register located at offset @offs from the base virtual
25 * address of the PRCI register target described by @pd, and return
26 * the value to the caller.
28 * Context: Any context.
30 * Return: the contents of the register described by @pd and @offs.
32 static u32
__prci_readl(struct __prci_data
*pd
, u32 offs
)
34 return readl_relaxed(pd
->va
+ offs
);
37 static void __prci_writel(u32 v
, u32 offs
, struct __prci_data
*pd
)
39 writel_relaxed(v
, pd
->va
+ offs
);
42 /* WRPLL-related private functions */
45 * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters
46 * @c: ptr to a struct wrpll_cfg record to write config into
47 * @r: value read from the PRCI PLL configuration register
49 * Given a value @r read from an FU740 PRCI PLL configuration register,
50 * split it into fields and populate it into the WRPLL configuration record
53 * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros
54 * have the same register layout.
56 * Context: Any context.
58 static void __prci_wrpll_unpack(struct wrpll_cfg
*c
, u32 r
)
62 v
= r
& PRCI_COREPLLCFG0_DIVR_MASK
;
63 v
>>= PRCI_COREPLLCFG0_DIVR_SHIFT
;
66 v
= r
& PRCI_COREPLLCFG0_DIVF_MASK
;
67 v
>>= PRCI_COREPLLCFG0_DIVF_SHIFT
;
70 v
= r
& PRCI_COREPLLCFG0_DIVQ_MASK
;
71 v
>>= PRCI_COREPLLCFG0_DIVQ_SHIFT
;
74 v
= r
& PRCI_COREPLLCFG0_RANGE_MASK
;
75 v
>>= PRCI_COREPLLCFG0_RANGE_SHIFT
;
79 (WRPLL_FLAGS_INT_FEEDBACK_MASK
| WRPLL_FLAGS_EXT_FEEDBACK_MASK
);
81 /* external feedback mode not supported */
82 c
->flags
|= WRPLL_FLAGS_INT_FEEDBACK_MASK
;
86 * __prci_wrpll_pack() - pack PLL configuration parameters into a register value
87 * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg
89 * Using a set of WRPLL configuration values pointed to by @c,
90 * assemble a PRCI PLL configuration register value, and return it to
93 * Context: Any context. Caller must ensure that the contents of the
94 * record pointed to by @c do not change during the execution
97 * Returns: a value suitable for writing into a PRCI PLL configuration
100 static u32
__prci_wrpll_pack(const struct wrpll_cfg
*c
)
104 r
|= c
->divr
<< PRCI_COREPLLCFG0_DIVR_SHIFT
;
105 r
|= c
->divf
<< PRCI_COREPLLCFG0_DIVF_SHIFT
;
106 r
|= c
->divq
<< PRCI_COREPLLCFG0_DIVQ_SHIFT
;
107 r
|= c
->range
<< PRCI_COREPLLCFG0_RANGE_SHIFT
;
109 /* external feedback mode not supported */
110 r
|= PRCI_COREPLLCFG0_FSE_MASK
;
116 * __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI
118 * @pwd: PRCI WRPLL metadata
120 * Read the current configuration of the PLL identified by @pwd from
121 * the PRCI identified by @pd, and store it into the local configuration
124 * Context: Any context. Caller must prevent the records pointed to by
125 * @pd and @pwd from changing during execution.
127 static void __prci_wrpll_read_cfg0(struct __prci_data
*pd
,
128 struct __prci_wrpll_data
*pwd
)
130 __prci_wrpll_unpack(&pwd
->c
, __prci_readl(pd
, pwd
->cfg0_offs
));
134 * __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI
136 * @pwd: PRCI WRPLL metadata
137 * @c: WRPLL configuration record to write
139 * Write the WRPLL configuration described by @c into the WRPLL
140 * configuration register identified by @pwd in the PRCI instance
141 * described by @c. Make a cached copy of the WRPLL's current
142 * configuration so it can be used by other code.
144 * Context: Any context. Caller must prevent the records pointed to by
145 * @pd and @pwd from changing during execution.
147 static void __prci_wrpll_write_cfg0(struct __prci_data
*pd
,
148 struct __prci_wrpll_data
*pwd
,
151 __prci_writel(__prci_wrpll_pack(c
), pwd
->cfg0_offs
, pd
);
153 memcpy(&pwd
->c
, c
, sizeof(*c
));
157 * __prci_wrpll_write_cfg1() - write Clock enable/disable configuration
160 * @pwd: PRCI WRPLL metadata
161 * @enable: Clock enable or disable value
163 static void __prci_wrpll_write_cfg1(struct __prci_data
*pd
,
164 struct __prci_wrpll_data
*pwd
,
167 __prci_writel(enable
, pwd
->cfg1_offs
, pd
);
171 * Linux clock framework integration
173 * See the Linux clock framework documentation for more information on
177 unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw
*hw
,
178 unsigned long parent_rate
)
180 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
181 struct __prci_wrpll_data
*pwd
= pc
->pwd
;
183 return wrpll_calc_output_rate(&pwd
->c
, parent_rate
);
186 long sifive_prci_wrpll_round_rate(struct clk_hw
*hw
,
188 unsigned long *parent_rate
)
190 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
191 struct __prci_wrpll_data
*pwd
= pc
->pwd
;
194 memcpy(&c
, &pwd
->c
, sizeof(c
));
196 wrpll_configure_for_rate(&c
, rate
, *parent_rate
);
198 return wrpll_calc_output_rate(&c
, *parent_rate
);
201 int sifive_prci_wrpll_set_rate(struct clk_hw
*hw
,
202 unsigned long rate
, unsigned long parent_rate
)
204 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
205 struct __prci_wrpll_data
*pwd
= pc
->pwd
;
206 struct __prci_data
*pd
= pc
->pd
;
209 r
= wrpll_configure_for_rate(&pwd
->c
, rate
, parent_rate
);
213 if (pwd
->enable_bypass
)
214 pwd
->enable_bypass(pd
);
216 __prci_wrpll_write_cfg0(pd
, pwd
, &pwd
->c
);
218 udelay(wrpll_calc_max_lock_us(&pwd
->c
));
223 int sifive_clk_is_enabled(struct clk_hw
*hw
)
225 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
226 struct __prci_wrpll_data
*pwd
= pc
->pwd
;
227 struct __prci_data
*pd
= pc
->pd
;
230 r
= __prci_readl(pd
, pwd
->cfg1_offs
);
232 if (r
& PRCI_COREPLLCFG1_CKE_MASK
)
238 int sifive_prci_clock_enable(struct clk_hw
*hw
)
240 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
241 struct __prci_wrpll_data
*pwd
= pc
->pwd
;
242 struct __prci_data
*pd
= pc
->pd
;
244 if (sifive_clk_is_enabled(hw
))
247 __prci_wrpll_write_cfg1(pd
, pwd
, PRCI_COREPLLCFG1_CKE_MASK
);
249 if (pwd
->disable_bypass
)
250 pwd
->disable_bypass(pd
);
255 void sifive_prci_clock_disable(struct clk_hw
*hw
)
257 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
258 struct __prci_wrpll_data
*pwd
= pc
->pwd
;
259 struct __prci_data
*pd
= pc
->pd
;
262 if (pwd
->enable_bypass
)
263 pwd
->enable_bypass(pd
);
265 r
= __prci_readl(pd
, pwd
->cfg1_offs
);
266 r
&= ~PRCI_COREPLLCFG1_CKE_MASK
;
268 __prci_wrpll_write_cfg1(pd
, pwd
, r
);
271 /* TLCLKSEL clock integration */
273 unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw
*hw
,
274 unsigned long parent_rate
)
276 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
277 struct __prci_data
*pd
= pc
->pd
;
281 v
= __prci_readl(pd
, PRCI_CLKMUXSTATUSREG_OFFSET
);
282 v
&= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK
;
285 return div_u64(parent_rate
, div
);
288 /* HFPCLK clock integration */
290 unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk_hw
*hw
,
291 unsigned long parent_rate
)
293 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
294 struct __prci_data
*pd
= pc
->pd
;
295 u32 div
= __prci_readl(pd
, PRCI_HFPCLKPLLDIV_OFFSET
);
297 return div_u64(parent_rate
, div
+ 2);
301 * Core clock mux control
305 * sifive_prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
306 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
308 * Switch the CORECLK mux to the HFCLK input source; return once complete.
310 * Context: Any context. Caller must prevent concurrent changes to the
311 * PRCI_CORECLKSEL_OFFSET register.
313 void sifive_prci_coreclksel_use_hfclk(struct __prci_data
*pd
)
317 r
= __prci_readl(pd
, PRCI_CORECLKSEL_OFFSET
);
318 r
|= PRCI_CORECLKSEL_CORECLKSEL_MASK
;
319 __prci_writel(r
, PRCI_CORECLKSEL_OFFSET
, pd
);
321 r
= __prci_readl(pd
, PRCI_CORECLKSEL_OFFSET
); /* barrier */
325 * sifive_prci_coreclksel_use_corepll() - switch the CORECLK mux to output
327 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
329 * Switch the CORECLK mux to the COREPLL output clock; return once complete.
331 * Context: Any context. Caller must prevent concurrent changes to the
332 * PRCI_CORECLKSEL_OFFSET register.
334 void sifive_prci_coreclksel_use_corepll(struct __prci_data
*pd
)
338 r
= __prci_readl(pd
, PRCI_CORECLKSEL_OFFSET
);
339 r
&= ~PRCI_CORECLKSEL_CORECLKSEL_MASK
;
340 __prci_writel(r
, PRCI_CORECLKSEL_OFFSET
, pd
);
342 r
= __prci_readl(pd
, PRCI_CORECLKSEL_OFFSET
); /* barrier */
346 * sifive_prci_coreclksel_use_final_corepll() - switch the CORECLK mux to output
348 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
350 * Switch the CORECLK mux to the final COREPLL output clock; return once
353 * Context: Any context. Caller must prevent concurrent changes to the
354 * PRCI_CORECLKSEL_OFFSET register.
356 void sifive_prci_coreclksel_use_final_corepll(struct __prci_data
*pd
)
360 r
= __prci_readl(pd
, PRCI_CORECLKSEL_OFFSET
);
361 r
&= ~PRCI_CORECLKSEL_CORECLKSEL_MASK
;
362 __prci_writel(r
, PRCI_CORECLKSEL_OFFSET
, pd
);
364 r
= __prci_readl(pd
, PRCI_CORECLKSEL_OFFSET
); /* barrier */
368 * sifive_prci_corepllsel_use_dvfscorepll() - switch the COREPLL mux to
369 * output DVFS_COREPLL
370 * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
372 * Switch the COREPLL mux to the DVFSCOREPLL output clock; return once complete.
374 * Context: Any context. Caller must prevent concurrent changes to the
375 * PRCI_COREPLLSEL_OFFSET register.
377 void sifive_prci_corepllsel_use_dvfscorepll(struct __prci_data
*pd
)
381 r
= __prci_readl(pd
, PRCI_COREPLLSEL_OFFSET
);
382 r
|= PRCI_COREPLLSEL_COREPLLSEL_MASK
;
383 __prci_writel(r
, PRCI_COREPLLSEL_OFFSET
, pd
);
385 r
= __prci_readl(pd
, PRCI_COREPLLSEL_OFFSET
); /* barrier */
389 * sifive_prci_corepllsel_use_corepll() - switch the COREPLL mux to
391 * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
393 * Switch the COREPLL mux to the COREPLL output clock; return once complete.
395 * Context: Any context. Caller must prevent concurrent changes to the
396 * PRCI_COREPLLSEL_OFFSET register.
398 void sifive_prci_corepllsel_use_corepll(struct __prci_data
*pd
)
402 r
= __prci_readl(pd
, PRCI_COREPLLSEL_OFFSET
);
403 r
&= ~PRCI_COREPLLSEL_COREPLLSEL_MASK
;
404 __prci_writel(r
, PRCI_COREPLLSEL_OFFSET
, pd
);
406 r
= __prci_readl(pd
, PRCI_COREPLLSEL_OFFSET
); /* barrier */
410 * sifive_prci_hfpclkpllsel_use_hfclk() - switch the HFPCLKPLL mux to
412 * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
414 * Switch the HFPCLKPLL mux to the HFCLK input source; return once complete.
416 * Context: Any context. Caller must prevent concurrent changes to the
417 * PRCI_HFPCLKPLLSEL_OFFSET register.
419 void sifive_prci_hfpclkpllsel_use_hfclk(struct __prci_data
*pd
)
423 r
= __prci_readl(pd
, PRCI_HFPCLKPLLSEL_OFFSET
);
424 r
|= PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK
;
425 __prci_writel(r
, PRCI_HFPCLKPLLSEL_OFFSET
, pd
);
427 r
= __prci_readl(pd
, PRCI_HFPCLKPLLSEL_OFFSET
); /* barrier */
431 * sifive_prci_hfpclkpllsel_use_hfpclkpll() - switch the HFPCLKPLL mux to
433 * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
435 * Switch the HFPCLKPLL mux to the HFPCLKPLL output clock; return once complete.
437 * Context: Any context. Caller must prevent concurrent changes to the
438 * PRCI_HFPCLKPLLSEL_OFFSET register.
440 void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data
*pd
)
444 r
= __prci_readl(pd
, PRCI_HFPCLKPLLSEL_OFFSET
);
445 r
&= ~PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK
;
446 __prci_writel(r
, PRCI_HFPCLKPLLSEL_OFFSET
, pd
);
448 r
= __prci_readl(pd
, PRCI_HFPCLKPLLSEL_OFFSET
); /* barrier */
451 /* PCIE AUX clock APIs for enable, disable. */
452 int sifive_prci_pcie_aux_clock_is_enabled(struct clk_hw
*hw
)
454 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
455 struct __prci_data
*pd
= pc
->pd
;
458 r
= __prci_readl(pd
, PRCI_PCIE_AUX_OFFSET
);
460 if (r
& PRCI_PCIE_AUX_EN_MASK
)
466 int sifive_prci_pcie_aux_clock_enable(struct clk_hw
*hw
)
468 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
469 struct __prci_data
*pd
= pc
->pd
;
470 u32 r __maybe_unused
;
472 if (sifive_prci_pcie_aux_clock_is_enabled(hw
))
475 __prci_writel(1, PRCI_PCIE_AUX_OFFSET
, pd
);
476 r
= __prci_readl(pd
, PRCI_PCIE_AUX_OFFSET
); /* barrier */
481 void sifive_prci_pcie_aux_clock_disable(struct clk_hw
*hw
)
483 struct __prci_clock
*pc
= clk_hw_to_prci_clock(hw
);
484 struct __prci_data
*pd
= pc
->pd
;
485 u32 r __maybe_unused
;
487 __prci_writel(0, PRCI_PCIE_AUX_OFFSET
, pd
);
488 r
= __prci_readl(pd
, PRCI_PCIE_AUX_OFFSET
); /* barrier */
493 * __prci_register_clocks() - register clock controls in the PRCI
494 * @dev: Linux struct device
495 * @pd: The pointer for PRCI per-device instance data
496 * @desc: The pointer for the information of clocks of each SoCs
498 * Register the list of clock controls described in __prci_init_clocks[] with
499 * the Linux clock framework.
501 * Return: 0 upon success or a negative error code upon failure.
503 static int __prci_register_clocks(struct device
*dev
, struct __prci_data
*pd
,
504 const struct prci_clk_desc
*desc
)
506 struct clk_init_data init
= { };
507 struct __prci_clock
*pic
;
508 int parent_count
, i
, r
;
510 parent_count
= of_clk_get_parent_count(dev
->of_node
);
511 if (parent_count
!= EXPECTED_CLK_PARENT_COUNT
) {
512 dev_err(dev
, "expected only two parent clocks, found %d\n",
518 for (i
= 0; i
< desc
->num_clks
; ++i
) {
519 pic
= &(desc
->clks
[i
]);
521 init
.name
= pic
->name
;
522 init
.parent_names
= &pic
->parent_name
;
523 init
.num_parents
= 1;
525 pic
->hw
.init
= &init
;
530 __prci_wrpll_read_cfg0(pd
, pic
->pwd
);
532 r
= devm_clk_hw_register(dev
, &pic
->hw
);
534 dev_warn(dev
, "Failed to register clock %s: %d\n",
539 pd
->hw_clks
.hws
[i
] = &pic
->hw
;
544 r
= devm_of_clk_add_hw_provider(dev
, of_clk_hw_onecell_get
,
547 dev_err(dev
, "could not add hw_provider: %d\n", r
);
555 * sifive_prci_probe() - initialize prci data and check parent count
556 * @pdev: platform device pointer for the prci
558 * Return: 0 upon success or a negative error code upon failure.
560 static int sifive_prci_probe(struct platform_device
*pdev
)
562 struct device
*dev
= &pdev
->dev
;
563 struct __prci_data
*pd
;
564 const struct prci_clk_desc
*desc
;
567 desc
= of_device_get_match_data(&pdev
->dev
);
569 pd
= devm_kzalloc(dev
, struct_size(pd
, hw_clks
.hws
, desc
->num_clks
), GFP_KERNEL
);
573 pd
->va
= devm_platform_ioremap_resource(pdev
, 0);
575 return PTR_ERR(pd
->va
);
577 pd
->reset
.rcdev
.owner
= THIS_MODULE
;
578 pd
->reset
.rcdev
.nr_resets
= PRCI_RST_NR
;
579 pd
->reset
.rcdev
.ops
= &reset_simple_ops
;
580 pd
->reset
.rcdev
.of_node
= pdev
->dev
.of_node
;
581 pd
->reset
.active_low
= true;
582 pd
->reset
.membase
= pd
->va
+ PRCI_DEVICESRESETREG_OFFSET
;
583 spin_lock_init(&pd
->reset
.lock
);
585 r
= devm_reset_controller_register(&pdev
->dev
, &pd
->reset
.rcdev
);
587 dev_err(dev
, "could not register reset controller: %d\n", r
);
590 r
= __prci_register_clocks(dev
, pd
, desc
);
592 dev_err(dev
, "could not register clocks: %d\n", r
);
596 dev_dbg(dev
, "SiFive PRCI probed\n");
601 static const struct of_device_id sifive_prci_of_match
[] = {
602 {.compatible
= "sifive,fu540-c000-prci", .data
= &prci_clk_fu540
},
603 {.compatible
= "sifive,fu740-c000-prci", .data
= &prci_clk_fu740
},
606 MODULE_DEVICE_TABLE(of
, sifive_prci_of_match
);
608 static struct platform_driver sifive_prci_driver
= {
610 .name
= "sifive-clk-prci",
611 .of_match_table
= sifive_prci_of_match
,
613 .probe
= sifive_prci_probe
,
615 module_platform_driver(sifive_prci_driver
);
617 MODULE_AUTHOR("Paul Walmsley <paul.walmsley@sifive.com>");
618 MODULE_DESCRIPTION("SiFive Power Reset Clock Interface (PRCI) driver");
619 MODULE_LICENSE("GPL");