Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/clk-axm5516.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* drivers/clk/clk-axm5516.c
4
*
5
* Provides clock implementations for three different types of clock devices on
6
* the Axxia device: PLL clock, a clock divider and a clock mux.
7
*
8
* Copyright (C) 2014 LSI Corporation
9
*/
10
#include <linux/module.h>
11
#include <linux/kernel.h>
12
#include <linux/slab.h>
13
#include <linux/platform_device.h>
14
#include <linux/of.h>
15
#include <linux/of_address.h>
16
#include <linux/clk-provider.h>
17
#include <linux/regmap.h>
18
#include <dt-bindings/clock/lsi,axm5516-clks.h>
19
20
21
/**
22
* struct axxia_clk - Common struct to all Axxia clocks.
23
* @hw: clk_hw for the common clk framework
24
* @regmap: Regmap for the clock control registers
25
*/
26
struct axxia_clk {
27
struct clk_hw hw;
28
struct regmap *regmap;
29
};
30
#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)
31
32
/**
33
* struct axxia_pllclk - Axxia PLL generated clock.
34
* @aclk: Common struct
35
* @reg: Offset into regmap for PLL control register
36
*/
37
struct axxia_pllclk {
38
struct axxia_clk aclk;
39
u32 reg;
40
};
41
#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)
42
43
/**
44
* axxia_pllclk_recalc - Calculate the PLL generated clock rate given the
45
* parent clock rate.
46
*/
47
static unsigned long
48
axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)
49
{
50
struct axxia_clk *aclk = to_axxia_clk(hw);
51
struct axxia_pllclk *pll = to_axxia_pllclk(aclk);
52
unsigned long rate, fbdiv, refdiv, postdiv;
53
u32 control;
54
55
regmap_read(aclk->regmap, pll->reg, &control);
56
postdiv = ((control >> 0) & 0xf) + 1;
57
fbdiv = ((control >> 4) & 0xfff) + 3;
58
refdiv = ((control >> 16) & 0x1f) + 1;
59
rate = (parent_rate / (refdiv * postdiv)) * fbdiv;
60
61
return rate;
62
}
63
64
static const struct clk_ops axxia_pllclk_ops = {
65
.recalc_rate = axxia_pllclk_recalc,
66
};
67
68
/**
69
* struct axxia_divclk - Axxia clock divider
70
* @aclk: Common struct
71
* @reg: Offset into regmap for PLL control register
72
* @shift: Bit position for divider value
73
* @width: Number of bits in divider value
74
*/
75
struct axxia_divclk {
76
struct axxia_clk aclk;
77
u32 reg;
78
u32 shift;
79
u32 width;
80
};
81
#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)
82
83
/**
84
* axxia_divclk_recalc_rate - Calculate clock divider output rage
85
*/
86
static unsigned long
87
axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
88
{
89
struct axxia_clk *aclk = to_axxia_clk(hw);
90
struct axxia_divclk *divclk = to_axxia_divclk(aclk);
91
u32 ctrl, div;
92
93
regmap_read(aclk->regmap, divclk->reg, &ctrl);
94
div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));
95
96
return parent_rate / div;
97
}
98
99
static const struct clk_ops axxia_divclk_ops = {
100
.recalc_rate = axxia_divclk_recalc_rate,
101
};
102
103
/**
104
* struct axxia_clkmux - Axxia clock mux
105
* @aclk: Common struct
106
* @reg: Offset into regmap for PLL control register
107
* @shift: Bit position for selection value
108
* @width: Number of bits in selection value
109
*/
110
struct axxia_clkmux {
111
struct axxia_clk aclk;
112
u32 reg;
113
u32 shift;
114
u32 width;
115
};
116
#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)
117
118
/**
119
* axxia_clkmux_get_parent - Return the index of selected parent clock
120
*/
121
static u8 axxia_clkmux_get_parent(struct clk_hw *hw)
122
{
123
struct axxia_clk *aclk = to_axxia_clk(hw);
124
struct axxia_clkmux *mux = to_axxia_clkmux(aclk);
125
u32 ctrl, parent;
126
127
regmap_read(aclk->regmap, mux->reg, &ctrl);
128
parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);
129
130
return (u8) parent;
131
}
132
133
static const struct clk_ops axxia_clkmux_ops = {
134
.get_parent = axxia_clkmux_get_parent,
135
};
136
137
138
/*
139
* PLLs
140
*/
141
142
static struct axxia_pllclk clk_fab_pll = {
143
.aclk.hw.init = &(struct clk_init_data){
144
.name = "clk_fab_pll",
145
.parent_names = (const char *[]){
146
"clk_ref0"
147
},
148
.num_parents = 1,
149
.ops = &axxia_pllclk_ops,
150
},
151
.reg = 0x01800,
152
};
153
154
static struct axxia_pllclk clk_cpu_pll = {
155
.aclk.hw.init = &(struct clk_init_data){
156
.name = "clk_cpu_pll",
157
.parent_names = (const char *[]){
158
"clk_ref0"
159
},
160
.num_parents = 1,
161
.ops = &axxia_pllclk_ops,
162
},
163
.reg = 0x02000,
164
};
165
166
static struct axxia_pllclk clk_sys_pll = {
167
.aclk.hw.init = &(struct clk_init_data){
168
.name = "clk_sys_pll",
169
.parent_names = (const char *[]){
170
"clk_ref0"
171
},
172
.num_parents = 1,
173
.ops = &axxia_pllclk_ops,
174
},
175
.reg = 0x02800,
176
};
177
178
static struct axxia_pllclk clk_sm0_pll = {
179
.aclk.hw.init = &(struct clk_init_data){
180
.name = "clk_sm0_pll",
181
.parent_names = (const char *[]){
182
"clk_ref2"
183
},
184
.num_parents = 1,
185
.ops = &axxia_pllclk_ops,
186
},
187
.reg = 0x03000,
188
};
189
190
static struct axxia_pllclk clk_sm1_pll = {
191
.aclk.hw.init = &(struct clk_init_data){
192
.name = "clk_sm1_pll",
193
.parent_names = (const char *[]){
194
"clk_ref1"
195
},
196
.num_parents = 1,
197
.ops = &axxia_pllclk_ops,
198
},
199
.reg = 0x03800,
200
};
201
202
/*
203
* Clock dividers
204
*/
205
206
static struct axxia_divclk clk_cpu0_div = {
207
.aclk.hw.init = &(struct clk_init_data){
208
.name = "clk_cpu0_div",
209
.parent_names = (const char *[]){
210
"clk_cpu_pll"
211
},
212
.num_parents = 1,
213
.ops = &axxia_divclk_ops,
214
},
215
.reg = 0x10008,
216
.shift = 0,
217
.width = 4,
218
};
219
220
static struct axxia_divclk clk_cpu1_div = {
221
.aclk.hw.init = &(struct clk_init_data){
222
.name = "clk_cpu1_div",
223
.parent_names = (const char *[]){
224
"clk_cpu_pll"
225
},
226
.num_parents = 1,
227
.ops = &axxia_divclk_ops,
228
},
229
.reg = 0x10008,
230
.shift = 4,
231
.width = 4,
232
};
233
234
static struct axxia_divclk clk_cpu2_div = {
235
.aclk.hw.init = &(struct clk_init_data){
236
.name = "clk_cpu2_div",
237
.parent_names = (const char *[]){
238
"clk_cpu_pll"
239
},
240
.num_parents = 1,
241
.ops = &axxia_divclk_ops,
242
},
243
.reg = 0x10008,
244
.shift = 8,
245
.width = 4,
246
};
247
248
static struct axxia_divclk clk_cpu3_div = {
249
.aclk.hw.init = &(struct clk_init_data){
250
.name = "clk_cpu3_div",
251
.parent_names = (const char *[]){
252
"clk_cpu_pll"
253
},
254
.num_parents = 1,
255
.ops = &axxia_divclk_ops,
256
},
257
.reg = 0x10008,
258
.shift = 12,
259
.width = 4,
260
};
261
262
static struct axxia_divclk clk_nrcp_div = {
263
.aclk.hw.init = &(struct clk_init_data){
264
.name = "clk_nrcp_div",
265
.parent_names = (const char *[]){
266
"clk_sys_pll"
267
},
268
.num_parents = 1,
269
.ops = &axxia_divclk_ops,
270
},
271
.reg = 0x1000c,
272
.shift = 0,
273
.width = 4,
274
};
275
276
static struct axxia_divclk clk_sys_div = {
277
.aclk.hw.init = &(struct clk_init_data){
278
.name = "clk_sys_div",
279
.parent_names = (const char *[]){
280
"clk_sys_pll"
281
},
282
.num_parents = 1,
283
.ops = &axxia_divclk_ops,
284
},
285
.reg = 0x1000c,
286
.shift = 4,
287
.width = 4,
288
};
289
290
static struct axxia_divclk clk_fab_div = {
291
.aclk.hw.init = &(struct clk_init_data){
292
.name = "clk_fab_div",
293
.parent_names = (const char *[]){
294
"clk_fab_pll"
295
},
296
.num_parents = 1,
297
.ops = &axxia_divclk_ops,
298
},
299
.reg = 0x1000c,
300
.shift = 8,
301
.width = 4,
302
};
303
304
static struct axxia_divclk clk_per_div = {
305
.aclk.hw.init = &(struct clk_init_data){
306
.name = "clk_per_div",
307
.parent_names = (const char *[]){
308
"clk_sm1_pll"
309
},
310
.num_parents = 1,
311
.ops = &axxia_divclk_ops,
312
},
313
.reg = 0x1000c,
314
.shift = 12,
315
.width = 4,
316
};
317
318
static struct axxia_divclk clk_mmc_div = {
319
.aclk.hw.init = &(struct clk_init_data){
320
.name = "clk_mmc_div",
321
.parent_names = (const char *[]){
322
"clk_sm1_pll"
323
},
324
.num_parents = 1,
325
.ops = &axxia_divclk_ops,
326
},
327
.reg = 0x1000c,
328
.shift = 16,
329
.width = 4,
330
};
331
332
/*
333
* Clock MUXes
334
*/
335
336
static struct axxia_clkmux clk_cpu0_mux = {
337
.aclk.hw.init = &(struct clk_init_data){
338
.name = "clk_cpu0",
339
.parent_names = (const char *[]){
340
"clk_ref0",
341
"clk_cpu_pll",
342
"clk_cpu0_div",
343
"clk_cpu0_div"
344
},
345
.num_parents = 4,
346
.ops = &axxia_clkmux_ops,
347
},
348
.reg = 0x10000,
349
.shift = 0,
350
.width = 2,
351
};
352
353
static struct axxia_clkmux clk_cpu1_mux = {
354
.aclk.hw.init = &(struct clk_init_data){
355
.name = "clk_cpu1",
356
.parent_names = (const char *[]){
357
"clk_ref0",
358
"clk_cpu_pll",
359
"clk_cpu1_div",
360
"clk_cpu1_div"
361
},
362
.num_parents = 4,
363
.ops = &axxia_clkmux_ops,
364
},
365
.reg = 0x10000,
366
.shift = 2,
367
.width = 2,
368
};
369
370
static struct axxia_clkmux clk_cpu2_mux = {
371
.aclk.hw.init = &(struct clk_init_data){
372
.name = "clk_cpu2",
373
.parent_names = (const char *[]){
374
"clk_ref0",
375
"clk_cpu_pll",
376
"clk_cpu2_div",
377
"clk_cpu2_div"
378
},
379
.num_parents = 4,
380
.ops = &axxia_clkmux_ops,
381
},
382
.reg = 0x10000,
383
.shift = 4,
384
.width = 2,
385
};
386
387
static struct axxia_clkmux clk_cpu3_mux = {
388
.aclk.hw.init = &(struct clk_init_data){
389
.name = "clk_cpu3",
390
.parent_names = (const char *[]){
391
"clk_ref0",
392
"clk_cpu_pll",
393
"clk_cpu3_div",
394
"clk_cpu3_div"
395
},
396
.num_parents = 4,
397
.ops = &axxia_clkmux_ops,
398
},
399
.reg = 0x10000,
400
.shift = 6,
401
.width = 2,
402
};
403
404
static struct axxia_clkmux clk_nrcp_mux = {
405
.aclk.hw.init = &(struct clk_init_data){
406
.name = "clk_nrcp",
407
.parent_names = (const char *[]){
408
"clk_ref0",
409
"clk_sys_pll",
410
"clk_nrcp_div",
411
"clk_nrcp_div"
412
},
413
.num_parents = 4,
414
.ops = &axxia_clkmux_ops,
415
},
416
.reg = 0x10004,
417
.shift = 0,
418
.width = 2,
419
};
420
421
static struct axxia_clkmux clk_sys_mux = {
422
.aclk.hw.init = &(struct clk_init_data){
423
.name = "clk_sys",
424
.parent_names = (const char *[]){
425
"clk_ref0",
426
"clk_sys_pll",
427
"clk_sys_div",
428
"clk_sys_div"
429
},
430
.num_parents = 4,
431
.ops = &axxia_clkmux_ops,
432
},
433
.reg = 0x10004,
434
.shift = 2,
435
.width = 2,
436
};
437
438
static struct axxia_clkmux clk_fab_mux = {
439
.aclk.hw.init = &(struct clk_init_data){
440
.name = "clk_fab",
441
.parent_names = (const char *[]){
442
"clk_ref0",
443
"clk_fab_pll",
444
"clk_fab_div",
445
"clk_fab_div"
446
},
447
.num_parents = 4,
448
.ops = &axxia_clkmux_ops,
449
},
450
.reg = 0x10004,
451
.shift = 4,
452
.width = 2,
453
};
454
455
static struct axxia_clkmux clk_per_mux = {
456
.aclk.hw.init = &(struct clk_init_data){
457
.name = "clk_per",
458
.parent_names = (const char *[]){
459
"clk_ref1",
460
"clk_per_div"
461
},
462
.num_parents = 2,
463
.ops = &axxia_clkmux_ops,
464
},
465
.reg = 0x10004,
466
.shift = 6,
467
.width = 1,
468
};
469
470
static struct axxia_clkmux clk_mmc_mux = {
471
.aclk.hw.init = &(struct clk_init_data){
472
.name = "clk_mmc",
473
.parent_names = (const char *[]){
474
"clk_ref1",
475
"clk_mmc_div"
476
},
477
.num_parents = 2,
478
.ops = &axxia_clkmux_ops,
479
},
480
.reg = 0x10004,
481
.shift = 9,
482
.width = 1,
483
};
484
485
/* Table of all supported clocks indexed by the clock identifiers from the
486
* device tree binding
487
*/
488
static struct axxia_clk *axmclk_clocks[] = {
489
[AXXIA_CLK_FAB_PLL] = &clk_fab_pll.aclk,
490
[AXXIA_CLK_CPU_PLL] = &clk_cpu_pll.aclk,
491
[AXXIA_CLK_SYS_PLL] = &clk_sys_pll.aclk,
492
[AXXIA_CLK_SM0_PLL] = &clk_sm0_pll.aclk,
493
[AXXIA_CLK_SM1_PLL] = &clk_sm1_pll.aclk,
494
[AXXIA_CLK_FAB_DIV] = &clk_fab_div.aclk,
495
[AXXIA_CLK_SYS_DIV] = &clk_sys_div.aclk,
496
[AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,
497
[AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,
498
[AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,
499
[AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,
500
[AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,
501
[AXXIA_CLK_PER_DIV] = &clk_per_div.aclk,
502
[AXXIA_CLK_MMC_DIV] = &clk_mmc_div.aclk,
503
[AXXIA_CLK_FAB] = &clk_fab_mux.aclk,
504
[AXXIA_CLK_SYS] = &clk_sys_mux.aclk,
505
[AXXIA_CLK_NRCP] = &clk_nrcp_mux.aclk,
506
[AXXIA_CLK_CPU0] = &clk_cpu0_mux.aclk,
507
[AXXIA_CLK_CPU1] = &clk_cpu1_mux.aclk,
508
[AXXIA_CLK_CPU2] = &clk_cpu2_mux.aclk,
509
[AXXIA_CLK_CPU3] = &clk_cpu3_mux.aclk,
510
[AXXIA_CLK_PER] = &clk_per_mux.aclk,
511
[AXXIA_CLK_MMC] = &clk_mmc_mux.aclk,
512
};
513
514
static struct clk_hw *
515
of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused)
516
{
517
unsigned int idx = clkspec->args[0];
518
519
if (idx >= ARRAY_SIZE(axmclk_clocks)) {
520
pr_err("%s: invalid index %u\n", __func__, idx);
521
return ERR_PTR(-EINVAL);
522
}
523
524
return &axmclk_clocks[idx]->hw;
525
}
526
527
static const struct regmap_config axmclk_regmap_config = {
528
.reg_bits = 32,
529
.reg_stride = 4,
530
.val_bits = 32,
531
.max_register = 0x1fffc,
532
.fast_io = true,
533
};
534
535
static const struct of_device_id axmclk_match_table[] = {
536
{ .compatible = "lsi,axm5516-clks" },
537
{ }
538
};
539
MODULE_DEVICE_TABLE(of, axmclk_match_table);
540
541
static int axmclk_probe(struct platform_device *pdev)
542
{
543
void __iomem *base;
544
int i, ret;
545
struct device *dev = &pdev->dev;
546
struct regmap *regmap;
547
size_t num_clks;
548
549
base = devm_platform_ioremap_resource(pdev, 0);
550
if (IS_ERR(base))
551
return PTR_ERR(base);
552
553
regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);
554
if (IS_ERR(regmap))
555
return PTR_ERR(regmap);
556
557
num_clks = ARRAY_SIZE(axmclk_clocks);
558
pr_info("axmclk: supporting %zu clocks\n", num_clks);
559
560
/* Update each entry with the allocated regmap and register the clock
561
* with the common clock framework
562
*/
563
for (i = 0; i < num_clks; i++) {
564
axmclk_clocks[i]->regmap = regmap;
565
ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw);
566
if (ret)
567
return ret;
568
}
569
570
return devm_of_clk_add_hw_provider(dev, of_clk_axmclk_get, NULL);
571
}
572
573
static struct platform_driver axmclk_driver = {
574
.probe = axmclk_probe,
575
.driver = {
576
.name = "clk-axm5516",
577
.of_match_table = axmclk_match_table,
578
},
579
};
580
581
static int __init axmclk_init(void)
582
{
583
return platform_driver_register(&axmclk_driver);
584
}
585
core_initcall(axmclk_init);
586
587
static void __exit axmclk_exit(void)
588
{
589
platform_driver_unregister(&axmclk_driver);
590
}
591
module_exit(axmclk_exit);
592
593
MODULE_DESCRIPTION("AXM5516 clock driver");
594
MODULE_LICENSE("GPL v2");
595
MODULE_ALIAS("platform:clk-axm5516");
596
597