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
  84struct cp110_gate_clk {
  85        struct clk_hw hw;
  86        struct regmap *regmap;
  87        u8 bit_idx;
  88};
  89
  90#define to_cp110_gate_clk(clk) container_of(clk, struct cp110_gate_clk, hw)
  91
  92static int cp110_gate_enable(struct clk_hw *hw)
  93{
  94        struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
  95
  96        regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
  97                           BIT(gate->bit_idx), BIT(gate->bit_idx));
  98
  99        return 0;
 100}
 101
 102static void cp110_gate_disable(struct clk_hw *hw)
 103{
 104        struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
 105
 106        regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
 107                           BIT(gate->bit_idx), 0);
 108}
 109
 110static int cp110_gate_is_enabled(struct clk_hw *hw)
 111{
 112        struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
 113        u32 val;
 114
 115        regmap_read(gate->regmap, CP110_PM_CLOCK_GATING_REG, &val);
 116
 117        return val & BIT(gate->bit_idx);
 118}
 119
 120static const struct clk_ops cp110_gate_ops = {
 121        .enable = cp110_gate_enable,
 122        .disable = cp110_gate_disable,
 123        .is_enabled = cp110_gate_is_enabled,
 124};
 125
 126static struct clk *cp110_register_gate(const char *name,
 127                                       const char *parent_name,
 128                                       struct regmap *regmap, u8 bit_idx)
 129{
 130        struct cp110_gate_clk *gate;
 131        struct clk *clk;
 132        struct clk_init_data init;
 133
 134        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 135        if (!gate)
 136                return ERR_PTR(-ENOMEM);
 137
 138        memset(&init, 0, sizeof(init));
 139
 140        init.name = name;
 141        init.ops = &cp110_gate_ops;
 142        init.parent_names = &parent_name;
 143        init.num_parents = 1;
 144
 145        gate->regmap = regmap;
 146        gate->bit_idx = bit_idx;
 147        gate->hw.init = &init;
 148
 149        clk = clk_register(NULL, &gate->hw);
 150        if (IS_ERR(clk))
 151                kfree(gate);
 152
 153        return clk;
 154}
 155
 156static void cp110_unregister_gate(struct clk *clk)
 157{
 158        struct clk_hw *hw;
 159
 160        hw = __clk_get_hw(clk);
 161        if (!hw)
 162                return;
 163
 164        clk_unregister(clk);
 165        kfree(to_cp110_gate_clk(hw));
 166}
 167
 168static struct clk *cp110_of_clk_get(struct of_phandle_args *clkspec, void *data)
 169{
 170        struct clk_onecell_data *clk_data = data;
 171        unsigned int type = clkspec->args[0];
 172        unsigned int idx = clkspec->args[1];
 173
 174        if (type == CP110_CLK_TYPE_CORE) {
 175                if (idx > CP110_MAX_CORE_CLOCKS)
 176                        return ERR_PTR(-EINVAL);
 177                return clk_data->clks[idx];
 178        } else if (type == CP110_CLK_TYPE_GATABLE) {
 179                if (idx > CP110_MAX_GATABLE_CLOCKS)
 180                        return ERR_PTR(-EINVAL);
 181                return clk_data->clks[CP110_MAX_CORE_CLOCKS + idx];
 182        }
 183
 184        return ERR_PTR(-EINVAL);
 185}
 186
 187static int cp110_syscon_clk_probe(struct platform_device *pdev)
 188{
 189        struct regmap *regmap;
 190        struct device_node *np = pdev->dev.of_node;
 191        const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name;
 192        struct clk_onecell_data *cp110_clk_data;
 193        struct clk *clk, **cp110_clks;
 194        u32 nand_clk_ctrl;
 195        int i, ret;
 196
 197        regmap = syscon_node_to_regmap(np);
 198        if (IS_ERR(regmap))
 199                return PTR_ERR(regmap);
 200
 201        ret = regmap_read(regmap, CP110_NAND_FLASH_CLK_CTRL_REG,
 202                          &nand_clk_ctrl);
 203        if (ret)
 204                return ret;
 205
 206        cp110_clks = devm_kcalloc(&pdev->dev, sizeof(struct clk *),
 207                                  CP110_CLK_NUM, GFP_KERNEL);
 208        if (!cp110_clks)
 209                return -ENOMEM;
 210
 211        cp110_clk_data = devm_kzalloc(&pdev->dev,
 212                                      sizeof(*cp110_clk_data),
 213                                      GFP_KERNEL);
 214        if (!cp110_clk_data)
 215                return -ENOMEM;
 216
 217        cp110_clk_data->clks = cp110_clks;
 218        cp110_clk_data->clk_num = CP110_CLK_NUM;
 219
 220        /* Register the APLL which is the root of the clk tree */
 221        of_property_read_string_index(np, "core-clock-output-names",
 222                                      CP110_CORE_APLL, &apll_name);
 223        clk = clk_register_fixed_rate(NULL, apll_name, NULL, 0,
 224                                      1000 * 1000 * 1000);
 225        if (IS_ERR(clk)) {
 226                ret = PTR_ERR(clk);
 227                goto fail0;
 228        }
 229
 230        cp110_clks[CP110_CORE_APLL] = clk;
 231
 232        /* PPv2 is APLL/3 */
 233        of_property_read_string_index(np, "core-clock-output-names",
 234                                      CP110_CORE_PPV2, &ppv2_name);
 235        clk = clk_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3);
 236        if (IS_ERR(clk)) {
 237                ret = PTR_ERR(clk);
 238                goto fail1;
 239        }
 240
 241        cp110_clks[CP110_CORE_PPV2] = clk;
 242
 243        /* EIP clock is APLL/2 */
 244        of_property_read_string_index(np, "core-clock-output-names",
 245                                      CP110_CORE_EIP, &eip_name);
 246        clk = clk_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2);
 247        if (IS_ERR(clk)) {
 248                ret = PTR_ERR(clk);
 249                goto fail2;
 250        }
 251
 252        cp110_clks[CP110_CORE_EIP] = clk;
 253
 254        /* Core clock is EIP/2 */
 255        of_property_read_string_index(np, "core-clock-output-names",
 256                                      CP110_CORE_CORE, &core_name);
 257        clk = clk_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2);
 258        if (IS_ERR(clk)) {
 259                ret = PTR_ERR(clk);
 260                goto fail3;
 261        }
 262
 263        cp110_clks[CP110_CORE_CORE] = clk;
 264
 265        /* NAND can be either APLL/2.5 or core clock */
 266        of_property_read_string_index(np, "core-clock-output-names",
 267                                      CP110_CORE_NAND, &nand_name);
 268        if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK)
 269                clk = clk_register_fixed_factor(NULL, nand_name,
 270                                                apll_name, 0, 2, 5);
 271        else
 272                clk = clk_register_fixed_factor(NULL, nand_name,
 273                                                core_name, 0, 1, 1);
 274        if (IS_ERR(clk)) {
 275                ret = PTR_ERR(clk);
 276                goto fail4;
 277        }
 278
 279        cp110_clks[CP110_CORE_NAND] = clk;
 280
 281        for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
 282                const char *parent, *name;
 283                int ret;
 284
 285                ret = of_property_read_string_index(np,
 286                                                    "gate-clock-output-names",
 287                                                    i, &name);
 288                /* Reached the end of the list? */
 289                if (ret < 0)
 290                        break;
 291
 292                if (!strcmp(name, "none"))
 293                        continue;
 294
 295                switch (i) {
 296                case CP110_GATE_AUDIO:
 297                case CP110_GATE_COMM_UNIT:
 298                case CP110_GATE_EIP150:
 299                case CP110_GATE_EIP197:
 300                case CP110_GATE_SLOW_IO:
 301                        of_property_read_string_index(np,
 302                                                      "gate-clock-output-names",
 303                                                      CP110_GATE_MAIN, &parent);
 304                        break;
 305                case CP110_GATE_NAND:
 306                        parent = nand_name;
 307                        break;
 308                case CP110_GATE_PPV2:
 309                        parent = ppv2_name;
 310                        break;
 311                case CP110_GATE_SDIO:
 312                        of_property_read_string_index(np,
 313                                                      "gate-clock-output-names",
 314                                                      CP110_GATE_SDMMC, &parent);
 315                        break;
 316                case CP110_GATE_XOR1:
 317                case CP110_GATE_XOR0:
 318                case CP110_GATE_PCIE_X1_0:
 319                case CP110_GATE_PCIE_X1_1:
 320                case CP110_GATE_PCIE_X4:
 321                        of_property_read_string_index(np,
 322                                                      "gate-clock-output-names",
 323                                                      CP110_GATE_PCIE_XOR, &parent);
 324                        break;
 325                case CP110_GATE_SATA:
 326                case CP110_GATE_USB3H0:
 327                case CP110_GATE_USB3H1:
 328                case CP110_GATE_USB3DEV:
 329                        of_property_read_string_index(np,
 330                                                      "gate-clock-output-names",
 331                                                      CP110_GATE_SATA_USB, &parent);
 332                        break;
 333                default:
 334                        parent = core_name;
 335                        break;
 336                }
 337
 338                clk = cp110_register_gate(name, parent, regmap, i);
 339                if (IS_ERR(clk)) {
 340                        ret = PTR_ERR(clk);
 341                        goto fail_gate;
 342                }
 343
 344                cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk;
 345        }
 346
 347        ret = of_clk_add_provider(np, cp110_of_clk_get, cp110_clk_data);
 348        if (ret)
 349                goto fail_clk_add;
 350
 351        platform_set_drvdata(pdev, cp110_clks);
 352
 353        return 0;
 354
 355fail_clk_add:
 356fail_gate:
 357        for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
 358                clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
 359
 360                if (clk)
 361                        cp110_unregister_gate(clk);
 362        }
 363
 364        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
 365fail4:
 366        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
 367fail3:
 368        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
 369fail2:
 370        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
 371fail1:
 372        clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
 373fail0:
 374        return ret;
 375}
 376
 377static int cp110_syscon_clk_remove(struct platform_device *pdev)
 378{
 379        struct clk **cp110_clks = platform_get_drvdata(pdev);
 380        int i;
 381
 382        of_clk_del_provider(pdev->dev.of_node);
 383
 384        for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
 385                struct clk *clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
 386
 387                if (clk)
 388                        cp110_unregister_gate(clk);
 389        }
 390
 391        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
 392        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
 393        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
 394        clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
 395        clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
 396
 397        return 0;
 398}
 399
 400static const struct of_device_id cp110_syscon_of_match[] = {
 401        { .compatible = "marvell,cp110-system-controller0", },
 402        { }
 403};
 404MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match);
 405
 406static struct platform_driver cp110_syscon_driver = {
 407        .probe = cp110_syscon_clk_probe,
 408        .remove = cp110_syscon_clk_remove,
 409        .driver         = {
 410                .name   = "marvell-cp110-system-controller0",
 411                .of_match_table = cp110_syscon_of_match,
 412        },
 413};
 414
 415module_platform_driver(cp110_syscon_driver);
 416
 417MODULE_DESCRIPTION("Marvell CP110 System Controller 0 driver");
 418MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 419MODULE_LICENSE("GPL");
 420