1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2020 SiFive, Inc.
4 * Copyright (C) 2020 Zong Li
7 #include <linux/clkdev.h>
8 #include <linux/delay.h>
10 #include <linux/of_device.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 */
452 * __prci_register_clocks() - register clock controls in the PRCI
453 * @dev: Linux struct device
454 * @pd: The pointer for PRCI per-device instance data
455 * @desc: The pointer for the information of clocks of each SoCs
457 * Register the list of clock controls described in __prci_init_clocks[] with
458 * the Linux clock framework.
460 * Return: 0 upon success or a negative error code upon failure.
462 static int __prci_register_clocks(struct device
*dev
, struct __prci_data
*pd
,
463 const struct prci_clk_desc
*desc
)
465 struct clk_init_data init
= { };
466 struct __prci_clock
*pic
;
467 int parent_count
, i
, r
;
469 parent_count
= of_clk_get_parent_count(dev
->of_node
);
470 if (parent_count
!= EXPECTED_CLK_PARENT_COUNT
) {
471 dev_err(dev
, "expected only two parent clocks, found %d\n",
477 for (i
= 0; i
< desc
->num_clks
; ++i
) {
478 pic
= &(desc
->clks
[i
]);
480 init
.name
= pic
->name
;
481 init
.parent_names
= &pic
->parent_name
;
482 init
.num_parents
= 1;
484 pic
->hw
.init
= &init
;
489 __prci_wrpll_read_cfg0(pd
, pic
->pwd
);
491 r
= devm_clk_hw_register(dev
, &pic
->hw
);
493 dev_warn(dev
, "Failed to register clock %s: %d\n",
498 r
= clk_hw_register_clkdev(&pic
->hw
, pic
->name
, dev_name(dev
));
500 dev_warn(dev
, "Failed to register clkdev for %s: %d\n",
505 pd
->hw_clks
.hws
[i
] = &pic
->hw
;
510 r
= devm_of_clk_add_hw_provider(dev
, of_clk_hw_onecell_get
,
513 dev_err(dev
, "could not add hw_provider: %d\n", r
);
521 * sifive_prci_init() - initialize prci data and check parent count
522 * @pdev: platform device pointer for the prci
524 * Return: 0 upon success or a negative error code upon failure.
526 static int sifive_prci_probe(struct platform_device
*pdev
)
528 struct device
*dev
= &pdev
->dev
;
529 struct resource
*res
;
530 struct __prci_data
*pd
;
531 const struct prci_clk_desc
*desc
;
534 desc
= of_device_get_match_data(&pdev
->dev
);
536 pd
= devm_kzalloc(dev
, struct_size(pd
, hw_clks
.hws
, desc
->num_clks
), GFP_KERNEL
);
540 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
541 pd
->va
= devm_ioremap_resource(dev
, res
);
543 return PTR_ERR(pd
->va
);
545 r
= __prci_register_clocks(dev
, pd
, desc
);
547 dev_err(dev
, "could not register clocks: %d\n", r
);
551 dev_dbg(dev
, "SiFive PRCI probed\n");
556 static const struct of_device_id sifive_prci_of_match
[] = {
557 {.compatible
= "sifive,fu540-c000-prci", .data
= &prci_clk_fu540
},
558 {.compatible
= "sifive,fu740-c000-prci", .data
= &prci_clk_fu740
},
562 static struct platform_driver sifive_prci_driver
= {
564 .name
= "sifive-clk-prci",
565 .of_match_table
= sifive_prci_of_match
,
567 .probe
= sifive_prci_probe
,
570 static int __init
sifive_prci_init(void)
572 return platform_driver_register(&sifive_prci_driver
);
574 core_initcall(sifive_prci_init
);