linux/drivers/clk/mvebu/cp110-system-controller.c
<<
>>
Prefs
   1/*
   2 * Marvell Armada CP110 System Controller
   3 *
   4 * Copyright (C) 2016 Marvell
   5 *
   6 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
   7 *
   8 * This file is licensed under the terms of the GNU General Public
   9 * License version 2.  This program is licensed "as is" without any
  10 * warranty of any kind, whether express or implied.
  11 */
  12
  13/*
  14 * CP110 has 5 core clocks:
  15 *
  16 *  - APLL              (1 Ghz)
  17 *    - PPv2 core       (1/3 APLL)
  18 *    - EIP             (1/2 APLL)
  19 *      - Core          (1/2 EIP)
  20 *
  21 *  - NAND clock, which is either:
  22 *    - Equal to the core clock
  23 *    - 2/5 APLL
  24 *
  25 * CP110 has 32 gatable clocks, for the various peripherals in the
  26 * IP. They have fairly complicated parent/child relationships.
  27 */
  28
  29#define pr_fmt(fmt) "cp110-system-controller: " fmt
  30
  31#include <linux/clk-provider.h>
  32#include <linux/mfd/syscon.h>
  33#include <linux/module.h>
  34#include <linux/of.h>
  35#include <linux/of_address.h>
  36#include <linux/platform_device.h>
  37#include <linux/regmap.h>
  38#include <linux/slab.h>
  39
  40#define CP110_PM_CLOCK_GATING_REG       0x220
  41#define CP110_NAND_FLASH_CLK_CTRL_REG   0x700
  42#define    NF_CLOCK_SEL_400_MASK        BIT(0)
  43
  44enum {
  45        CP110_CLK_TYPE_CORE,
  46        CP110_CLK_TYPE_GATABLE,
  47};
  48
  49#define CP110_MAX_CORE_CLOCKS           5
  50#define CP110_MAX_GATABLE_CLOCKS        32
  51
  52#define CP110_CLK_NUM \
  53        (CP110_MAX_CORE_CLOCKS + CP110_MAX_GATABLE_CLOCKS)
  54
  55#define CP110_CORE_APLL                 0
  56#define CP110_CORE_PPV2                 1
  57#define CP110_CORE_EIP                  2
  58#define CP110_CORE_CORE                 3
  59#define CP110_CORE_NAND                 4
  60
  61/* A number of gatable clocks need special handling */
  62#define CP110_GATE_AUDIO                0
  63#define CP110_GATE_COMM_UNIT            1
  64#define CP110_GATE_NAND                 2
  65#define CP110_GATE_PPV2                 3
  66#define CP110_GATE_SDIO                 4
  67#define CP110_GATE_XOR1                 7
  68#define CP110_GATE_XOR0                 8
  69#define CP110_GATE_PCIE_X1_0            11
  70#define CP110_GATE_PCIE_X1_1            12
  71#define CP110_GATE_PCIE_X4              13
  72#define CP110_GATE_PCIE_XOR             14
  73#define CP110_GATE_SATA                 15
  74#define CP110_GATE_SATA_USB             16
  75#define CP110_GATE_MAIN                 17
  76#define CP110_GATE_SDMMC                18
  77#define CP110_GATE_SLOW_IO              21
  78#define CP110_GATE_USB3H0               22
  79#define CP110_GATE_USB3H1               23
  80#define CP110_GATE_USB3DEV              24
  81#define CP110_GATE_EIP150               25
  82#define CP110_GATE_EIP197               26
  83
  84static struct clk *cp110_clks[CP110_CLK_NUM];
  85
  86static struct clk_onecell_data cp110_clk_data = {
  87        .clks = cp110_clks,
  88        .clk_num = CP110_CLK_NUM,
  89};
  90
  91struct cp110_gate_clk {
  92        struct clk_hw hw;
  93        struct regmap *regmap;
  94        u8 bit_idx;
  95};
  96
  97#define to_cp110_gate_clk(clk) container_of(clk, struct cp110_gate_clk, hw)
  98
  99static int cp110_gate_enable(struct clk_hw *hw)
 100{
 101        struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
 102
 103        regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
 104                           BIT(gate->bit_idx), BIT(gate->bit_idx));
 105
 106        return 0;
 107}
 108
 109static void cp110_gate_disable(struct clk_hw *hw)
 110{
 111        struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
 112
 113        regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
 114                           BIT(gate->bit_idx), 0);
 115}
 116
 117static int cp110_gate_is_enabled(struct clk_hw *hw)
 118{
 119        struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
 120        u32 val;
 121
 122        regmap_read(gate->regmap, CP110_PM_CLOCK_GATING_REG, &val);
 123
 124        return val & BIT(gate->bit_idx);
 125}
 126
 127static const struct clk_ops cp110_gate_ops = {
 128        .enable = cp110_gate_enable,
 129        .disable = cp110_gate_disable,
 130        .is_enabled = cp110_gate_is_enabled,
 131};
 132
 133static struct clk *cp110_register_gate(const char *name,
 134                                       const char *parent_name,
 135                                       struct regmap *regmap, u8 bit_idx)
 136{
 137        struct cp110_gate_clk *gate;
 138        struct clk *clk;
 139        struct clk_init_data init;
 140
 141        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 142        if (!gate)
 143                return ERR_PTR(-ENOMEM);
 144
 145        init.name = name;
 146        init.ops = &cp110_gate_ops;
 147        init.parent_names = &parent_name;
 148        init.num_parents = 1;
 149
 150        gate->regmap = regmap;
 151        gate->bit_idx = bit_idx;
 152        gate->hw.init = &init;
 153
 154        clk = clk_register(NULL, &gate->hw);
 155        if (IS_ERR(clk))
 156                kfree(gate);
 157
 158        return clk;
 159}
 160
 161static void cp110_unregister_gate(struct clk *clk)
 162{
 163        struct clk_hw *hw;
 164
 165        hw = __clk_get_hw(clk);
 166        if (!hw)
 167                return;
 168
 169        clk_unregister(clk);
 170        kfree(to_cp110_gate_clk(hw));
 171}
 172
 173static struct clk *cp110_of_clk_get(struct of_phandle_args *clkspec, void *data)
 174{
 175        struct clk_onecell_data *clk_data = data;
 176        unsigned int type = clkspec->args[0];
 177        unsigned int idx = clkspec->args[1];
 178
 179        if (type == CP110_CLK_TYPE_CORE) {
 180                if (idx > CP110_MAX_CORE_CLOCKS)
 181                        return ERR_PTR(-EINVAL);
 182                return clk_data->clks[idx];
 183        } else if (type == CP110_CLK_TYPE_GATABLE) {
 184                if (idx > CP110_MAX_GATABLE_CLOCKS)
 185                        return ERR_PTR(-EINVAL);
 186                return clk_data->clks[CP110_MAX_CORE_CLOCKS + idx];
 187        }
 188
 189        return ERR_PTR(-EINVAL);
 190}
 191
 192static int cp110_syscon_clk_probe(struct platform_device *pdev)
 193{
 194        struct regmap *regmap;
 195        struct device_node *np = pdev->dev.of_node;
 196        const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name;
 197        struct clk *clk;
 198        u32 nand_clk_ctrl;
 199        int i, ret;
 200
 201        regmap = syscon_node_to_regmap(np);
 202        if (IS_ERR(regmap))
 203                return PTR_ERR(regmap);
 204
 205        ret = regmap_read(regmap, CP110_NAND_FLASH_CLK_CTRL_REG,
 206                          &nand_clk_ctrl);
 207        if (ret)
 208                return ret;
 209
 210        /* Register the APLL which is the root of the clk tree */
 211        of_property_read_string_index(np, "core-clock-output-names",
 212                                      CP110_CORE_APLL, &apll_name);
 213        clk = clk_register_fixed_rate(NULL, apll_name, NULL, 0,
 214                                      1000 * 1000 * 1000);
 215        if (IS_ERR(clk)) {
 216                ret = PTR_ERR(clk);
 217                goto fail0;
 218        }
 219
 220        cp110_clks[CP110_CORE_APLL] = clk;
 221
 222        /* PPv2 is APLL/3 */
 223        of_property_read_string_index(np, "core-clock-output-names",
 224                                      CP110_CORE_PPV2, &ppv2_name);
 225        clk = clk_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3);
 226        if (IS_ERR(clk)) {
 227                ret = PTR_ERR(clk);
 228                goto fail1;
 229        }
 230
 231        cp110_clks[CP110_CORE_PPV2] = clk;
 232
 233        /* EIP clock is APLL/2 */
 234        of_property_read_string_index(np, "core-clock-output-names",
 235                                      CP110_CORE_EIP, &eip_name);
 236        clk = clk_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2);
 237        if (IS_ERR(clk)) {
 238                ret = PTR_ERR(clk);
 239                goto fail2;
 240        }
 241
 242        cp110_clks[CP110_CORE_EIP] = clk;
 243
 244        /* Core clock is EIP/2 */
 245        of_property_read_string_index(np, "core-clock-output-names",
 246                                      CP110_CORE_CORE, &core_name);
 247        clk = clk_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2);
 248        if (IS_ERR(clk)) {
 249                ret = PTR_ERR(clk);
 250                goto fail3;
 251        }
 252
 253        cp110_clks[CP110_CORE_CORE] = clk;
 254
 255        /* NAND can be either APLL/2.5 or core clock */
 256        of_property_read_string_index(np, "core-clock-output-names",
 257                                      CP110_CORE_NAND, &nand_name);
 258        if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK)
 259                clk = clk_register_fixed_factor(NULL, nand_name,
 260                                                apll_name, 0, 2, 5);
 261        else
 262                clk = clk_register_fixed_factor(NULL, nand_name,
 263                                                core_name, 0, 1, 1);
 264        if (IS_ERR(clk)) {
 265                ret = PTR_ERR(clk);
 266                goto fail4;
 267        }
 268
 269        cp110_clks[CP110_CORE_NAND] = clk;
 270
 271        for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
 272                const char *parent, *name;
 273                int ret;
 274
 275                ret = of_property_read_string_index(np,
 276                                                    "gate-clock-output-names",
 277                                                    i, &name);
 278                /* Reached the end of the list? */
 279                if (ret < 0)
 280                        break;
 281
 282                if (!strcmp(name, "none"))
 283                        continue;
 284
 285                switch (i) {
 286                case CP110_GATE_AUDIO:
 287                case CP110_GATE_COMM_UNIT:
 288                case CP110_GATE_EIP150:
 289                case CP110_GATE_EIP197:
 290                case CP110_GATE_SLOW_IO:
 291                        of_property_read_string_index(np,
 292                                                      "gate-clock-output-names",
 293                                                      CP110_GATE_MAIN, &parent);
 294                        break;
 295                case CP110_GATE_NAND:
 296                        parent = nand_name;
 297                        break;
 298                case CP110_GATE_PPV2:
 299                        parent = ppv2_name;
 300                        break;
 301                case CP110_GATE_SDIO:
 302                        of_property_read_string_index(np,
 303                                                      "gate-clock-output-names",
 304                                                      CP110_GATE_SDMMC, &parent);
 305                        break;
 306                case CP110_GATE_XOR1:
 307                case CP110_GATE_XOR0:
 308                case CP110_GATE_PCIE_X1_0:
 309                case CP110_GATE_PCIE_X1_1:
 310                case CP110_GATE_PCIE_X4:
 311                        of_property_read_string_index(np,
 312                                                      "gate-clock-output-names",
 313                                                      CP110_GATE_PCIE_XOR, &parent);
 314                        break;
 315                case CP110_GATE_SATA:
 316                case CP110_GATE_USB3H0:
 317                case CP110_GATE_USB3H1:
 318                case CP110_GATE_USB3DEV:
 319                        of_property_read_string_index(np,
 320                                                      "gate-clock-output-names",
 321                                                      CP110_GATE_SATA_USB, &parent);
 322                        break;
 323                default:
 324                        parent = core_name;
 325                        break;
 326                }
 327
 328                clk = cp110_register_gate(name, parent, regmap, i);
 329                if (IS_ERR(clk)) {
 330                        ret = PTR_ERR(clk);
 331                        goto fail_gate;
 332                }
 333
 334                cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk;
 335        }
 336
 337        ret = of_clk_add_provider(np, cp110_of_clk_get, &cp110_clk_data);
 338        if (ret)
 339                goto fail_clk_add;
 340
 341        return 0;
 342
 343fail_clk_add:
 344fail_gate:
 345        for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
 346                clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
 347
 348                if (clk)
 349                        cp110_unregister_gate(clk);
 350        }
 351
 352        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
 353fail4:
 354        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
 355fail3:
 356        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
 357fail2:
 358        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
 359fail1:
 360        clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
 361fail0:
 362        return ret;
 363}
 364
 365static int cp110_syscon_clk_remove(struct platform_device *pdev)
 366{
 367        int i;
 368
 369        of_clk_del_provider(pdev->dev.of_node);
 370
 371        for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
 372                struct clk *clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
 373
 374                if (clk)
 375                        cp110_unregister_gate(clk);
 376        }
 377
 378        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
 379        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
 380        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
 381        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
 382        clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
 383
 384        return 0;
 385}
 386
 387static const struct of_device_id cp110_syscon_of_match[] = {
 388        { .compatible = "marvell,cp110-system-controller0", },
 389        { }
 390};
 391MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match);
 392
 393static struct platform_driver cp110_syscon_driver = {
 394        .probe = cp110_syscon_clk_probe,
 395        .remove = cp110_syscon_clk_remove,
 396        .driver         = {
 397                .name   = "marvell-cp110-system-controller0",
 398                .of_match_table = cp110_syscon_of_match,
 399        },
 400};
 401
 402module_platform_driver(cp110_syscon_driver);
 403
 404MODULE_DESCRIPTION("Marvell CP110 System Controller 0 driver");
 405MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 406MODULE_LICENSE("GPL");
 407