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