blob: 163e3378fe176f9c3894ec79b056dfef3f070239 [file] [log] [blame]
Viresh Kumar8a67f0e2013-04-01 12:57:49 +00001/*
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>
Nishanth Menone4db1c72013-09-19 16:03:52 -050028#include <linux/pm_opp.h>
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000029#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
38static struct cpufreq_arm_bL_ops *arm_bL_ops;
39static struct clk *clk[MAX_CLUSTERS];
40static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS];
41static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)};
42
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000043static unsigned int bL_cpufreq_get(unsigned int cpu)
44{
45 u32 cur_cluster = cpu_to_cluster(cpu);
46
47 return clk_get_rate(clk[cur_cluster]) / 1000;
48}
49
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000050/* Set clock frequency */
51static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +053052 unsigned int index)
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000053{
54 struct cpufreq_freqs freqs;
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +053055 u32 cpu = policy->cpu, cur_cluster;
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000056 int ret = 0;
57
58 cur_cluster = cpu_to_cluster(policy->cpu);
59
60 freqs.old = bL_cpufreq_get(policy->cpu);
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +053061 freqs.new = freq_table[cur_cluster][index].frequency;
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000062
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000063 pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n",
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +053064 __func__, cpu, cur_cluster, freqs.old, freqs.new,
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000065 freqs.new);
66
Viresh Kumarad61f442013-04-15 07:05:25 +000067 cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000068
69 ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000);
70 if (ret) {
71 pr_err("clk_set_rate failed: %d\n", ret);
Viresh Kumar3d69dd52013-06-19 11:18:20 +053072 freqs.new = freqs.old;
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000073 }
74
Viresh Kumarad61f442013-04-15 07:05:25 +000075 cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000076
77 return ret;
78}
79
80static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
81{
82 u32 cluster = cpu_to_cluster(cpu_dev->id);
83
84 if (!atomic_dec_return(&cluster_usage[cluster])) {
85 clk_put(clk[cluster]);
Nishanth Menon5d4879c2013-09-19 16:03:50 -050086 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +000087 dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
88 }
89}
90
91static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
92{
93 u32 cluster = cpu_to_cluster(cpu_dev->id);
94 char name[14] = "cpu-cluster.";
95 int ret;
96
97 if (atomic_inc_return(&cluster_usage[cluster]) != 1)
98 return 0;
99
100 ret = arm_bL_ops->init_opp_table(cpu_dev);
101 if (ret) {
102 dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n",
103 __func__, cpu_dev->id, ret);
104 goto atomic_dec;
105 }
106
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500107 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000108 if (ret) {
109 dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
110 __func__, cpu_dev->id, ret);
111 goto atomic_dec;
112 }
113
114 name[12] = cluster + '0';
Sudeep KarkadaNagesha076dec92013-10-16 14:52:39 +0100115 clk[cluster] = clk_get(cpu_dev, name);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000116 if (!IS_ERR(clk[cluster])) {
117 dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
118 __func__, clk[cluster], freq_table[cluster],
119 cluster);
120 return 0;
121 }
122
123 dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n",
124 __func__, cpu_dev->id, cluster);
125 ret = PTR_ERR(clk[cluster]);
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500126 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000127
128atomic_dec:
129 atomic_dec(&cluster_usage[cluster]);
130 dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
131 cluster);
132 return ret;
133}
134
135/* Per-CPU initialization */
136static int bL_cpufreq_init(struct cpufreq_policy *policy)
137{
138 u32 cur_cluster = cpu_to_cluster(policy->cpu);
139 struct device *cpu_dev;
140 int ret;
141
142 cpu_dev = get_cpu_device(policy->cpu);
143 if (!cpu_dev) {
144 pr_err("%s: failed to get cpu%d device\n", __func__,
145 policy->cpu);
146 return -ENODEV;
147 }
148
149 ret = get_cluster_clk_and_freq_table(cpu_dev);
150 if (ret)
151 return ret;
152
Viresh Kumar39b10eb2013-09-16 18:56:08 +0530153 ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000154 if (ret) {
155 dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n",
156 policy->cpu, cur_cluster);
157 put_cluster_clk_and_freq_table(cpu_dev);
158 return ret;
159 }
160
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000161 if (arm_bL_ops->get_transition_latency)
162 policy->cpuinfo.transition_latency =
163 arm_bL_ops->get_transition_latency(cpu_dev);
164 else
165 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
166
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000167 cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
168
Viresh Kumar2b80f312013-04-29 13:24:48 +0000169 dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000170 return 0;
171}
172
173static int bL_cpufreq_exit(struct cpufreq_policy *policy)
174{
175 struct device *cpu_dev;
176
177 cpu_dev = get_cpu_device(policy->cpu);
178 if (!cpu_dev) {
179 pr_err("%s: failed to get cpu%d device\n", __func__,
180 policy->cpu);
181 return -ENODEV;
182 }
183
Viresh Kumar2457dac2013-09-16 18:56:42 +0530184 cpufreq_frequency_table_put_attr(policy->cpu);
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000185 put_cluster_clk_and_freq_table(cpu_dev);
186 dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu);
187
188 return 0;
189}
190
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000191static struct cpufreq_driver bL_cpufreq_driver = {
192 .name = "arm-big-little",
Viresh Kumar0b981e72013-10-02 14:13:18 +0530193 .flags = CPUFREQ_STICKY |
194 CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
Viresh Kumar3c75a152013-10-03 20:27:57 +0530195 .verify = cpufreq_generic_frequency_table_verify,
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +0530196 .target_index = bL_cpufreq_set_target,
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000197 .get = bL_cpufreq_get,
198 .init = bL_cpufreq_init,
199 .exit = bL_cpufreq_exit,
Viresh Kumar3c75a152013-10-03 20:27:57 +0530200 .attr = cpufreq_generic_attr,
Viresh Kumar8a67f0e2013-04-01 12:57:49 +0000201};
202
203int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
204{
205 int ret;
206
207 if (arm_bL_ops) {
208 pr_debug("%s: Already registered: %s, exiting\n", __func__,
209 arm_bL_ops->name);
210 return -EBUSY;
211 }
212
213 if (!ops || !strlen(ops->name) || !ops->init_opp_table) {
214 pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__);
215 return -ENODEV;
216 }
217
218 arm_bL_ops = ops;
219
220 ret = cpufreq_register_driver(&bL_cpufreq_driver);
221 if (ret) {
222 pr_info("%s: Failed registering platform driver: %s, err: %d\n",
223 __func__, ops->name, ret);
224 arm_bL_ops = NULL;
225 } else {
226 pr_info("%s: Registered platform driver: %s\n", __func__,
227 ops->name);
228 }
229
230 return ret;
231}
232EXPORT_SYMBOL_GPL(bL_cpufreq_register);
233
234void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops)
235{
236 if (arm_bL_ops != ops) {
237 pr_err("%s: Registered with: %s, can't unregister, exiting\n",
238 __func__, arm_bL_ops->name);
239 return;
240 }
241
242 cpufreq_unregister_driver(&bL_cpufreq_driver);
243 pr_info("%s: Un-registered platform driver: %s\n", __func__,
244 arm_bL_ops->name);
245 arm_bL_ops = NULL;
246}
247EXPORT_SYMBOL_GPL(bL_cpufreq_unregister);