blob: 05fc7448f5cbdaf5e49bc5a8a7acb66086fc5367 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001/*
2 * System Control and Power Interface (SCPI) based CPUFreq Interface driver
3 *
4 * It provides necessary ops to arm_big_little cpufreq driver.
5 *
6 * Copyright (C) 2015 ARM Ltd.
7 * Sudeep Holla <sudeep.holla@arm.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
14 * kind, whether express or implied; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
21#include <linux/clk.h>
22#include <linux/cpu.h>
23#include <linux/cpufreq.h>
24#include <linux/cpumask.h>
25#include <linux/cpu_cooling.h>
26#include <linux/energy_model.h>
27#include <linux/export.h>
28#include <linux/module.h>
29#include <linux/of_platform.h>
30#include <linux/pm_opp.h>
31#include <linux/scpi_protocol.h>
32#include <linux/slab.h>
33#include <linux/types.h>
34
35struct scpi_data {
36 struct clk *clk;
37 struct device *cpu_dev;
38 struct thermal_cooling_device *cdev;
39};
40
41static struct scpi_ops *scpi_ops;
42
43static unsigned int scpi_cpufreq_get_rate(unsigned int cpu)
44{
45 struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
46 struct scpi_data *priv = policy->driver_data;
47 unsigned long rate = clk_get_rate(priv->clk);
48
49 return rate / 1000;
50}
51
52static int
53scpi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
54{
55 unsigned long freq = policy->freq_table[index].frequency;
56 struct scpi_data *priv = policy->driver_data;
57 u64 rate = freq * 1000;
58 int ret;
59
60 ret = clk_set_rate(priv->clk, rate);
61
62 if (ret)
63 return ret;
64
65 if (clk_get_rate(priv->clk) != rate)
66 return -EIO;
67
68 arch_set_freq_scale(policy->related_cpus, freq,
69 policy->cpuinfo.max_freq);
70
71 return 0;
72}
73
74static int
75scpi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
76{
77 int cpu, domain, tdomain;
78 struct device *tcpu_dev;
79
80 domain = scpi_ops->device_domain_id(cpu_dev);
81 if (domain < 0)
82 return domain;
83
84 for_each_possible_cpu(cpu) {
85 if (cpu == cpu_dev->id)
86 continue;
87
88 tcpu_dev = get_cpu_device(cpu);
89 if (!tcpu_dev)
90 continue;
91
92 tdomain = scpi_ops->device_domain_id(tcpu_dev);
93 if (tdomain == domain)
94 cpumask_set_cpu(cpu, cpumask);
95 }
96
97 return 0;
98}
99
100static int scpi_cpufreq_init(struct cpufreq_policy *policy)
101{
102 int ret, nr_opp;
103 unsigned int latency;
104 struct device *cpu_dev;
105 struct scpi_data *priv;
106 struct cpufreq_frequency_table *freq_table;
107 struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power);
108
109 cpu_dev = get_cpu_device(policy->cpu);
110 if (!cpu_dev) {
111 pr_err("failed to get cpu%d device\n", policy->cpu);
112 return -ENODEV;
113 }
114
115 ret = scpi_ops->add_opps_to_device(cpu_dev);
116 if (ret) {
117 dev_warn(cpu_dev, "failed to add opps to the device\n");
118 return ret;
119 }
120
121 ret = scpi_get_sharing_cpus(cpu_dev, policy->cpus);
122 if (ret) {
123 dev_warn(cpu_dev, "failed to get sharing cpumask\n");
124 return ret;
125 }
126
127 ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
128 if (ret) {
129 dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
130 __func__, ret);
131 return ret;
132 }
133
134 ret = dev_pm_opp_get_opp_count(cpu_dev);
135 if (ret <= 0) {
136 dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
137 ret = -EPROBE_DEFER;
138 goto out_free_opp;
139 }
140 nr_opp = ret;
141
142 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
143 if (!priv) {
144 ret = -ENOMEM;
145 goto out_free_opp;
146 }
147
148 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
149 if (ret) {
150 dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
151 goto out_free_priv;
152 }
153
154 priv->cpu_dev = cpu_dev;
155 priv->clk = clk_get(cpu_dev, NULL);
156 if (IS_ERR(priv->clk)) {
157 dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d\n",
158 __func__, cpu_dev->id);
159 ret = PTR_ERR(priv->clk);
160 goto out_free_cpufreq_table;
161 }
162
163 policy->driver_data = priv;
164 policy->freq_table = freq_table;
165
166 /* scpi allows DVFS request for any domain from any CPU */
167 policy->dvfs_possible_from_any_cpu = true;
168
169 latency = scpi_ops->get_transition_latency(cpu_dev);
170 if (!latency)
171 latency = CPUFREQ_ETERNAL;
172
173 policy->cpuinfo.transition_latency = latency;
174
175 policy->fast_switch_possible = false;
176
177 em_register_perf_domain(policy->cpus, nr_opp, &em_cb);
178
179 return 0;
180
181out_free_cpufreq_table:
182 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
183out_free_priv:
184 kfree(priv);
185out_free_opp:
186 dev_pm_opp_cpumask_remove_table(policy->cpus);
187
188 return ret;
189}
190
191static int scpi_cpufreq_exit(struct cpufreq_policy *policy)
192{
193 struct scpi_data *priv = policy->driver_data;
194
195 cpufreq_cooling_unregister(priv->cdev);
196 clk_put(priv->clk);
197 dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
198 kfree(priv);
199 dev_pm_opp_cpumask_remove_table(policy->related_cpus);
200
201 return 0;
202}
203
204static void scpi_cpufreq_ready(struct cpufreq_policy *policy)
205{
206 struct scpi_data *priv = policy->driver_data;
207
208 priv->cdev = of_cpufreq_cooling_register(policy);
209}
210
211static struct cpufreq_driver scpi_cpufreq_driver = {
212 .name = "scpi-cpufreq",
213 .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
214 CPUFREQ_NEED_INITIAL_FREQ_CHECK,
215 .verify = cpufreq_generic_frequency_table_verify,
216 .attr = cpufreq_generic_attr,
217 .get = scpi_cpufreq_get_rate,
218 .init = scpi_cpufreq_init,
219 .exit = scpi_cpufreq_exit,
220 .ready = scpi_cpufreq_ready,
221 .target_index = scpi_cpufreq_set_target,
222};
223
224static int scpi_cpufreq_probe(struct platform_device *pdev)
225{
226 int ret;
227
228 scpi_ops = get_scpi_ops();
229 if (!scpi_ops)
230 return -EIO;
231
232 ret = cpufreq_register_driver(&scpi_cpufreq_driver);
233 if (ret)
234 dev_err(&pdev->dev, "%s: registering cpufreq failed, err: %d\n",
235 __func__, ret);
236 return ret;
237}
238
239static int scpi_cpufreq_remove(struct platform_device *pdev)
240{
241 cpufreq_unregister_driver(&scpi_cpufreq_driver);
242 scpi_ops = NULL;
243 return 0;
244}
245
246static struct platform_driver scpi_cpufreq_platdrv = {
247 .driver = {
248 .name = "scpi-cpufreq",
249 },
250 .probe = scpi_cpufreq_probe,
251 .remove = scpi_cpufreq_remove,
252};
253module_platform_driver(scpi_cpufreq_platdrv);
254
255MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
256MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
257MODULE_LICENSE("GPL v2");