linux/drivers/clk/renesas/clk-rz.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * RZ/A1 Core CPG Clocks
   4 *
   5 * Copyright (C) 2013 Ideas On Board SPRL
   6 * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
   7 */
   8
   9#include <linux/clk-provider.h>
  10#include <linux/clk/renesas.h>
  11#include <linux/init.h>
  12#include <linux/io.h>
  13#include <linux/kernel.h>
  14#include <linux/of.h>
  15#include <linux/of_address.h>
  16#include <linux/slab.h>
  17
  18struct rz_cpg {
  19        struct clk_onecell_data data;
  20        void __iomem *reg;
  21};
  22
  23#define CPG_FRQCR       0x10
  24#define CPG_FRQCR2      0x14
  25
  26#define PPR0            0xFCFE3200
  27#define PIBC0           0xFCFE7000
  28
  29#define MD_CLK(x)       ((x >> 2) & 1)  /* P0_2 */
  30
  31/* -----------------------------------------------------------------------------
  32 * Initialization
  33 */
  34
  35static u16 __init rz_cpg_read_mode_pins(void)
  36{
  37        void __iomem *ppr0, *pibc0;
  38        u16 modes;
  39
  40        ppr0 = ioremap(PPR0, 2);
  41        pibc0 = ioremap(PIBC0, 2);
  42        BUG_ON(!ppr0 || !pibc0);
  43        iowrite16(4, pibc0);    /* enable input buffer */
  44        modes = ioread16(ppr0);
  45        iounmap(ppr0);
  46        iounmap(pibc0);
  47
  48        return modes;
  49}
  50
  51static struct clk * __init
  52rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
  53{
  54        u32 val;
  55        unsigned mult;
  56        static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
  57
  58        if (strcmp(name, "pll") == 0) {
  59                unsigned int cpg_mode = MD_CLK(rz_cpg_read_mode_pins());
  60                const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
  61
  62                mult = cpg_mode ? (32 / 4) : 30;
  63
  64                return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1);
  65        }
  66
  67        /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */
  68        if (!cpg->reg)
  69                return ERR_PTR(-ENXIO);
  70
  71        /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3)
  72         * and the constraint that always g <= i. To get the rz platform started,
  73         * let them run at fixed current speed and implement the details later.
  74         */
  75        if (strcmp(name, "i") == 0)
  76                val = (readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
  77        else if (strcmp(name, "g") == 0)
  78                val = readl(cpg->reg + CPG_FRQCR2) & 3;
  79        else
  80                return ERR_PTR(-EINVAL);
  81
  82        mult = frqcr_tab[val];
  83        return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3);
  84}
  85
  86static void __init rz_cpg_clocks_init(struct device_node *np)
  87{
  88        struct rz_cpg *cpg;
  89        struct clk **clks;
  90        unsigned i;
  91        int num_clks;
  92
  93        num_clks = of_property_count_strings(np, "clock-output-names");
  94        if (WARN(num_clks <= 0, "can't count CPG clocks\n"))
  95                return;
  96
  97        cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
  98        clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL);
  99        BUG_ON(!cpg || !clks);
 100
 101        cpg->data.clks = clks;
 102        cpg->data.clk_num = num_clks;
 103
 104        cpg->reg = of_iomap(np, 0);
 105
 106        for (i = 0; i < num_clks; ++i) {
 107                const char *name;
 108                struct clk *clk;
 109
 110                of_property_read_string_index(np, "clock-output-names", i, &name);
 111
 112                clk = rz_cpg_register_clock(np, cpg, name);
 113                if (IS_ERR(clk))
 114                        pr_err("%s: failed to register %pOFn %s clock (%ld)\n",
 115                               __func__, np, name, PTR_ERR(clk));
 116                else
 117                        cpg->data.clks[i] = clk;
 118        }
 119
 120        of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
 121
 122        cpg_mstp_add_clk_domain(np);
 123}
 124CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init);
 125