aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandre Belloni <alexandre.belloni@free-electrons.com>2014-05-19 12:43:28 -0400
committerMike Turquette <mturquette@linaro.org>2014-05-29 12:30:19 -0400
commit18882ac5f38ec44e070c68728c2635bcdf60915f (patch)
treea5d8d8e6b2a000d7ab65008a66e2e9691cb8a546
parentba0fae3b06a69aab4ff18f628f88b9f9d653d734 (diff)
clk: berlin: add core clock driver for BG2Q
This driver deals with the core clocks found on Marvell Berlin BG2Q. For the shared register dividers, make use of the corresponding driver and add some single clock muxes and gates for the rest. Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r--drivers/clk/berlin/Makefile1
-rw-r--r--drivers/clk/berlin/bg2q.c389
2 files changed, 390 insertions, 0 deletions
diff --git a/drivers/clk/berlin/Makefile b/drivers/clk/berlin/Makefile
index 2b33e1e74503..2a36ab710a07 100644
--- a/drivers/clk/berlin/Makefile
+++ b/drivers/clk/berlin/Makefile
@@ -1,3 +1,4 @@
1obj-y += berlin2-avpll.o berlin2-pll.o berlin2-div.o 1obj-y += berlin2-avpll.o berlin2-pll.o berlin2-div.o
2obj-$(CONFIG_MACH_BERLIN_BG2) += bg2.o 2obj-$(CONFIG_MACH_BERLIN_BG2) += bg2.o
3obj-$(CONFIG_MACH_BERLIN_BG2CD) += bg2.o 3obj-$(CONFIG_MACH_BERLIN_BG2CD) += bg2.o
4obj-$(CONFIG_MACH_BERLIN_BG2Q) += bg2q.o
diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c
new file mode 100644
index 000000000000..21784e4eb3f0
--- /dev/null
+++ b/drivers/clk/berlin/bg2q.c
@@ -0,0 +1,389 @@
1/*
2 * Copyright (c) 2014 Marvell Technology Group Ltd.
3 *
4 * Alexandre Belloni <alexandre.belloni@free-electrons.com>
5 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/clk.h>
21#include <linux/clk-provider.h>
22#include <linux/kernel.h>
23#include <linux/of.h>
24#include <linux/of_address.h>
25#include <linux/slab.h>
26
27#include <dt-bindings/clock/berlin2q.h>
28
29#include "berlin2-div.h"
30#include "berlin2-pll.h"
31#include "common.h"
32
33#define REG_PINMUX0 0x0018
34#define REG_PINMUX5 0x002c
35#define REG_SYSPLLCTL0 0x0030
36#define REG_SYSPLLCTL4 0x0040
37#define REG_CLKENABLE 0x00e8
38#define REG_CLKSELECT0 0x00ec
39#define REG_CLKSELECT1 0x00f0
40#define REG_CLKSELECT2 0x00f4
41#define REG_CLKSWITCH0 0x00f8
42#define REG_CLKSWITCH1 0x00fc
43#define REG_SW_GENERIC0 0x0110
44#define REG_SW_GENERIC3 0x011c
45#define REG_SDIO0XIN_CLKCTL 0x0158
46#define REG_SDIO1XIN_CLKCTL 0x015c
47
48#define MAX_CLKS 27
49static struct clk *clks[MAX_CLKS];
50static struct clk_onecell_data clk_data;
51static DEFINE_SPINLOCK(lock);
52static void __iomem *gbase;
53static void __iomem *cpupll_base;
54
55enum {
56 REFCLK,
57 SYSPLL, CPUPLL,
58 AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
59 AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
60};
61
62static const char *clk_names[] = {
63 [REFCLK] = "refclk",
64 [SYSPLL] = "syspll",
65 [CPUPLL] = "cpupll",
66 [AVPLL_B1] = "avpll_b1",
67 [AVPLL_B2] = "avpll_b2",
68 [AVPLL_B3] = "avpll_b3",
69 [AVPLL_B4] = "avpll_b4",
70 [AVPLL_B5] = "avpll_b5",
71 [AVPLL_B6] = "avpll_b6",
72 [AVPLL_B7] = "avpll_b7",
73 [AVPLL_B8] = "avpll_b8",
74};
75
76static const struct berlin2_pll_map bg2q_pll_map __initconst = {
77 .vcodiv = {1, 0, 2, 0, 3, 4, 0, 6, 8},
78 .mult = 1,
79 .fbdiv_shift = 7,
80 .rfdiv_shift = 2,
81 .divsel_shift = 9,
82};
83
84static const u8 default_parent_ids[] = {
85 SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
86};
87
88static const struct berlin2_div_data bg2q_divs[] __initconst = {
89 {
90 .name = "sys",
91 .parent_ids = default_parent_ids,
92 .num_parents = ARRAY_SIZE(default_parent_ids),
93 .map = {
94 BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
95 BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
96 BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
97 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
98 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
99 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
100 },
101 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
102 .flags = CLK_IGNORE_UNUSED,
103 },
104 {
105 .name = "drmfigo",
106 .parent_ids = default_parent_ids,
107 .num_parents = ARRAY_SIZE(default_parent_ids),
108 .map = {
109 BERLIN2_DIV_GATE(REG_CLKENABLE, 17),
110 BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
111 BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
112 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
113 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
114 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
115 },
116 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
117 .flags = 0,
118 },
119 {
120 .name = "cfg",
121 .parent_ids = default_parent_ids,
122 .num_parents = ARRAY_SIZE(default_parent_ids),
123 .map = {
124 BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
125 BERLIN2_PLL_SELECT(REG_CLKSELECT0, 12),
126 BERLIN2_DIV_SELECT(REG_CLKSELECT0, 15),
127 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 9),
128 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 10),
129 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 11),
130 },
131 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
132 .flags = 0,
133 },
134 {
135 .name = "gfx2d",
136 .parent_ids = default_parent_ids,
137 .num_parents = ARRAY_SIZE(default_parent_ids),
138 .map = {
139 BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
140 BERLIN2_PLL_SELECT(REG_CLKSELECT0, 18),
141 BERLIN2_DIV_SELECT(REG_CLKSELECT0, 21),
142 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
143 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
144 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
145 },
146 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
147 .flags = 0,
148 },
149 {
150 .name = "zsp",
151 .parent_ids = default_parent_ids,
152 .num_parents = ARRAY_SIZE(default_parent_ids),
153 .map = {
154 BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
155 BERLIN2_PLL_SELECT(REG_CLKSELECT0, 24),
156 BERLIN2_DIV_SELECT(REG_CLKSELECT0, 27),
157 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
158 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
159 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
160 },
161 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
162 .flags = 0,
163 },
164 {
165 .name = "perif",
166 .parent_ids = default_parent_ids,
167 .num_parents = ARRAY_SIZE(default_parent_ids),
168 .map = {
169 BERLIN2_DIV_GATE(REG_CLKENABLE, 7),
170 BERLIN2_PLL_SELECT(REG_CLKSELECT1, 0),
171 BERLIN2_DIV_SELECT(REG_CLKSELECT1, 3),
172 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
173 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
174 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
175 },
176 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
177 .flags = CLK_IGNORE_UNUSED,
178 },
179 {
180 .name = "pcube",
181 .parent_ids = default_parent_ids,
182 .num_parents = ARRAY_SIZE(default_parent_ids),
183 .map = {
184 BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
185 BERLIN2_PLL_SELECT(REG_CLKSELECT1, 6),
186 BERLIN2_DIV_SELECT(REG_CLKSELECT1, 9),
187 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
188 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
189 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
190 },
191 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
192 .flags = 0,
193 },
194 {
195 .name = "vscope",
196 .parent_ids = default_parent_ids,
197 .num_parents = ARRAY_SIZE(default_parent_ids),
198 .map = {
199 BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
200 BERLIN2_PLL_SELECT(REG_CLKSELECT1, 12),
201 BERLIN2_DIV_SELECT(REG_CLKSELECT1, 15),
202 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
203 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
204 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
205 },
206 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
207 .flags = 0,
208 },
209 {
210 .name = "nfc_ecc",
211 .parent_ids = default_parent_ids,
212 .num_parents = ARRAY_SIZE(default_parent_ids),
213 .map = {
214 BERLIN2_DIV_GATE(REG_CLKENABLE, 19),
215 BERLIN2_PLL_SELECT(REG_CLKSELECT1, 18),
216 BERLIN2_DIV_SELECT(REG_CLKSELECT1, 21),
217 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
218 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
219 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
220 },
221 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
222 .flags = 0,
223 },
224 {
225 .name = "vpp",
226 .parent_ids = default_parent_ids,
227 .num_parents = ARRAY_SIZE(default_parent_ids),
228 .map = {
229 BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
230 BERLIN2_PLL_SELECT(REG_CLKSELECT1, 24),
231 BERLIN2_DIV_SELECT(REG_CLKSELECT1, 27),
232 BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
233 BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
234 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
235 },
236 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
237 .flags = 0,
238 },
239 {
240 .name = "app",
241 .parent_ids = default_parent_ids,
242 .num_parents = ARRAY_SIZE(default_parent_ids),
243 .map = {
244 BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
245 BERLIN2_PLL_SELECT(REG_CLKSELECT2, 0),
246 BERLIN2_DIV_SELECT(REG_CLKSELECT2, 3),
247 BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
248 BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
249 BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
250 },
251 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
252 .flags = 0,
253 },
254 {
255 .name = "sdio0xin",
256 .parent_ids = default_parent_ids,
257 .num_parents = ARRAY_SIZE(default_parent_ids),
258 .map = {
259 BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
260 },
261 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
262 .flags = 0,
263 },
264 {
265 .name = "sdio1xin",
266 .parent_ids = default_parent_ids,
267 .num_parents = ARRAY_SIZE(default_parent_ids),
268 .map = {
269 BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
270 },
271 .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
272 .flags = 0,
273 },
274};
275
276static const struct berlin2_gate_data bg2q_gates[] __initconst = {
277 { "gfx2daxi", "perif", 5 },
278 { "geth0", "perif", 8 },
279 { "sata", "perif", 9 },
280 { "ahbapb", "perif", 10, CLK_IGNORE_UNUSED },
281 { "usb0", "perif", 11 },
282 { "usb1", "perif", 12 },
283 { "usb2", "perif", 13 },
284 { "usb3", "perif", 14 },
285 { "pbridge", "perif", 15, CLK_IGNORE_UNUSED },
286 { "sdio", "perif", 16, CLK_IGNORE_UNUSED },
287 { "nfc", "perif", 18 },
288 { "smemc", "perif", 19 },
289 { "pcie", "perif", 22 },
290};
291
292static void __init berlin2q_clock_setup(struct device_node *np)
293{
294 const char *parent_names[9];
295 struct clk *clk;
296 int n;
297
298 gbase = of_iomap(np, 0);
299 if (!gbase) {
300 pr_err("%s: Unable to map global base\n", np->full_name);
301 return;
302 }
303
304 /* BG2Q CPU PLL is not part of global registers */
305 cpupll_base = of_iomap(np, 1);
306 if (!cpupll_base) {
307 pr_err("%s: Unable to map cpupll base\n", np->full_name);
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 clk = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
321 clk_names[SYSPLL], clk_names[REFCLK], 0);
322 if (IS_ERR(clk))
323 goto bg2q_fail;
324
325 clk = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
326 clk_names[CPUPLL], clk_names[REFCLK], 0);
327 if (IS_ERR(clk))
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 clks[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 clks[CLKID_GFX2DAXI + n] = clk_register_gate(NULL, gd->name,
355 gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
356 gd->bit_idx, 0, &lock);
357 }
358
359 /*
360 * twdclk is derived from cpu/3
361 * TODO: use cpupll until cpuclk is not available
362 */
363 clks[CLKID_TWD] =
364 clk_register_fixed_factor(NULL, "twd", clk_names[CPUPLL],
365 0, 1, 3);
366
367 /* check for errors on leaf clocks */
368 for (n = 0; n < MAX_CLKS; n++) {
369 if (!IS_ERR(clks[n]))
370 continue;
371
372 pr_err("%s: Unable to register leaf clock %d\n",
373 np->full_name, n);
374 goto bg2q_fail;
375 }
376
377 /* register clk-provider */
378 clk_data.clks = clks;
379 clk_data.clk_num = MAX_CLKS;
380 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
381
382 return;
383
384bg2q_fail:
385 iounmap(cpupll_base);
386 iounmap(gbase);
387}
388CLK_OF_DECLARE(berlin2q_clock, "marvell,berlin2q-chip-ctrl",
389 berlin2q_clock_setup);