diff options
-rw-r--r-- | Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt | 65 | ||||
-rw-r--r-- | MAINTAINERS | 11 | ||||
-rw-r--r-- | drivers/cpufreq/Kconfig.arm | 12 | ||||
-rw-r--r-- | drivers/cpufreq/Makefile | 5 | ||||
-rw-r--r-- | drivers/cpufreq/arm_big_little.c | 282 | ||||
-rw-r--r-- | drivers/cpufreq/arm_big_little.h | 40 | ||||
-rw-r--r-- | drivers/cpufreq/arm_big_little_dt.c | 92 |
7 files changed, 507 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt new file mode 100644 index 000000000000..0715695e94a9 --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt | |||
@@ -0,0 +1,65 @@ | |||
1 | Generic ARM big LITTLE cpufreq driver's DT glue | ||
2 | ----------------------------------------------- | ||
3 | |||
4 | This is DT specific glue layer for generic cpufreq driver for big LITTLE | ||
5 | systems. | ||
6 | |||
7 | Both required and optional properties listed below must be defined | ||
8 | under node /cpus/cpu@x. Where x is the first cpu inside a cluster. | ||
9 | |||
10 | FIXME: Cpus should boot in the order specified in DT and all cpus for a cluster | ||
11 | must be present contiguously. Generic DT driver will check only node 'x' for | ||
12 | cpu:x. | ||
13 | |||
14 | Required properties: | ||
15 | - operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt | ||
16 | for details | ||
17 | |||
18 | Optional properties: | ||
19 | - clock-latency: Specify the possible maximum transition latency for clock, | ||
20 | in unit of nanoseconds. | ||
21 | |||
22 | Examples: | ||
23 | |||
24 | cpus { | ||
25 | #address-cells = <1>; | ||
26 | #size-cells = <0>; | ||
27 | |||
28 | cpu@0 { | ||
29 | compatible = "arm,cortex-a15"; | ||
30 | reg = <0>; | ||
31 | next-level-cache = <&L2>; | ||
32 | operating-points = < | ||
33 | /* kHz uV */ | ||
34 | 792000 1100000 | ||
35 | 396000 950000 | ||
36 | 198000 850000 | ||
37 | >; | ||
38 | clock-latency = <61036>; /* two CLK32 periods */ | ||
39 | }; | ||
40 | |||
41 | cpu@1 { | ||
42 | compatible = "arm,cortex-a15"; | ||
43 | reg = <1>; | ||
44 | next-level-cache = <&L2>; | ||
45 | }; | ||
46 | |||
47 | cpu@100 { | ||
48 | compatible = "arm,cortex-a7"; | ||
49 | reg = <100>; | ||
50 | next-level-cache = <&L2>; | ||
51 | operating-points = < | ||
52 | /* kHz uV */ | ||
53 | 792000 950000 | ||
54 | 396000 750000 | ||
55 | 198000 450000 | ||
56 | >; | ||
57 | clock-latency = <61036>; /* two CLK32 periods */ | ||
58 | }; | ||
59 | |||
60 | cpu@101 { | ||
61 | compatible = "arm,cortex-a7"; | ||
62 | reg = <101>; | ||
63 | next-level-cache = <&L2>; | ||
64 | }; | ||
65 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 74e58a4d035b..be739837d6c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -2206,6 +2206,17 @@ S: Maintained | |||
2206 | F: drivers/cpufreq/ | 2206 | F: drivers/cpufreq/ |
2207 | F: include/linux/cpufreq.h | 2207 | F: include/linux/cpufreq.h |
2208 | 2208 | ||
2209 | CPU FREQUENCY DRIVERS - ARM BIG LITTLE | ||
2210 | M: Viresh Kumar <viresh.kumar@linaro.org> | ||
2211 | M: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com> | ||
2212 | L: cpufreq@vger.kernel.org | ||
2213 | L: linux-pm@vger.kernel.org | ||
2214 | W: http://www.arm.com/products/processors/technologies/biglittleprocessing.php | ||
2215 | S: Maintained | ||
2216 | F: drivers/cpufreq/arm_big_little.h | ||
2217 | F: drivers/cpufreq/arm_big_little.c | ||
2218 | F: drivers/cpufreq/arm_big_little_dt.c | ||
2219 | |||
2209 | CPUID/MSR DRIVER | 2220 | CPUID/MSR DRIVER |
2210 | M: "H. Peter Anvin" <hpa@zytor.com> | 2221 | M: "H. Peter Anvin" <hpa@zytor.com> |
2211 | S: Maintained | 2222 | S: Maintained |
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 030ddf6dd3f1..87b7e48698d0 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm | |||
@@ -2,6 +2,18 @@ | |||
2 | # ARM CPU Frequency scaling drivers | 2 | # ARM CPU Frequency scaling drivers |
3 | # | 3 | # |
4 | 4 | ||
5 | config ARM_BIG_LITTLE_CPUFREQ | ||
6 | tristate | ||
7 | depends on ARM_CPU_TOPOLOGY | ||
8 | |||
9 | config ARM_DT_BL_CPUFREQ | ||
10 | tristate "Generic ARM big LITTLE CPUfreq driver probed via DT" | ||
11 | select ARM_BIG_LITTLE_CPUFREQ | ||
12 | depends on OF && HAVE_CLK | ||
13 | help | ||
14 | This enables the Generic CPUfreq driver for ARM big.LITTLE platform. | ||
15 | This gets frequency tables from DT. | ||
16 | |||
5 | config ARM_OMAP2PLUS_CPUFREQ | 17 | config ARM_OMAP2PLUS_CPUFREQ |
6 | bool "TI OMAP2+" | 18 | bool "TI OMAP2+" |
7 | depends on ARCH_OMAP2PLUS | 19 | depends on ARCH_OMAP2PLUS |
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 863fd1865d45..ba9a3e1b1e69 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile | |||
@@ -44,6 +44,11 @@ obj-$(CONFIG_X86_INTEL_PSTATE) += intel_pstate.o | |||
44 | 44 | ||
45 | ################################################################################## | 45 | ################################################################################## |
46 | # ARM SoC drivers | 46 | # ARM SoC drivers |
47 | obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o | ||
48 | # big LITTLE per platform glues. Keep DT_BL_CPUFREQ as the last entry in all big | ||
49 | # LITTLE drivers, so that it is probed last. | ||
50 | obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o | ||
51 | |||
47 | obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o | 52 | obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o |
48 | obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o | 53 | obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o |
49 | obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o | 54 | obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o |
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c new file mode 100644 index 000000000000..1d29d1acbda7 --- /dev/null +++ b/drivers/cpufreq/arm_big_little.c | |||
@@ -0,0 +1,282 @@ | |||
1 | /* | ||
2 | * ARM big.LITTLE Platforms CPUFreq support | ||
3 | * | ||
4 | * Copyright (C) 2013 ARM Ltd. | ||
5 | * Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com> | ||
6 | * | ||
7 | * Copyright (C) 2013 Linaro. | ||
8 | * Viresh Kumar <viresh.kumar@linaro.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
15 | * kind, whether express or implied; without even the implied warranty | ||
16 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
21 | |||
22 | #include <linux/clk.h> | ||
23 | #include <linux/cpu.h> | ||
24 | #include <linux/cpufreq.h> | ||
25 | #include <linux/cpumask.h> | ||
26 | #include <linux/export.h> | ||
27 | #include <linux/of_platform.h> | ||
28 | #include <linux/opp.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/topology.h> | ||
31 | #include <linux/types.h> | ||
32 | |||
33 | #include "arm_big_little.h" | ||
34 | |||
35 | /* Currently we support only two clusters */ | ||
36 | #define MAX_CLUSTERS 2 | ||
37 | |||
38 | static struct cpufreq_arm_bL_ops *arm_bL_ops; | ||
39 | static struct clk *clk[MAX_CLUSTERS]; | ||
40 | static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; | ||
41 | static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)}; | ||
42 | |||
43 | static int cpu_to_cluster(int cpu) | ||
44 | { | ||
45 | return topology_physical_package_id(cpu); | ||
46 | } | ||
47 | |||
48 | static unsigned int bL_cpufreq_get(unsigned int cpu) | ||
49 | { | ||
50 | u32 cur_cluster = cpu_to_cluster(cpu); | ||
51 | |||
52 | return clk_get_rate(clk[cur_cluster]) / 1000; | ||
53 | } | ||
54 | |||
55 | /* Validate policy frequency range */ | ||
56 | static int bL_cpufreq_verify_policy(struct cpufreq_policy *policy) | ||
57 | { | ||
58 | u32 cur_cluster = cpu_to_cluster(policy->cpu); | ||
59 | |||
60 | return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]); | ||
61 | } | ||
62 | |||
63 | /* Set clock frequency */ | ||
64 | static int bL_cpufreq_set_target(struct cpufreq_policy *policy, | ||
65 | unsigned int target_freq, unsigned int relation) | ||
66 | { | ||
67 | struct cpufreq_freqs freqs; | ||
68 | u32 cpu = policy->cpu, freq_tab_idx, cur_cluster; | ||
69 | int ret = 0; | ||
70 | |||
71 | cur_cluster = cpu_to_cluster(policy->cpu); | ||
72 | |||
73 | freqs.old = bL_cpufreq_get(policy->cpu); | ||
74 | |||
75 | /* Determine valid target frequency using freq_table */ | ||
76 | cpufreq_frequency_table_target(policy, freq_table[cur_cluster], | ||
77 | target_freq, relation, &freq_tab_idx); | ||
78 | freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency; | ||
79 | |||
80 | freqs.cpu = policy->cpu; | ||
81 | |||
82 | pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n", | ||
83 | __func__, cpu, cur_cluster, freqs.old, target_freq, | ||
84 | freqs.new); | ||
85 | |||
86 | if (freqs.old == freqs.new) | ||
87 | return 0; | ||
88 | |||
89 | for_each_cpu(freqs.cpu, policy->cpus) | ||
90 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
91 | |||
92 | ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000); | ||
93 | if (ret) { | ||
94 | pr_err("clk_set_rate failed: %d\n", ret); | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | policy->cur = freqs.new; | ||
99 | |||
100 | for_each_cpu(freqs.cpu, policy->cpus) | ||
101 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
102 | |||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | static void put_cluster_clk_and_freq_table(struct device *cpu_dev) | ||
107 | { | ||
108 | u32 cluster = cpu_to_cluster(cpu_dev->id); | ||
109 | |||
110 | if (!atomic_dec_return(&cluster_usage[cluster])) { | ||
111 | clk_put(clk[cluster]); | ||
112 | opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); | ||
113 | dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | static int get_cluster_clk_and_freq_table(struct device *cpu_dev) | ||
118 | { | ||
119 | u32 cluster = cpu_to_cluster(cpu_dev->id); | ||
120 | char name[14] = "cpu-cluster."; | ||
121 | int ret; | ||
122 | |||
123 | if (atomic_inc_return(&cluster_usage[cluster]) != 1) | ||
124 | return 0; | ||
125 | |||
126 | ret = arm_bL_ops->init_opp_table(cpu_dev); | ||
127 | if (ret) { | ||
128 | dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n", | ||
129 | __func__, cpu_dev->id, ret); | ||
130 | goto atomic_dec; | ||
131 | } | ||
132 | |||
133 | ret = opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]); | ||
134 | if (ret) { | ||
135 | dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n", | ||
136 | __func__, cpu_dev->id, ret); | ||
137 | goto atomic_dec; | ||
138 | } | ||
139 | |||
140 | name[12] = cluster + '0'; | ||
141 | clk[cluster] = clk_get_sys(name, NULL); | ||
142 | if (!IS_ERR(clk[cluster])) { | ||
143 | dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n", | ||
144 | __func__, clk[cluster], freq_table[cluster], | ||
145 | cluster); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n", | ||
150 | __func__, cpu_dev->id, cluster); | ||
151 | ret = PTR_ERR(clk[cluster]); | ||
152 | opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); | ||
153 | |||
154 | atomic_dec: | ||
155 | atomic_dec(&cluster_usage[cluster]); | ||
156 | dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__, | ||
157 | cluster); | ||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | /* Per-CPU initialization */ | ||
162 | static int bL_cpufreq_init(struct cpufreq_policy *policy) | ||
163 | { | ||
164 | u32 cur_cluster = cpu_to_cluster(policy->cpu); | ||
165 | struct device *cpu_dev; | ||
166 | int ret; | ||
167 | |||
168 | cpu_dev = get_cpu_device(policy->cpu); | ||
169 | if (!cpu_dev) { | ||
170 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
171 | policy->cpu); | ||
172 | return -ENODEV; | ||
173 | } | ||
174 | |||
175 | ret = get_cluster_clk_and_freq_table(cpu_dev); | ||
176 | if (ret) | ||
177 | return ret; | ||
178 | |||
179 | ret = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]); | ||
180 | if (ret) { | ||
181 | dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n", | ||
182 | policy->cpu, cur_cluster); | ||
183 | put_cluster_clk_and_freq_table(cpu_dev); | ||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); | ||
188 | |||
189 | if (arm_bL_ops->get_transition_latency) | ||
190 | policy->cpuinfo.transition_latency = | ||
191 | arm_bL_ops->get_transition_latency(cpu_dev); | ||
192 | else | ||
193 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | ||
194 | |||
195 | policy->cur = bL_cpufreq_get(policy->cpu); | ||
196 | |||
197 | cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); | ||
198 | |||
199 | dev_info(cpu_dev, "CPU %d initialized\n", policy->cpu); | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int bL_cpufreq_exit(struct cpufreq_policy *policy) | ||
204 | { | ||
205 | struct device *cpu_dev; | ||
206 | |||
207 | cpu_dev = get_cpu_device(policy->cpu); | ||
208 | if (!cpu_dev) { | ||
209 | pr_err("%s: failed to get cpu%d device\n", __func__, | ||
210 | policy->cpu); | ||
211 | return -ENODEV; | ||
212 | } | ||
213 | |||
214 | put_cluster_clk_and_freq_table(cpu_dev); | ||
215 | dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | /* Export freq_table to sysfs */ | ||
221 | static struct freq_attr *bL_cpufreq_attr[] = { | ||
222 | &cpufreq_freq_attr_scaling_available_freqs, | ||
223 | NULL, | ||
224 | }; | ||
225 | |||
226 | static struct cpufreq_driver bL_cpufreq_driver = { | ||
227 | .name = "arm-big-little", | ||
228 | .flags = CPUFREQ_STICKY, | ||
229 | .verify = bL_cpufreq_verify_policy, | ||
230 | .target = bL_cpufreq_set_target, | ||
231 | .get = bL_cpufreq_get, | ||
232 | .init = bL_cpufreq_init, | ||
233 | .exit = bL_cpufreq_exit, | ||
234 | .have_multiple_policies = true, | ||
235 | .attr = bL_cpufreq_attr, | ||
236 | }; | ||
237 | |||
238 | int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) | ||
239 | { | ||
240 | int ret; | ||
241 | |||
242 | if (arm_bL_ops) { | ||
243 | pr_debug("%s: Already registered: %s, exiting\n", __func__, | ||
244 | arm_bL_ops->name); | ||
245 | return -EBUSY; | ||
246 | } | ||
247 | |||
248 | if (!ops || !strlen(ops->name) || !ops->init_opp_table) { | ||
249 | pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__); | ||
250 | return -ENODEV; | ||
251 | } | ||
252 | |||
253 | arm_bL_ops = ops; | ||
254 | |||
255 | ret = cpufreq_register_driver(&bL_cpufreq_driver); | ||
256 | if (ret) { | ||
257 | pr_info("%s: Failed registering platform driver: %s, err: %d\n", | ||
258 | __func__, ops->name, ret); | ||
259 | arm_bL_ops = NULL; | ||
260 | } else { | ||
261 | pr_info("%s: Registered platform driver: %s\n", __func__, | ||
262 | ops->name); | ||
263 | } | ||
264 | |||
265 | return ret; | ||
266 | } | ||
267 | EXPORT_SYMBOL_GPL(bL_cpufreq_register); | ||
268 | |||
269 | void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops) | ||
270 | { | ||
271 | if (arm_bL_ops != ops) { | ||
272 | pr_err("%s: Registered with: %s, can't unregister, exiting\n", | ||
273 | __func__, arm_bL_ops->name); | ||
274 | return; | ||
275 | } | ||
276 | |||
277 | cpufreq_unregister_driver(&bL_cpufreq_driver); | ||
278 | pr_info("%s: Un-registered platform driver: %s\n", __func__, | ||
279 | arm_bL_ops->name); | ||
280 | arm_bL_ops = NULL; | ||
281 | } | ||
282 | EXPORT_SYMBOL_GPL(bL_cpufreq_unregister); | ||
diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h new file mode 100644 index 000000000000..70f18fc12d4a --- /dev/null +++ b/drivers/cpufreq/arm_big_little.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * ARM big.LITTLE platform's CPUFreq header file | ||
3 | * | ||
4 | * Copyright (C) 2013 ARM Ltd. | ||
5 | * Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com> | ||
6 | * | ||
7 | * Copyright (C) 2013 Linaro. | ||
8 | * Viresh Kumar <viresh.kumar@linaro.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
15 | * kind, whether express or implied; without even the implied warranty | ||
16 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | #ifndef CPUFREQ_ARM_BIG_LITTLE_H | ||
20 | #define CPUFREQ_ARM_BIG_LITTLE_H | ||
21 | |||
22 | #include <linux/cpufreq.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/types.h> | ||
25 | |||
26 | struct cpufreq_arm_bL_ops { | ||
27 | char name[CPUFREQ_NAME_LEN]; | ||
28 | int (*get_transition_latency)(struct device *cpu_dev); | ||
29 | |||
30 | /* | ||
31 | * This must set opp table for cpu_dev in a similar way as done by | ||
32 | * of_init_opp_table(). | ||
33 | */ | ||
34 | int (*init_opp_table)(struct device *cpu_dev); | ||
35 | }; | ||
36 | |||
37 | int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops); | ||
38 | void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops); | ||
39 | |||
40 | #endif /* CPUFREQ_ARM_BIG_LITTLE_H */ | ||
diff --git a/drivers/cpufreq/arm_big_little_dt.c b/drivers/cpufreq/arm_big_little_dt.c new file mode 100644 index 000000000000..452ff46f20b7 --- /dev/null +++ b/drivers/cpufreq/arm_big_little_dt.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * Generic big.LITTLE CPUFreq Interface driver | ||
3 | * | ||
4 | * It provides necessary ops to arm_big_little cpufreq driver and gets | ||
5 | * Frequency information from Device Tree. Freq table in DT must be in KHz. | ||
6 | * | ||
7 | * Copyright (C) 2013 Linaro. | ||
8 | * Viresh Kumar <viresh.kumar@linaro.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
15 | * kind, whether express or implied; without even the implied warranty | ||
16 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
21 | |||
22 | #include <linux/cpufreq.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/export.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/of.h> | ||
27 | #include <linux/opp.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/types.h> | ||
30 | #include "arm_big_little.h" | ||
31 | |||
32 | static int dt_init_opp_table(struct device *cpu_dev) | ||
33 | { | ||
34 | struct device_node *np = NULL; | ||
35 | int count = 0, ret; | ||
36 | |||
37 | for_each_child_of_node(of_find_node_by_path("/cpus"), np) { | ||
38 | if (count++ != cpu_dev->id) | ||
39 | continue; | ||
40 | if (!of_get_property(np, "operating-points", NULL)) | ||
41 | return -ENODATA; | ||
42 | |||
43 | cpu_dev->of_node = np; | ||
44 | |||
45 | ret = of_init_opp_table(cpu_dev); | ||
46 | if (ret) | ||
47 | return ret; | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | return -ENODEV; | ||
53 | } | ||
54 | |||
55 | static int dt_get_transition_latency(struct device *cpu_dev) | ||
56 | { | ||
57 | struct device_node *np = NULL; | ||
58 | u32 transition_latency = CPUFREQ_ETERNAL; | ||
59 | int count = 0; | ||
60 | |||
61 | for_each_child_of_node(of_find_node_by_path("/cpus"), np) { | ||
62 | if (count++ != cpu_dev->id) | ||
63 | continue; | ||
64 | |||
65 | of_property_read_u32(np, "clock-latency", &transition_latency); | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | return -ENODEV; | ||
70 | } | ||
71 | |||
72 | static struct cpufreq_arm_bL_ops dt_bL_ops = { | ||
73 | .name = "dt-bl", | ||
74 | .get_transition_latency = dt_get_transition_latency, | ||
75 | .init_opp_table = dt_init_opp_table, | ||
76 | }; | ||
77 | |||
78 | static int generic_bL_init(void) | ||
79 | { | ||
80 | return bL_cpufreq_register(&dt_bL_ops); | ||
81 | } | ||
82 | module_init(generic_bL_init); | ||
83 | |||
84 | static void generic_bL_exit(void) | ||
85 | { | ||
86 | return bL_cpufreq_unregister(&dt_bL_ops); | ||
87 | } | ||
88 | module_exit(generic_bL_exit); | ||
89 | |||
90 | MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>"); | ||
91 | MODULE_DESCRIPTION("Generic ARM big LITTLE cpufreq driver via DT"); | ||
92 | MODULE_LICENSE("GPL"); | ||