aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeepak Sikri <deepak.sikri@st.com>2012-11-27 08:05:26 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2012-11-27 08:05:26 -0500
commit420993221175a45db5af012c53fd2fef4d9533dc (patch)
treed860d8781144eb6d8cd18ace65be8b432691b10c
parenta0e5af3cb89b59aa6c62b1f97c8d553ff3fb51c1 (diff)
cpufreq: SPEAr: Add CPUFreq driver
SPEAr is an ARM based family of SoCs. This patch adds in support of cpufreq driver for SPEAr SoCs. It is supported via DT only and so bindings are present in binding document. Signed-off-by: Deepak Sikri <deepak.sikri@st.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt42
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--drivers/cpufreq/Kconfig.arm7
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/spear-cpufreq.c291
5 files changed, 342 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt
new file mode 100644
index 000000000000..f3d44984d91c
--- /dev/null
+++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt
@@ -0,0 +1,42 @@
1SPEAr cpufreq driver
2-------------------
3
4SPEAr SoC cpufreq driver for CPU frequency scaling.
5It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) systems
6which share clock across all CPUs.
7
8Required properties:
9- cpufreq_tbl: Table of frequencies CPU could be transitioned into, in the
10 increasing order.
11
12Optional properties:
13- clock-latency: Specify the possible maximum transition latency for clock, in
14 unit of nanoseconds.
15
16Both required and optional properties listed above must be defined under node
17/cpus/cpu@0.
18
19Examples:
20--------
21cpus {
22
23 <...>
24
25 cpu@0 {
26 compatible = "arm,cortex-a9";
27 reg = <0>;
28
29 <...>
30
31 cpufreq_tbl = < 166000
32 200000
33 250000
34 300000
35 400000
36 500000
37 600000 >;
38 };
39
40 <...>
41
42};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ade7e924bef5..159e99737e31 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -904,6 +904,7 @@ config ARCH_NOMADIK
904 904
905config PLAT_SPEAR 905config PLAT_SPEAR
906 bool "ST SPEAr" 906 bool "ST SPEAr"
907 select ARCH_HAS_CPUFREQ
907 select ARCH_REQUIRE_GPIOLIB 908 select ARCH_REQUIRE_GPIOLIB
908 select ARM_AMBA 909 select ARM_AMBA
909 select CLKDEV_LOOKUP 910 select CLKDEV_LOOKUP
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 5961e6415f08..a0b3661d90b0 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -76,3 +76,10 @@ config ARM_EXYNOS5250_CPUFREQ
76 help 76 help
77 This adds the CPUFreq driver for Samsung EXYNOS5250 77 This adds the CPUFreq driver for Samsung EXYNOS5250
78 SoC. 78 SoC.
79
80config ARM_SPEAR_CPUFREQ
81 bool "SPEAr CPUFreq support"
82 depends on PLAT_SPEAR
83 default y
84 help
85 This adds the CPUFreq driver support for SPEAr SOCs.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 5b1413e47216..1f254ec087c1 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o
50obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o 50obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
51obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o 51obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
52obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o 52obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
53obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
53 54
54################################################################################## 55##################################################################################
55# PowerPC platform drivers 56# PowerPC platform drivers
diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c
new file mode 100644
index 000000000000..4575cfe41755
--- /dev/null
+++ b/drivers/cpufreq/spear-cpufreq.c
@@ -0,0 +1,291 @@
1/*
2 * drivers/cpufreq/spear-cpufreq.c
3 *
4 * CPU Frequency Scaling for SPEAr platform
5 *
6 * Copyright (C) 2012 ST Microelectronics
7 * Deepak Sikri <deepak.sikri@st.com>
8 *
9 * This file is licensed under the terms of the GNU General Public
10 * License version 2. This program is licensed "as is" without any
11 * warranty of any kind, whether express or implied.
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/clk.h>
17#include <linux/cpufreq.h>
18#include <linux/err.h>
19#include <linux/init.h>
20#include <linux/module.h>
21#include <linux/of.h>
22#include <linux/slab.h>
23#include <linux/types.h>
24
25/* SPEAr CPUFreq driver data structure */
26static struct {
27 struct clk *clk;
28 unsigned int transition_latency;
29 struct cpufreq_frequency_table *freq_tbl;
30 u32 cnt;
31} spear_cpufreq;
32
33int spear_cpufreq_verify(struct cpufreq_policy *policy)
34{
35 return cpufreq_frequency_table_verify(policy, spear_cpufreq.freq_tbl);
36}
37
38static unsigned int spear_cpufreq_get(unsigned int cpu)
39{
40 return clk_get_rate(spear_cpufreq.clk) / 1000;
41}
42
43static struct clk *spear1340_cpu_get_possible_parent(unsigned long newfreq)
44{
45 struct clk *sys_pclk;
46 int pclk;
47 /*
48 * In SPEAr1340, cpu clk's parent sys clk can take input from
49 * following sources
50 */
51 const char *sys_clk_src[] = {
52 "sys_syn_clk",
53 "pll1_clk",
54 "pll2_clk",
55 "pll3_clk",
56 };
57
58 /*
59 * As sys clk can have multiple source with their own range
60 * limitation so we choose possible sources accordingly
61 */
62 if (newfreq <= 300000000)
63 pclk = 0; /* src is sys_syn_clk */
64 else if (newfreq > 300000000 && newfreq <= 500000000)
65 pclk = 3; /* src is pll3_clk */
66 else if (newfreq == 600000000)
67 pclk = 1; /* src is pll1_clk */
68 else
69 return ERR_PTR(-EINVAL);
70
71 /* Get parent to sys clock */
72 sys_pclk = clk_get(NULL, sys_clk_src[pclk]);
73 if (IS_ERR(sys_pclk))
74 pr_err("Failed to get %s clock\n", sys_clk_src[pclk]);
75
76 return sys_pclk;
77}
78
79/*
80 * In SPEAr1340, we cannot use newfreq directly because we need to actually
81 * access a source clock (clk) which might not be ancestor of cpu at present.
82 * Hence in SPEAr1340 we would operate on source clock directly before switching
83 * cpu clock to it.
84 */
85static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq)
86{
87 struct clk *sys_clk;
88 int ret = 0;
89
90 sys_clk = clk_get_parent(spear_cpufreq.clk);
91 if (IS_ERR(sys_clk)) {
92 pr_err("failed to get cpu's parent (sys) clock\n");
93 return PTR_ERR(sys_clk);
94 }
95
96 /* Set the rate of the source clock before changing the parent */
97 ret = clk_set_rate(sys_pclk, newfreq);
98 if (ret) {
99 pr_err("Failed to set sys clk rate to %lu\n", newfreq);
100 return ret;
101 }
102
103 ret = clk_set_parent(sys_clk, sys_pclk);
104 if (ret) {
105 pr_err("Failed to set sys clk parent\n");
106 return ret;
107 }
108
109 return 0;
110}
111
112static int spear_cpufreq_target(struct cpufreq_policy *policy,
113 unsigned int target_freq, unsigned int relation)
114{
115 struct cpufreq_freqs freqs;
116 unsigned long newfreq;
117 struct clk *srcclk;
118 int index, ret, mult = 1;
119
120 if (cpufreq_frequency_table_target(policy, spear_cpufreq.freq_tbl,
121 target_freq, relation, &index))
122 return -EINVAL;
123
124 freqs.cpu = policy->cpu;
125 freqs.old = spear_cpufreq_get(0);
126
127 newfreq = spear_cpufreq.freq_tbl[index].frequency * 1000;
128 if (of_machine_is_compatible("st,spear1340")) {
129 /*
130 * SPEAr1340 is special in the sense that due to the possibility
131 * of multiple clock sources for cpu clk's parent we can have
132 * different clock source for different frequency of cpu clk.
133 * Hence we need to choose one from amongst these possible clock
134 * sources.
135 */
136 srcclk = spear1340_cpu_get_possible_parent(newfreq);
137 if (IS_ERR(srcclk)) {
138 pr_err("Failed to get src clk\n");
139 return PTR_ERR(srcclk);
140 }
141
142 /* SPEAr1340: src clk is always 2 * intended cpu clk */
143 mult = 2;
144 } else {
145 /*
146 * src clock to be altered is ancestor of cpu clock. Hence we
147 * can directly work on cpu clk
148 */
149 srcclk = spear_cpufreq.clk;
150 }
151
152 newfreq = clk_round_rate(srcclk, newfreq * mult);
153 if (newfreq < 0) {
154 pr_err("clk_round_rate failed for cpu src clock\n");
155 return newfreq;
156 }
157
158 freqs.new = newfreq / 1000;
159 freqs.new /= mult;
160 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
161
162 if (mult == 2)
163 ret = spear1340_set_cpu_rate(srcclk, newfreq);
164 else
165 ret = clk_set_rate(spear_cpufreq.clk, newfreq);
166
167 /* Get current rate after clk_set_rate, in case of failure */
168 if (ret) {
169 pr_err("CPU Freq: cpu clk_set_rate failed: %d\n", ret);
170 freqs.new = clk_get_rate(spear_cpufreq.clk) / 1000;
171 }
172
173 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
174 return ret;
175}
176
177static int spear_cpufreq_init(struct cpufreq_policy *policy)
178{
179 int ret;
180
181 ret = cpufreq_frequency_table_cpuinfo(policy, spear_cpufreq.freq_tbl);
182 if (ret) {
183 pr_err("cpufreq_frequency_table_cpuinfo() failed");
184 return ret;
185 }
186
187 cpufreq_frequency_table_get_attr(spear_cpufreq.freq_tbl, policy->cpu);
188 policy->cpuinfo.transition_latency = spear_cpufreq.transition_latency;
189 policy->cur = spear_cpufreq_get(0);
190
191 cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
192 cpumask_copy(policy->related_cpus, policy->cpus);
193
194 return 0;
195}
196
197static int spear_cpufreq_exit(struct cpufreq_policy *policy)
198{
199 cpufreq_frequency_table_put_attr(policy->cpu);
200 return 0;
201}
202
203static struct freq_attr *spear_cpufreq_attr[] = {
204 &cpufreq_freq_attr_scaling_available_freqs,
205 NULL,
206};
207
208static struct cpufreq_driver spear_cpufreq_driver = {
209 .name = "cpufreq-spear",
210 .flags = CPUFREQ_STICKY,
211 .verify = spear_cpufreq_verify,
212 .target = spear_cpufreq_target,
213 .get = spear_cpufreq_get,
214 .init = spear_cpufreq_init,
215 .exit = spear_cpufreq_exit,
216 .attr = spear_cpufreq_attr,
217};
218
219static int spear_cpufreq_driver_init(void)
220{
221 struct device_node *np;
222 const struct property *prop;
223 struct cpufreq_frequency_table *freq_tbl;
224 const __be32 *val;
225 int cnt, i, ret;
226
227 np = of_find_node_by_path("/cpus/cpu@0");
228 if (!np) {
229 pr_err("No cpu node found");
230 return -ENODEV;
231 }
232
233 if (of_property_read_u32(np, "clock-latency",
234 &spear_cpufreq.transition_latency))
235 spear_cpufreq.transition_latency = CPUFREQ_ETERNAL;
236
237 prop = of_find_property(np, "cpufreq_tbl", NULL);
238 if (!prop || !prop->value) {
239 pr_err("Invalid cpufreq_tbl");
240 ret = -ENODEV;
241 goto out_put_node;
242 }
243
244 cnt = prop->length / sizeof(u32);
245 val = prop->value;
246
247 freq_tbl = kmalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL);
248 if (!freq_tbl) {
249 ret = -ENOMEM;
250 goto out_put_node;
251 }
252
253 for (i = 0; i < cnt; i++) {
254 freq_tbl[i].index = i;
255 freq_tbl[i].frequency = be32_to_cpup(val++);
256 }
257
258 freq_tbl[i].index = i;
259 freq_tbl[i].frequency = CPUFREQ_TABLE_END;
260
261 spear_cpufreq.freq_tbl = freq_tbl;
262
263 of_node_put(np);
264
265 spear_cpufreq.clk = clk_get(NULL, "cpu_clk");
266 if (IS_ERR(spear_cpufreq.clk)) {
267 pr_err("Unable to get CPU clock\n");
268 ret = PTR_ERR(spear_cpufreq.clk);
269 goto out_put_mem;
270 }
271
272 ret = cpufreq_register_driver(&spear_cpufreq_driver);
273 if (!ret)
274 return 0;
275
276 pr_err("failed register driver: %d\n", ret);
277 clk_put(spear_cpufreq.clk);
278
279out_put_mem:
280 kfree(freq_tbl);
281 return ret;
282
283out_put_node:
284 of_node_put(np);
285 return ret;
286}
287late_initcall(spear_cpufreq_driver_init);
288
289MODULE_AUTHOR("Deepak Sikri <deepak.sikri@st.com>");
290MODULE_DESCRIPTION("SPEAr CPUFreq driver");
291MODULE_LICENSE("GPL");