Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/berlin/bg2q.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (c) 2014 Marvell Technology Group Ltd.
4
*
5
* Alexandre Belloni <[email protected]>
6
* Sebastian Hesselbarth <[email protected]>
7
*/
8
9
#include <linux/clk.h>
10
#include <linux/clk-provider.h>
11
#include <linux/io.h>
12
#include <linux/kernel.h>
13
#include <linux/of.h>
14
#include <linux/of_address.h>
15
#include <linux/slab.h>
16
17
#include <dt-bindings/clock/berlin2q.h>
18
19
#include "berlin2-div.h"
20
#include "berlin2-pll.h"
21
#include "common.h"
22
23
#define REG_PINMUX0 0x0018
24
#define REG_PINMUX5 0x002c
25
#define REG_SYSPLLCTL0 0x0030
26
#define REG_SYSPLLCTL4 0x0040
27
#define REG_CLKENABLE 0x00e8
28
#define REG_CLKSELECT0 0x00ec
29
#define REG_CLKSELECT1 0x00f0
30
#define REG_CLKSELECT2 0x00f4
31
#define REG_CLKSWITCH0 0x00f8
32
#define REG_CLKSWITCH1 0x00fc
33
#define REG_SW_GENERIC0 0x0110
34
#define REG_SW_GENERIC3 0x011c
35
#define REG_SDIO0XIN_CLKCTL 0x0158
36
#define REG_SDIO1XIN_CLKCTL 0x015c
37
38
#define MAX_CLKS 28
39
static struct clk_hw_onecell_data *clk_data;
40
static DEFINE_SPINLOCK(lock);
41
static void __iomem *gbase;
42
static void __iomem *cpupll_base;
43
44
enum {
45
REFCLK,
46
SYSPLL, CPUPLL,
47
AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
48
AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
49
};
50
51
static const char *clk_names[] = {
52
[REFCLK] = "refclk",
53
[SYSPLL] = "syspll",
54
[CPUPLL] = "cpupll",
55
[AVPLL_B1] = "avpll_b1",
56
[AVPLL_B2] = "avpll_b2",
57
[AVPLL_B3] = "avpll_b3",
58
[AVPLL_B4] = "avpll_b4",
59
[AVPLL_B5] = "avpll_b5",
60
[AVPLL_B6] = "avpll_b6",
61
[AVPLL_B7] = "avpll_b7",
62
[AVPLL_B8] = "avpll_b8",
63
};
64
65
static const struct berlin2_pll_map bg2q_pll_map __initconst = {
66
.vcodiv = {1, 0, 2, 0, 3, 4, 0, 6, 8},
67
.mult = 1,
68
.fbdiv_shift = 7,
69
.rfdiv_shift = 2,
70
.divsel_shift = 9,
71
};
72
73
static const u8 default_parent_ids[] = {
74
SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
75
};
76
77
static const struct berlin2_div_data bg2q_divs[] __initconst = {
78
{
79
.name = "sys",
80
.parent_ids = default_parent_ids,
81
.num_parents = ARRAY_SIZE(default_parent_ids),
82
.map = {
83
BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
84
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
85
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
86
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
87
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
88
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
89
},
90
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
91
.flags = CLK_IGNORE_UNUSED,
92
},
93
{
94
.name = "drmfigo",
95
.parent_ids = default_parent_ids,
96
.num_parents = ARRAY_SIZE(default_parent_ids),
97
.map = {
98
BERLIN2_DIV_GATE(REG_CLKENABLE, 17),
99
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
100
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
101
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
102
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
103
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
104
},
105
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
106
.flags = 0,
107
},
108
{
109
.name = "cfg",
110
.parent_ids = default_parent_ids,
111
.num_parents = ARRAY_SIZE(default_parent_ids),
112
.map = {
113
BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
114
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 12),
115
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 15),
116
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 9),
117
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 10),
118
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 11),
119
},
120
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
121
.flags = 0,
122
},
123
{
124
.name = "gfx2d",
125
.parent_ids = default_parent_ids,
126
.num_parents = ARRAY_SIZE(default_parent_ids),
127
.map = {
128
BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
129
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 18),
130
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 21),
131
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
132
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
133
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
134
},
135
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
136
.flags = 0,
137
},
138
{
139
.name = "zsp",
140
.parent_ids = default_parent_ids,
141
.num_parents = ARRAY_SIZE(default_parent_ids),
142
.map = {
143
BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
144
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 24),
145
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 27),
146
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
147
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
148
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
149
},
150
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
151
.flags = 0,
152
},
153
{
154
.name = "perif",
155
.parent_ids = default_parent_ids,
156
.num_parents = ARRAY_SIZE(default_parent_ids),
157
.map = {
158
BERLIN2_DIV_GATE(REG_CLKENABLE, 7),
159
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 0),
160
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 3),
161
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
162
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
163
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
164
},
165
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
166
.flags = CLK_IGNORE_UNUSED,
167
},
168
{
169
.name = "pcube",
170
.parent_ids = default_parent_ids,
171
.num_parents = ARRAY_SIZE(default_parent_ids),
172
.map = {
173
BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
174
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 6),
175
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 9),
176
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
177
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
178
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
179
},
180
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
181
.flags = 0,
182
},
183
{
184
.name = "vscope",
185
.parent_ids = default_parent_ids,
186
.num_parents = ARRAY_SIZE(default_parent_ids),
187
.map = {
188
BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
189
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 12),
190
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 15),
191
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
192
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
193
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
194
},
195
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
196
.flags = 0,
197
},
198
{
199
.name = "nfc_ecc",
200
.parent_ids = default_parent_ids,
201
.num_parents = ARRAY_SIZE(default_parent_ids),
202
.map = {
203
BERLIN2_DIV_GATE(REG_CLKENABLE, 19),
204
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 18),
205
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 21),
206
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
207
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
208
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
209
},
210
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
211
.flags = 0,
212
},
213
{
214
.name = "vpp",
215
.parent_ids = default_parent_ids,
216
.num_parents = ARRAY_SIZE(default_parent_ids),
217
.map = {
218
BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
219
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 24),
220
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 27),
221
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
222
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
223
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
224
},
225
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
226
.flags = 0,
227
},
228
{
229
.name = "app",
230
.parent_ids = default_parent_ids,
231
.num_parents = ARRAY_SIZE(default_parent_ids),
232
.map = {
233
BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
234
BERLIN2_PLL_SELECT(REG_CLKSELECT2, 0),
235
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 3),
236
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
237
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
238
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
239
},
240
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
241
.flags = 0,
242
},
243
{
244
.name = "sdio0xin",
245
.parent_ids = default_parent_ids,
246
.num_parents = ARRAY_SIZE(default_parent_ids),
247
.map = {
248
BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
249
},
250
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
251
.flags = 0,
252
},
253
{
254
.name = "sdio1xin",
255
.parent_ids = default_parent_ids,
256
.num_parents = ARRAY_SIZE(default_parent_ids),
257
.map = {
258
BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
259
},
260
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
261
.flags = 0,
262
},
263
};
264
265
static const struct berlin2_gate_data bg2q_gates[] __initconst = {
266
{ "gfx2daxi", "perif", 5 },
267
{ "geth0", "perif", 8 },
268
{ "sata", "perif", 9 },
269
{ "ahbapb", "perif", 10, CLK_IGNORE_UNUSED },
270
{ "usb0", "perif", 11 },
271
{ "usb1", "perif", 12 },
272
{ "usb2", "perif", 13 },
273
{ "usb3", "perif", 14 },
274
{ "pbridge", "perif", 15, CLK_IGNORE_UNUSED },
275
{ "sdio", "perif", 16 },
276
{ "nfc", "perif", 18 },
277
{ "pcie", "perif", 22 },
278
};
279
280
static void __init berlin2q_clock_setup(struct device_node *np)
281
{
282
struct device_node *parent_np = of_get_parent(np);
283
const char *parent_names[9];
284
struct clk *clk;
285
struct clk_hw **hws;
286
int n, ret;
287
288
clk_data = kzalloc(struct_size(clk_data, hws, MAX_CLKS), GFP_KERNEL);
289
if (!clk_data) {
290
of_node_put(parent_np);
291
return;
292
}
293
clk_data->num = MAX_CLKS;
294
hws = clk_data->hws;
295
296
gbase = of_iomap(parent_np, 0);
297
if (!gbase) {
298
of_node_put(parent_np);
299
pr_err("%pOF: Unable to map global base\n", np);
300
return;
301
}
302
303
/* BG2Q CPU PLL is not part of global registers */
304
cpupll_base = of_iomap(parent_np, 1);
305
of_node_put(parent_np);
306
if (!cpupll_base) {
307
pr_err("%pOF: Unable to map cpupll base\n", np);
308
iounmap(gbase);
309
return;
310
}
311
312
/* overwrite default clock names with DT provided ones */
313
clk = of_clk_get_by_name(np, clk_names[REFCLK]);
314
if (!IS_ERR(clk)) {
315
clk_names[REFCLK] = __clk_get_name(clk);
316
clk_put(clk);
317
}
318
319
/* simple register PLLs */
320
ret = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
321
clk_names[SYSPLL], clk_names[REFCLK], 0);
322
if (ret)
323
goto bg2q_fail;
324
325
ret = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
326
clk_names[CPUPLL], clk_names[REFCLK], 0);
327
if (ret)
328
goto bg2q_fail;
329
330
/* TODO: add BG2Q AVPLL */
331
332
/*
333
* TODO: add reference clock bypass switches:
334
* memPLLSWBypass, cpuPLLSWBypass, and sysPLLSWBypass
335
*/
336
337
/* clock divider cells */
338
for (n = 0; n < ARRAY_SIZE(bg2q_divs); n++) {
339
const struct berlin2_div_data *dd = &bg2q_divs[n];
340
int k;
341
342
for (k = 0; k < dd->num_parents; k++)
343
parent_names[k] = clk_names[dd->parent_ids[k]];
344
345
hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
346
dd->name, dd->div_flags, parent_names,
347
dd->num_parents, dd->flags, &lock);
348
}
349
350
/* clock gate cells */
351
for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) {
352
const struct berlin2_gate_data *gd = &bg2q_gates[n];
353
354
hws[CLKID_GFX2DAXI + n] = clk_hw_register_gate(NULL, gd->name,
355
gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
356
gd->bit_idx, 0, &lock);
357
}
358
359
/* cpuclk divider is fixed to 1 */
360
hws[CLKID_CPU] =
361
clk_hw_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL],
362
0, 1, 1);
363
/* twdclk is derived from cpu/3 */
364
hws[CLKID_TWD] =
365
clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
366
367
/* check for errors on leaf clocks */
368
for (n = 0; n < MAX_CLKS; n++) {
369
if (!IS_ERR(hws[n]))
370
continue;
371
372
pr_err("%pOF: Unable to register leaf clock %d\n", np, n);
373
goto bg2q_fail;
374
}
375
376
/* register clk-provider */
377
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
378
379
return;
380
381
bg2q_fail:
382
iounmap(cpupll_base);
383
iounmap(gbase);
384
}
385
CLK_OF_DECLARE(berlin2q_clk, "marvell,berlin2q-clk",
386
berlin2q_clock_setup);
387
388