387 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2014 Marvell Technology Group Ltd.4 *5 * Alexandre Belloni <alexandre.belloni@free-electrons.com>6 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>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 0x001824#define REG_PINMUX5 0x002c25#define REG_SYSPLLCTL0 0x003026#define REG_SYSPLLCTL4 0x004027#define REG_CLKENABLE 0x00e828#define REG_CLKSELECT0 0x00ec29#define REG_CLKSELECT1 0x00f030#define REG_CLKSELECT2 0x00f431#define REG_CLKSWITCH0 0x00f832#define REG_CLKSWITCH1 0x00fc33#define REG_SW_GENERIC0 0x011034#define REG_SW_GENERIC3 0x011c35#define REG_SDIO0XIN_CLKCTL 0x015836#define REG_SDIO1XIN_CLKCTL 0x015c37 38#define MAX_CLKS 2839static struct clk_hw_onecell_data *clk_data;40static DEFINE_SPINLOCK(lock);41static void __iomem *gbase;42static void __iomem *cpupll_base;43 44enum {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 51static 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 65static 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 73static const u8 default_parent_ids[] = {74 SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL75};76 77static 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 265static 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 280static 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 sysPLLSWBypass335 */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 381bg2q_fail:382 iounmap(cpupll_base);383 iounmap(gbase);384}385CLK_OF_DECLARE(berlin2q_clk, "marvell,berlin2q-clk",386 berlin2q_clock_setup);387