This source file includes following definitions.
- __prci_readl
- __prci_writel
- __prci_wrpll_unpack
- __prci_wrpll_pack
- __prci_wrpll_read_cfg
- __prci_wrpll_write_cfg
- __prci_coreclksel_use_hfclk
- __prci_coreclksel_use_corepll
- sifive_fu540_prci_wrpll_recalc_rate
- sifive_fu540_prci_wrpll_round_rate
- sifive_fu540_prci_wrpll_set_rate
- sifive_fu540_prci_tlclksel_recalc_rate
- __prci_register_clocks
- sifive_fu540_prci_probe
- sifive_fu540_prci_init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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>
33 #include <linux/io.h>
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
42
43
44 #define EXPECTED_CLK_PARENT_COUNT 2
45
46
47
48
49
50
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
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
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
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
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
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
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
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
136
137
138
139
140
141
142
143
144
145 struct __prci_data {
146 void __iomem *va;
147 struct clk_hw_onecell_data hw_clks;
148 };
149
150
151
152
153
154
155
156
157
158
159
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
170
171
172
173
174
175
176
177
178
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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
258 c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK;
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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
286 r |= PRCI_COREPLLCFG0_FSE_MASK;
287
288 return r;
289 }
290
291
292
293
294
295
296
297
298
299
300
301
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
311
312
313
314
315
316
317
318
319
320
321
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
333
334
335
336
337
338
339
340
341
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);
352 }
353
354
355
356
357
358
359
360
361
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);
372 }
373
374
375
376
377
378
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
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
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
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
512
513
514
515
516
517
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
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
578
579
580
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);