blob: 0c6230a48eb916035c5340c5000d8727d1f3ebe4 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Support for asr spi controller
4 *
5 * Copyright (C) 2021 ASR Micro Limited
6 *
7 */
8
9#include <linux/clk.h>
10#include <linux/clk-provider.h>
11#include <linux/cpufreq.h>
12#include <linux/cpumask.h>
13#include <linux/delay.h>
14#include <linux/err.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/types.h>
20#include <linux/sched.h>
21#include <linux/suspend.h>
22//#include <asm/system.h>
23#include <asm/cpu.h>
24#include <linux/pm_qos.h>
25
26#define NUM_CPUS num_possible_cpus()
27
28#define KHZ_TO_HZ (1000)
29#define MHZ_TO_KHZ (1000)
30#define MHZ_TO_HZ (1000000)
31
32static struct clk *cpu_clk;
33static DEFINE_MUTEX(asr18xx_cpu_lock);
34static bool is_suspended;
35static struct cpufreq_frequency_table *freq_table;
36static bool is_qosreq_inited;
37
38/* Qos min request client cpufreq driver policy->cpuinfo.min */
39static struct pm_qos_request cpufreq_qos_req_min = {
40 .name = "cpu_freqmin",
41};
42/* Qos max request client cpufreq driver policy->cpuinfo.min */
43static struct pm_qos_request cpufreq_qos_req_max = {
44 .name = "cpu_freqmax",
45};
46
47int asr18xx_verify_speed(struct cpufreq_policy_data *policy)
48{
49 return cpufreq_frequency_table_verify(policy, freq_table);
50}
51
52unsigned int asr18xx_getspeed(unsigned int cpu)
53{
54 unsigned long rate;
55
56 if (cpu >= NUM_CPUS)
57 return 0;
58
59 rate = clk_get_rate(cpu_clk) / KHZ_TO_HZ;
60 return rate;
61}
62
63static int asr18xx_update_cpu_speed(unsigned long rate)
64{
65 struct cpufreq_freqs freqs;
66 struct cpufreq_policy *policy;
67 int ret = 0;
68 int cpu = 0;
69
70 freqs.old = asr18xx_getspeed(0);
71 freqs.new = rate;
72
73 if (freqs.old == freqs.new)
74 return 0;
75
76 policy = cpufreq_cpu_get(cpu);
77 BUG_ON(!policy);
78#ifdef CONFIG_CPU_FREQ_DEBUG
79 printk(KERN_DEBUG "cpufreq-asr18xx: transition: %u --> %u\n",
80 freqs.old, freqs.new);
81#endif
82 /* cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); */
83 cpufreq_freq_transition_begin(policy, &freqs);
84
85 ret = clk_set_rate(cpu_clk, freqs.new * KHZ_TO_HZ);
86
87 if (ret)
88 freqs.new = freqs.old;
89
90 /* cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); */
91 cpufreq_freq_transition_end(policy, &freqs, 0);
92 cpufreq_cpu_put(policy);
93 return ret;
94}
95
96/* Same as cpufreq_frequency_table_target, but no policy min/max check */
97static int cpufreq_table_target(struct cpufreq_policy *policy,
98 struct cpufreq_frequency_table *table,
99 unsigned int target_freq,
100 unsigned int relation,
101 unsigned int *index)
102{
103 struct cpufreq_frequency_table optimal = {
104 .driver_data = ~0,
105 .frequency = 0,
106 };
107 struct cpufreq_frequency_table suboptimal = {
108 .driver_data = ~0,
109 .frequency = 0,
110 };
111 unsigned int diff;
112 unsigned int i;
113
114 pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
115 target_freq, relation, policy->cpu);
116
117 switch (relation) {
118 case CPUFREQ_RELATION_H:
119 suboptimal.frequency = ~0;
120 break;
121 case CPUFREQ_RELATION_L:
122 case CPUFREQ_RELATION_C:
123 optimal.frequency = ~0;
124 break;
125 }
126
127 if (!cpu_online(policy->cpu)) {
128 WARN(1, "%s error: cpu%d offline\n", __func__, policy->cpu);
129 return 0;
130 }
131
132 for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
133 unsigned int freq = table[i].frequency;
134 if (freq == CPUFREQ_ENTRY_INVALID)
135 continue;
136 if (freq == target_freq) {
137 optimal.driver_data = i;
138 break;
139 }
140 switch (relation) {
141 case CPUFREQ_RELATION_H:
142 if (freq <= target_freq) {
143 if (freq >= optimal.frequency) {
144 optimal.frequency = freq;
145 optimal.driver_data = i;
146 }
147 } else {
148 if (freq <= suboptimal.frequency) {
149 suboptimal.frequency = freq;
150 suboptimal.driver_data = i;
151 }
152 }
153 break;
154 case CPUFREQ_RELATION_L:
155 if (freq >= target_freq) {
156 if (freq <= optimal.frequency) {
157 optimal.frequency = freq;
158 optimal.driver_data = i;
159 }
160 } else {
161 if (freq >= suboptimal.frequency) {
162 suboptimal.frequency = freq;
163 suboptimal.driver_data = i;
164 }
165 }
166 break;
167 case CPUFREQ_RELATION_C:
168 diff = abs(freq - target_freq);
169 if (diff < optimal.frequency ||
170 (diff == optimal.frequency &&
171 freq > table[optimal.driver_data].frequency)) {
172 optimal.frequency = diff;
173 optimal.driver_data = i;
174 }
175 break;
176 }
177 }
178 if (optimal.driver_data > i) {
179 if (suboptimal.driver_data > i) {
180 WARN(1, "%s Invalid frequency table: %d\n", __func__, policy->cpu);
181 return 0;
182 }
183 *index = suboptimal.driver_data;
184 } else
185 *index = optimal.driver_data;
186
187 pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
188 table[*index].driver_data);
189
190 return 0;
191}
192
193static int asr18xx_target(struct cpufreq_policy *policy,
194 unsigned int target_freq,
195 unsigned int relation)
196{
197 int idx = 0;
198 unsigned int freq, qos_min, qos_max;
199 int ret = 0;
200
201 mutex_lock(&asr18xx_cpu_lock);
202
203 if (is_suspended) {
204 ret = -EBUSY;
205 goto out;
206 }
207
208 /*
209 * Policy min and qos min have lower priority
210 * than policy max and qos max.
211 * Policy min and qos min has no priority order
212 * Policy max and qos max has no proirity order
213 */
214 qos_min = (unsigned int)pm_qos_request(PM_QOS_CPUFREQ_MIN);
215 qos_max = (unsigned int)pm_qos_request(PM_QOS_CPUFREQ_MAX);
216 target_freq = max(policy->min, target_freq);
217 target_freq = max(qos_min, target_freq);
218 target_freq = min(policy->max, target_freq);
219 target_freq = min(qos_max, target_freq);
220 if ((target_freq == policy->max) || (target_freq == qos_max))
221 relation = CPUFREQ_RELATION_H;
222 cpufreq_table_target(policy, freq_table, target_freq, relation, &idx);
223 freq = freq_table[idx].frequency;
224 pr_debug("Qos_min:%d Qos_max:%d, Target:%d(KHZ)\n",
225 pm_qos_request(PM_QOS_CPUFREQ_MIN),
226 pm_qos_request(PM_QOS_CPUFREQ_MAX),
227 freq);
228
229 ret = asr18xx_update_cpu_speed(freq);
230out:
231 mutex_unlock(&asr18xx_cpu_lock);
232 return ret;
233}
234
235
236static int asr18xx_pm_notify(struct notifier_block *nb, unsigned long event,
237 void *dummy)
238{
239 static unsigned int saved_cpuclk = 0;
240 mutex_lock(&asr18xx_cpu_lock);
241 if (event == PM_SUSPEND_PREPARE) {
242 /* scaling to the min frequency before entering suspend */
243 saved_cpuclk = asr18xx_getspeed(0);
244 asr18xx_update_cpu_speed(freq_table[0].frequency);
245 is_suspended = true;
246 pr_pm_debug("%s: disable cpu freq-chg before suspend, cur"\
247 " rate %dKhz\n",
248 __func__, asr18xx_getspeed(0));
249 } else if (event == PM_POST_SUSPEND) {
250 is_suspended = false;
251 asr18xx_update_cpu_speed(saved_cpuclk);
252 pr_pm_debug("%s: enable cpu freq-chg after resume, cur"\
253 " rate %dKhz\n",
254 __func__, asr18xx_getspeed(0));
255 }
256 mutex_unlock(&asr18xx_cpu_lock);
257 return NOTIFY_OK;
258}
259
260static struct notifier_block asr18xx_cpu_pm_notifier = {
261 .notifier_call = asr18xx_pm_notify,
262};
263
264/* cpufreq qos min/max notifier unit Khz */
265static int cpufreq_min_notify(struct notifier_block *b,
266 unsigned long min, void *v)
267{
268 int ret;
269 unsigned long freq, val = min;
270 struct cpufreq_policy *policy;
271 int cpu = 0;
272
273 freq = asr18xx_getspeed(cpu);
274 if (freq >= val)
275 return NOTIFY_OK;
276
277 policy = cpufreq_cpu_get(cpu);
278 if (!policy)
279 return NOTIFY_BAD;
280
281 ret = asr18xx_target(policy, val, CPUFREQ_RELATION_L);
282 cpufreq_cpu_put(policy);
283 if (ret < 0)
284 return NOTIFY_BAD;
285
286 return NOTIFY_OK;
287}
288
289static struct notifier_block cpufreq_min_notifier = {
290 .notifier_call = cpufreq_min_notify,
291};
292
293static int cpufreq_max_notify(struct notifier_block *b,
294 unsigned long max, void *v)
295{
296 int ret;
297 unsigned long freq, val = max;
298 struct cpufreq_policy *policy;
299 int cpu = 0;
300
301 freq = asr18xx_getspeed(cpu);
302
303 policy = cpufreq_cpu_get(cpu);
304 if (!policy)
305 return NOTIFY_BAD;
306
307 ret = asr18xx_target(policy, val, CPUFREQ_RELATION_H);
308 cpufreq_cpu_put(policy);
309 if (ret < 0)
310 return NOTIFY_BAD;
311
312 return NOTIFY_OK;
313}
314
315
316static struct notifier_block cpufreq_max_notifier = {
317 .notifier_call = cpufreq_max_notify,
318};
319
320
321static int asr18xx_cpufreq_init(struct cpufreq_policy *policy)
322{
323 if (policy->cpu >= NUM_CPUS)
324 return -EINVAL;
325
326 if (unlikely(!cpu_clk)) {
327 cpu_clk = __clk_lookup("cpu");
328 if (IS_ERR(cpu_clk))
329 return PTR_ERR(cpu_clk);
330 }
331
332 freq_table = cpufreq_frequency_get_table(policy->cpu);
333 BUG_ON(!freq_table);
334 cpufreq_frequency_table_cpuinfo(policy, freq_table);
335 policy->cur = asr18xx_getspeed(policy->cpu);
336
337 /*
338 * FIXME: what's the actual transition time?
339 * use 10ms as sampling rate for bring up
340 */
341 policy->cpuinfo.transition_latency = 10 * 1000;
342 policy->freq_table = freq_table;
343 cpumask_setall(policy->cpus);
344 if (unlikely(!is_qosreq_inited)) {
345 pm_qos_add_request(&cpufreq_qos_req_min,
346 PM_QOS_CPUFREQ_MIN, policy->cpuinfo.min_freq);
347 pm_qos_add_request(&cpufreq_qos_req_max,
348 PM_QOS_CPUFREQ_MAX, policy->cpuinfo.max_freq);
349 is_qosreq_inited = true;
350 }
351
352 return 0;
353}
354
355static int asr18xx_cpufreq_exit(struct cpufreq_policy *policy)
356{
357 cpufreq_frequency_table_cpuinfo(policy, freq_table);
358 return 0;
359}
360
361static struct freq_attr *asr18xx_cpufreq_attr[] = {
362 &cpufreq_freq_attr_scaling_available_freqs,
363 NULL,
364};
365
366static struct cpufreq_driver asr18xx_cpufreq_driver = {
367#ifdef CONFIG_CPU_ASR18XX
368 .flags = 0,
369#else
370 .flags = CPUFREQ_CONST_LOOPS,
371#endif
372 .verify = asr18xx_verify_speed,
373 .target = asr18xx_target,
374 .get = asr18xx_getspeed,
375 .init = asr18xx_cpufreq_init,
376 .exit = asr18xx_cpufreq_exit,
377 .name = "asr18xx-cpufreq",
378 .attr = asr18xx_cpufreq_attr,
379};
380
381static int __init cpufreq_init(void)
382{
383 register_pm_notifier(&asr18xx_cpu_pm_notifier);
384 pm_qos_add_notifier(PM_QOS_CPUFREQ_MIN,
385 &cpufreq_min_notifier);
386 pm_qos_add_notifier(PM_QOS_CPUFREQ_MAX,
387 &cpufreq_max_notifier);
388 return cpufreq_register_driver(&asr18xx_cpufreq_driver);
389}
390
391static void __exit cpufreq_exit(void)
392{
393 unregister_pm_notifier(&asr18xx_cpu_pm_notifier);
394 pm_qos_remove_notifier(PM_QOS_CPUFREQ_MIN,
395 &cpufreq_min_notifier);
396 pm_qos_remove_notifier(PM_QOS_CPUFREQ_MAX,
397 &cpufreq_max_notifier);
398 cpufreq_unregister_driver(&asr18xx_cpufreq_driver);
399}
400
401
402MODULE_DESCRIPTION("cpufreq driver for ASR ASR18XX");
403MODULE_LICENSE("GPL");
404module_init(cpufreq_init);
405module_exit(cpufreq_exit);