clk: shmobile: add CPG driver for rz-platforms

Signed-off-by: Wolfram Sang <wsa@sang-engineering.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
This commit is contained in:
Wolfram Sang 2014-03-07 17:00:37 +01:00 committed by Mike Turquette
parent f736386160
commit a665962e8f
3 changed files with 133 additions and 0 deletions

View File

@ -0,0 +1,29 @@
* Renesas RZ Clock Pulse Generator (CPG)
The CPG generates core clocks for the RZ SoCs. It includes the PLL, variable
CPU and GPU clocks, and several fixed ratio dividers.
Required Properties:
- compatible: Must be one of
- "renesas,r7s72100-cpg-clocks" for the r7s72100 CPG
- "renesas,rz-cpg-clocks" for the generic RZ CPG
- reg: Base address and length of the memory resource used by the CPG
- clocks: References to possible parent clocks. Order must match clock modes
in the datasheet. For the r7s72100, this is extal, usb_x1.
- #clock-cells: Must be 1
- clock-output-names: The names of the clocks. Supported clocks are "pll",
"i", and "g"
Example
-------
cpg_clocks: cpg_clocks@fcfe0000 {
#clock-cells = <1>;
compatible = "renesas,r7s72100-cpg-clocks",
"renesas,rz-cpg-clocks";
reg = <0xfcfe0000 0x18>;
clocks = <&extal_clk>, <&usb_x1_clk>;
clock-output-names = "pll", "i", "g";
};

View File

@ -1,4 +1,5 @@
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o

View File

@ -0,0 +1,103 @@
/*
* rz Core CPG Clocks
*
* Copyright (C) 2013 Ideas On Board SPRL
* Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*/
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
struct rz_cpg {
struct clk_onecell_data data;
void __iomem *reg;
};
#define CPG_FRQCR 0x10
#define CPG_FRQCR2 0x14
/* -----------------------------------------------------------------------------
* Initialization
*/
static struct clk * __init
rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
{
u32 val;
unsigned mult;
static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
if (strcmp(name, "pll") == 0) {
/* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */
unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */
const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
mult = cpg_mode ? (32 / 4) : 30;
return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1);
}
/* If mapping regs failed, skip non-pll clocks. System will boot anyhow */
if (!cpg->reg)
return ERR_PTR(-ENXIO);
/* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3)
* and the constraint that always g <= i. To get the rz platform started,
* let them run at fixed current speed and implement the details later.
*/
if (strcmp(name, "i") == 0)
val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
else if (strcmp(name, "g") == 0)
val = clk_readl(cpg->reg + CPG_FRQCR2) & 3;
else
return ERR_PTR(-EINVAL);
mult = frqcr_tab[val];
return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3);
}
static void __init rz_cpg_clocks_init(struct device_node *np)
{
struct rz_cpg *cpg;
struct clk **clks;
unsigned i;
int num_clks;
num_clks = of_property_count_strings(np, "clock-output-names");
if (WARN(num_clks <= 0, "can't count CPG clocks\n"))
return;
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
BUG_ON(!cpg || !clks);
cpg->data.clks = clks;
cpg->data.clk_num = num_clks;
cpg->reg = of_iomap(np, 0);
for (i = 0; i < num_clks; ++i) {
const char *name;
struct clk *clk;
of_property_read_string_index(np, "clock-output-names", i, &name);
clk = rz_cpg_register_clock(np, cpg, name);
if (IS_ERR(clk))
pr_err("%s: failed to register %s %s clock (%ld)\n",
__func__, np->name, name, PTR_ERR(clk));
else
cpg->data.clks[i] = clk;
}
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
}
CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init);