aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2013-10-17 17:54:07 -0400
committerMike Turquette <mturquette@linaro.org>2013-12-12 22:23:58 -0500
commitabe844aa5bb50444ac3e02aed89b431823d6ad56 (patch)
tree4ac49f2cbbb3f16e0714f39a8447874e108a9180
parent10cdfe9f327ab8d120cf6957e58c6203e3a53847 (diff)
clk: shmobile: Add DIV6 clock support
DIV6 clocks are divider gate clocks controlled through a single register. The divider is expressed on 6 bits, hence the name, and can take values from 1/1 to 1/64. Those clocks are found on Renesas ARM SoCs. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt28
-rw-r--r--drivers/clk/shmobile/Makefile1
-rw-r--r--drivers/clk/shmobile/clk-div6.c185
3 files changed, 214 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt
new file mode 100644
index 000000000000..952e373178d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt
@@ -0,0 +1,28 @@
1* Renesas CPG DIV6 Clock
2
3The CPG DIV6 clocks are variable factor clocks provided by the Clock Pulse
4Generator (CPG). They clock input is divided by a configurable factor from 1
5to 64.
6
7Required Properties:
8
9 - compatible: Must be one of the following
10 - "renesas,r8a7790-div6-clock" for R8A7790 (R-Car H2) DIV6 clocks
11 - "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2) DIV6 clocks
12 - "renesas,cpg-div6-clock" for generic DIV6 clocks
13 - reg: Base address and length of the memory resource used by the DIV6 clock
14 - clocks: Reference to the parent clock
15 - #clock-cells: Must be 0
16 - clock-output-names: The name of the clock as a free-form string
17
18
19Example
20-------
21
22 sd2_clk: sd2_clk@e6150078 {
23 compatible = "renesas,r8a7790-div6-clock", "renesas,cpg-div6-clock";
24 reg = <0 0xe6150078 0 4>;
25 clocks = <&pll1_div2_clk>;
26 #clock-cells = <0>;
27 clock-output-names = "sd2";
28 };
diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile
index d0a9034a7946..2e4a1197aa0a 100644
--- a/drivers/clk/shmobile/Makefile
+++ b/drivers/clk/shmobile/Makefile
@@ -1,5 +1,6 @@
1obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o 1obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
2obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o 2obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
3obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
3 4
4# for emply built-in.o 5# for emply built-in.o
5obj-n := dummy 6obj-n := dummy
diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c
new file mode 100644
index 000000000000..aac4756ec52e
--- /dev/null
+++ b/drivers/clk/shmobile/clk-div6.c
@@ -0,0 +1,185 @@
1/*
2 * r8a7790 Common Clock Framework support
3 *
4 * Copyright (C) 2013 Renesas Solutions Corp.
5 *
6 * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 */
12
13#include <linux/clk-provider.h>
14#include <linux/clkdev.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/kernel.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20
21#define CPG_DIV6_CKSTP BIT(8)
22#define CPG_DIV6_DIV(d) ((d) & 0x3f)
23#define CPG_DIV6_DIV_MASK 0x3f
24
25/**
26 * struct div6_clock - MSTP gating clock
27 * @hw: handle between common and hardware-specific interfaces
28 * @reg: IO-remapped register
29 * @div: divisor value (1-64)
30 */
31struct div6_clock {
32 struct clk_hw hw;
33 void __iomem *reg;
34 unsigned int div;
35};
36
37#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
38
39static int cpg_div6_clock_enable(struct clk_hw *hw)
40{
41 struct div6_clock *clock = to_div6_clock(hw);
42
43 clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
44
45 return 0;
46}
47
48static void cpg_div6_clock_disable(struct clk_hw *hw)
49{
50 struct div6_clock *clock = to_div6_clock(hw);
51
52 /* DIV6 clocks require the divisor field to be non-zero when stopping
53 * the clock.
54 */
55 clk_writel(CPG_DIV6_CKSTP | CPG_DIV6_DIV(CPG_DIV6_DIV_MASK),
56 clock->reg);
57}
58
59static int cpg_div6_clock_is_enabled(struct clk_hw *hw)
60{
61 struct div6_clock *clock = to_div6_clock(hw);
62
63 return !(clk_readl(clock->reg) & CPG_DIV6_CKSTP);
64}
65
66static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw *hw,
67 unsigned long parent_rate)
68{
69 struct div6_clock *clock = to_div6_clock(hw);
70 unsigned int div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
71
72 return parent_rate / div;
73}
74
75static unsigned int cpg_div6_clock_calc_div(unsigned long rate,
76 unsigned long parent_rate)
77{
78 unsigned int div;
79
80 div = DIV_ROUND_CLOSEST(parent_rate, rate);
81 return clamp_t(unsigned int, div, 1, 64);
82}
83
84static long cpg_div6_clock_round_rate(struct clk_hw *hw, unsigned long rate,
85 unsigned long *parent_rate)
86{
87 unsigned int div = cpg_div6_clock_calc_div(rate, *parent_rate);
88
89 return *parent_rate / div;
90}
91
92static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate,
93 unsigned long parent_rate)
94{
95 struct div6_clock *clock = to_div6_clock(hw);
96 unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate);
97
98 clock->div = div;
99
100 /* Only program the new divisor if the clock isn't stopped. */
101 if (!(clk_readl(clock->reg) & CPG_DIV6_CKSTP))
102 clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
103
104 return 0;
105}
106
107static const struct clk_ops cpg_div6_clock_ops = {
108 .enable = cpg_div6_clock_enable,
109 .disable = cpg_div6_clock_disable,
110 .is_enabled = cpg_div6_clock_is_enabled,
111 .recalc_rate = cpg_div6_clock_recalc_rate,
112 .round_rate = cpg_div6_clock_round_rate,
113 .set_rate = cpg_div6_clock_set_rate,
114};
115
116static void __init cpg_div6_clock_init(struct device_node *np)
117{
118 struct clk_init_data init;
119 struct div6_clock *clock;
120 const char *parent_name;
121 const char *name;
122 struct clk *clk;
123 int ret;
124
125 clock = kzalloc(sizeof(*clock), GFP_KERNEL);
126 if (!clock) {
127 pr_err("%s: failed to allocate %s DIV6 clock\n",
128 __func__, np->name);
129 return;
130 }
131
132 /* Remap the clock register and read the divisor. Disabling the
133 * clock overwrites the divisor, so we need to cache its value for the
134 * enable operation.
135 */
136 clock->reg = of_iomap(np, 0);
137 if (clock->reg == NULL) {
138 pr_err("%s: failed to map %s DIV6 clock register\n",
139 __func__, np->name);
140 goto error;
141 }
142
143 clock->div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
144
145 /* Parse the DT properties. */
146 ret = of_property_read_string(np, "clock-output-names", &name);
147 if (ret < 0) {
148 pr_err("%s: failed to get %s DIV6 clock output name\n",
149 __func__, np->name);
150 goto error;
151 }
152
153 parent_name = of_clk_get_parent_name(np, 0);
154 if (parent_name == NULL) {
155 pr_err("%s: failed to get %s DIV6 clock parent name\n",
156 __func__, np->name);
157 goto error;
158 }
159
160 /* Register the clock. */
161 init.name = name;
162 init.ops = &cpg_div6_clock_ops;
163 init.flags = CLK_IS_BASIC;
164 init.parent_names = &parent_name;
165 init.num_parents = 1;
166
167 clock->hw.init = &init;
168
169 clk = clk_register(NULL, &clock->hw);
170 if (IS_ERR(clk)) {
171 pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
172 __func__, np->name, PTR_ERR(clk));
173 goto error;
174 }
175
176 of_clk_add_provider(np, of_clk_src_simple_get, clk);
177
178 return;
179
180error:
181 if (clock->reg)
182 iounmap(clock->reg);
183 kfree(clock);
184}
185CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init);