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 28 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 }, 287 { "nfc", "perif", 18 }, 288 { "pcie", "perif", 22 }, 289}; 290 291static void __init berlin2q_clock_setup(struct device_node *np) 292{ 293 struct device_node *parent_np = of_get_parent(np); 294 const char *parent_names[9]; 295 struct clk *clk; 296 int n; 297 298 gbase = of_iomap(parent_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(parent_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 /* cpuclk divider is fixed to 1 */ 360 clks[CLKID_CPU] = 361 clk_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL], 362 0, 1, 1); 363 /* twdclk is derived from cpu/3 */ 364 clks[CLKID_TWD] = 365 clk_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(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_clk, "marvell,berlin2q-clk", 389 berlin2q_clock_setup); 390