linux/drivers/clk/berlin/bg2q.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   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             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
  39static 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, SYSPLL
  75};
  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                return;
 291        clk_data->num = MAX_CLKS;
 292        hws = clk_data->hws;
 293
 294        gbase = of_iomap(parent_np, 0);
 295        if (!gbase) {
 296                pr_err("%pOF: Unable to map global base\n", np);
 297                return;
 298        }
 299
 300        /* BG2Q CPU PLL is not part of global registers */
 301        cpupll_base = of_iomap(parent_np, 1);
 302        if (!cpupll_base) {
 303                pr_err("%pOF: Unable to map cpupll base\n", np);
 304                iounmap(gbase);
 305                return;
 306        }
 307
 308        /* overwrite default clock names with DT provided ones */
 309        clk = of_clk_get_by_name(np, clk_names[REFCLK]);
 310        if (!IS_ERR(clk)) {
 311                clk_names[REFCLK] = __clk_get_name(clk);
 312                clk_put(clk);
 313        }
 314
 315        /* simple register PLLs */
 316        ret = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
 317                                   clk_names[SYSPLL], clk_names[REFCLK], 0);
 318        if (ret)
 319                goto bg2q_fail;
 320
 321        ret = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
 322                                   clk_names[CPUPLL], clk_names[REFCLK], 0);
 323        if (ret)
 324                goto bg2q_fail;
 325
 326        /* TODO: add BG2Q AVPLL */
 327
 328        /*
 329         * TODO: add reference clock bypass switches:
 330         * memPLLSWBypass, cpuPLLSWBypass, and sysPLLSWBypass
 331         */
 332
 333        /* clock divider cells */
 334        for (n = 0; n < ARRAY_SIZE(bg2q_divs); n++) {
 335                const struct berlin2_div_data *dd = &bg2q_divs[n];
 336                int k;
 337
 338                for (k = 0; k < dd->num_parents; k++)
 339                        parent_names[k] = clk_names[dd->parent_ids[k]];
 340
 341                hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
 342                                dd->name, dd->div_flags, parent_names,
 343                                dd->num_parents, dd->flags, &lock);
 344        }
 345
 346        /* clock gate cells */
 347        for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) {
 348                const struct berlin2_gate_data *gd = &bg2q_gates[n];
 349
 350                hws[CLKID_GFX2DAXI + n] = clk_hw_register_gate(NULL, gd->name,
 351                            gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
 352                            gd->bit_idx, 0, &lock);
 353        }
 354
 355        /* cpuclk divider is fixed to 1 */
 356        hws[CLKID_CPU] =
 357                clk_hw_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL],
 358                                          0, 1, 1);
 359        /* twdclk is derived from cpu/3 */
 360        hws[CLKID_TWD] =
 361                clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
 362
 363        /* check for errors on leaf clocks */
 364        for (n = 0; n < MAX_CLKS; n++) {
 365                if (!IS_ERR(hws[n]))
 366                        continue;
 367
 368                pr_err("%pOF: Unable to register leaf clock %d\n", np, n);
 369                goto bg2q_fail;
 370        }
 371
 372        /* register clk-provider */
 373        of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
 374
 375        return;
 376
 377bg2q_fail:
 378        iounmap(cpupll_base);
 379        iounmap(gbase);
 380}
 381CLK_OF_DECLARE(berlin2q_clk, "marvell,berlin2q-clk",
 382               berlin2q_clock_setup);
 383