]> rtime.felk.cvut.cz Git - linux-imx.git/blob - drivers/cpufreq/arm_big_little.c
cpufreq: ARM big LITTLE: Adapt to latest cpufreq updates
[linux-imx.git] / drivers / cpufreq / arm_big_little.c
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         pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n",
81                         __func__, cpu, cur_cluster, freqs.old, target_freq,
82                         freqs.new);
83
84         if (freqs.old == freqs.new)
85                 return 0;
86
87         cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
88
89         ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000);
90         if (ret) {
91                 pr_err("clk_set_rate failed: %d\n", ret);
92                 return ret;
93         }
94
95         policy->cur = freqs.new;
96
97         cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
98
99         return ret;
100 }
101
102 static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
103 {
104         u32 cluster = cpu_to_cluster(cpu_dev->id);
105
106         if (!atomic_dec_return(&cluster_usage[cluster])) {
107                 clk_put(clk[cluster]);
108                 opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
109                 dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
110         }
111 }
112
113 static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
114 {
115         u32 cluster = cpu_to_cluster(cpu_dev->id);
116         char name[14] = "cpu-cluster.";
117         int ret;
118
119         if (atomic_inc_return(&cluster_usage[cluster]) != 1)
120                 return 0;
121
122         ret = arm_bL_ops->init_opp_table(cpu_dev);
123         if (ret) {
124                 dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n",
125                                 __func__, cpu_dev->id, ret);
126                 goto atomic_dec;
127         }
128
129         ret = opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
130         if (ret) {
131                 dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
132                                 __func__, cpu_dev->id, ret);
133                 goto atomic_dec;
134         }
135
136         name[12] = cluster + '0';
137         clk[cluster] = clk_get_sys(name, NULL);
138         if (!IS_ERR(clk[cluster])) {
139                 dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
140                                 __func__, clk[cluster], freq_table[cluster],
141                                 cluster);
142                 return 0;
143         }
144
145         dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n",
146                         __func__, cpu_dev->id, cluster);
147         ret = PTR_ERR(clk[cluster]);
148         opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
149
150 atomic_dec:
151         atomic_dec(&cluster_usage[cluster]);
152         dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
153                         cluster);
154         return ret;
155 }
156
157 /* Per-CPU initialization */
158 static int bL_cpufreq_init(struct cpufreq_policy *policy)
159 {
160         u32 cur_cluster = cpu_to_cluster(policy->cpu);
161         struct device *cpu_dev;
162         int ret;
163
164         cpu_dev = get_cpu_device(policy->cpu);
165         if (!cpu_dev) {
166                 pr_err("%s: failed to get cpu%d device\n", __func__,
167                                 policy->cpu);
168                 return -ENODEV;
169         }
170
171         ret = get_cluster_clk_and_freq_table(cpu_dev);
172         if (ret)
173                 return ret;
174
175         ret = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]);
176         if (ret) {
177                 dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n",
178                                 policy->cpu, cur_cluster);
179                 put_cluster_clk_and_freq_table(cpu_dev);
180                 return ret;
181         }
182
183         cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu);
184
185         if (arm_bL_ops->get_transition_latency)
186                 policy->cpuinfo.transition_latency =
187                         arm_bL_ops->get_transition_latency(cpu_dev);
188         else
189                 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
190
191         policy->cur = bL_cpufreq_get(policy->cpu);
192
193         cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
194
195         dev_info(cpu_dev, "CPU %d initialized\n", policy->cpu);
196         return 0;
197 }
198
199 static int bL_cpufreq_exit(struct cpufreq_policy *policy)
200 {
201         struct device *cpu_dev;
202
203         cpu_dev = get_cpu_device(policy->cpu);
204         if (!cpu_dev) {
205                 pr_err("%s: failed to get cpu%d device\n", __func__,
206                                 policy->cpu);
207                 return -ENODEV;
208         }
209
210         put_cluster_clk_and_freq_table(cpu_dev);
211         dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu);
212
213         return 0;
214 }
215
216 /* Export freq_table to sysfs */
217 static struct freq_attr *bL_cpufreq_attr[] = {
218         &cpufreq_freq_attr_scaling_available_freqs,
219         NULL,
220 };
221
222 static struct cpufreq_driver bL_cpufreq_driver = {
223         .name                   = "arm-big-little",
224         .flags                  = CPUFREQ_STICKY,
225         .verify                 = bL_cpufreq_verify_policy,
226         .target                 = bL_cpufreq_set_target,
227         .get                    = bL_cpufreq_get,
228         .init                   = bL_cpufreq_init,
229         .exit                   = bL_cpufreq_exit,
230         .have_governor_per_policy = true,
231         .attr                   = bL_cpufreq_attr,
232 };
233
234 int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
235 {
236         int ret;
237
238         if (arm_bL_ops) {
239                 pr_debug("%s: Already registered: %s, exiting\n", __func__,
240                                 arm_bL_ops->name);
241                 return -EBUSY;
242         }
243
244         if (!ops || !strlen(ops->name) || !ops->init_opp_table) {
245                 pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__);
246                 return -ENODEV;
247         }
248
249         arm_bL_ops = ops;
250
251         ret = cpufreq_register_driver(&bL_cpufreq_driver);
252         if (ret) {
253                 pr_info("%s: Failed registering platform driver: %s, err: %d\n",
254                                 __func__, ops->name, ret);
255                 arm_bL_ops = NULL;
256         } else {
257                 pr_info("%s: Registered platform driver: %s\n", __func__,
258                                 ops->name);
259         }
260
261         return ret;
262 }
263 EXPORT_SYMBOL_GPL(bL_cpufreq_register);
264
265 void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops)
266 {
267         if (arm_bL_ops != ops) {
268                 pr_err("%s: Registered with: %s, can't unregister, exiting\n",
269                                 __func__, arm_bL_ops->name);
270                 return;
271         }
272
273         cpufreq_unregister_driver(&bL_cpufreq_driver);
274         pr_info("%s: Un-registered platform driver: %s\n", __func__,
275                         arm_bL_ops->name);
276         arm_bL_ops = NULL;
277 }
278 EXPORT_SYMBOL_GPL(bL_cpufreq_unregister);