1de6cc651SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2880e7105SChristian Krafft /*
3880e7105SChristian Krafft * spu aware cpufreq governor for the cell processor
4880e7105SChristian Krafft *
5880e7105SChristian Krafft * © Copyright IBM Corporation 2006-2008
6880e7105SChristian Krafft *
7880e7105SChristian Krafft * Author: Christian Krafft <krafft@de.ibm.com>
8880e7105SChristian Krafft */
9880e7105SChristian Krafft
10880e7105SChristian Krafft #include <linux/cpufreq.h>
11880e7105SChristian Krafft #include <linux/sched.h>
124f17722cSIngo Molnar #include <linux/sched/loadavg.h>
137dfe293cSPaul Gortmaker #include <linux/module.h>
14880e7105SChristian Krafft #include <linux/timer.h>
15880e7105SChristian Krafft #include <linux/workqueue.h>
1660063497SArun Sharma #include <linux/atomic.h>
17880e7105SChristian Krafft #include <asm/machdep.h>
18880e7105SChristian Krafft #include <asm/spu.h>
19880e7105SChristian Krafft
20880e7105SChristian Krafft #define POLL_TIME 100000 /* in µs */
21880e7105SChristian Krafft #define EXP 753 /* exp(-1) in fixed-point */
22880e7105SChristian Krafft
23880e7105SChristian Krafft struct spu_gov_info_struct {
24880e7105SChristian Krafft unsigned long busy_spus; /* fixed-point */
25880e7105SChristian Krafft struct cpufreq_policy *policy;
26880e7105SChristian Krafft struct delayed_work work;
27880e7105SChristian Krafft unsigned int poll_int; /* µs */
28880e7105SChristian Krafft };
29880e7105SChristian Krafft static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info);
30880e7105SChristian Krafft
calc_freq(struct spu_gov_info_struct * info)31880e7105SChristian Krafft static int calc_freq(struct spu_gov_info_struct *info)
32880e7105SChristian Krafft {
33880e7105SChristian Krafft int cpu;
34880e7105SChristian Krafft int busy_spus;
35880e7105SChristian Krafft
36880e7105SChristian Krafft cpu = info->policy->cpu;
37880e7105SChristian Krafft busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus);
38880e7105SChristian Krafft
398508cf3fSJohannes Weiner info->busy_spus = calc_load(info->busy_spus, EXP, busy_spus * FIXED_1);
40880e7105SChristian Krafft pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n",
41880e7105SChristian Krafft cpu, busy_spus, info->busy_spus);
42880e7105SChristian Krafft
43880e7105SChristian Krafft return info->policy->max * info->busy_spus / FIXED_1;
44880e7105SChristian Krafft }
45880e7105SChristian Krafft
spu_gov_work(struct work_struct * work)46880e7105SChristian Krafft static void spu_gov_work(struct work_struct *work)
47880e7105SChristian Krafft {
48880e7105SChristian Krafft struct spu_gov_info_struct *info;
49880e7105SChristian Krafft int delay;
50880e7105SChristian Krafft unsigned long target_freq;
51880e7105SChristian Krafft
52880e7105SChristian Krafft info = container_of(work, struct spu_gov_info_struct, work.work);
53880e7105SChristian Krafft
54880e7105SChristian Krafft /* after cancel_delayed_work_sync we unset info->policy */
55880e7105SChristian Krafft BUG_ON(info->policy == NULL);
56880e7105SChristian Krafft
57880e7105SChristian Krafft target_freq = calc_freq(info);
58880e7105SChristian Krafft __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H);
59880e7105SChristian Krafft
60880e7105SChristian Krafft delay = usecs_to_jiffies(info->poll_int);
61b18ae08dSTejun Heo schedule_delayed_work_on(info->policy->cpu, &info->work, delay);
62880e7105SChristian Krafft }
63880e7105SChristian Krafft
spu_gov_init_work(struct spu_gov_info_struct * info)64880e7105SChristian Krafft static void spu_gov_init_work(struct spu_gov_info_struct *info)
65880e7105SChristian Krafft {
66880e7105SChristian Krafft int delay = usecs_to_jiffies(info->poll_int);
67203b42f7STejun Heo INIT_DEFERRABLE_WORK(&info->work, spu_gov_work);
68b18ae08dSTejun Heo schedule_delayed_work_on(info->policy->cpu, &info->work, delay);
69880e7105SChristian Krafft }
70880e7105SChristian Krafft
spu_gov_cancel_work(struct spu_gov_info_struct * info)71880e7105SChristian Krafft static void spu_gov_cancel_work(struct spu_gov_info_struct *info)
72880e7105SChristian Krafft {
73880e7105SChristian Krafft cancel_delayed_work_sync(&info->work);
74880e7105SChristian Krafft }
75880e7105SChristian Krafft
spu_gov_start(struct cpufreq_policy * policy)76e788892bSRafael J. Wysocki static int spu_gov_start(struct cpufreq_policy *policy)
77880e7105SChristian Krafft {
78880e7105SChristian Krafft unsigned int cpu = policy->cpu;
79e788892bSRafael J. Wysocki struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu);
80e788892bSRafael J. Wysocki struct spu_gov_info_struct *affected_info;
81880e7105SChristian Krafft int i;
82880e7105SChristian Krafft
83880e7105SChristian Krafft if (!cpu_online(cpu)) {
84880e7105SChristian Krafft printk(KERN_ERR "cpu %d is not online\n", cpu);
85e788892bSRafael J. Wysocki return -EINVAL;
86880e7105SChristian Krafft }
87880e7105SChristian Krafft
88880e7105SChristian Krafft if (!policy->cur) {
89880e7105SChristian Krafft printk(KERN_ERR "no cpu specified in policy\n");
90e788892bSRafael J. Wysocki return -EINVAL;
91880e7105SChristian Krafft }
92880e7105SChristian Krafft
93880e7105SChristian Krafft /* initialize spu_gov_info for all affected cpus */
94ae04d140SBenjamin Herrenschmidt for_each_cpu(i, policy->cpus) {
95880e7105SChristian Krafft affected_info = &per_cpu(spu_gov_info, i);
96880e7105SChristian Krafft affected_info->policy = policy;
97880e7105SChristian Krafft }
98880e7105SChristian Krafft
99880e7105SChristian Krafft info->poll_int = POLL_TIME;
100880e7105SChristian Krafft
101880e7105SChristian Krafft /* setup timer */
102880e7105SChristian Krafft spu_gov_init_work(info);
103880e7105SChristian Krafft
104e788892bSRafael J. Wysocki return 0;
105e788892bSRafael J. Wysocki }
106880e7105SChristian Krafft
spu_gov_stop(struct cpufreq_policy * policy)107e788892bSRafael J. Wysocki static void spu_gov_stop(struct cpufreq_policy *policy)
108e788892bSRafael J. Wysocki {
109e788892bSRafael J. Wysocki unsigned int cpu = policy->cpu;
110e788892bSRafael J. Wysocki struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu);
111e788892bSRafael J. Wysocki int i;
112e788892bSRafael J. Wysocki
113880e7105SChristian Krafft /* cancel timer */
114880e7105SChristian Krafft spu_gov_cancel_work(info);
115880e7105SChristian Krafft
116880e7105SChristian Krafft /* clean spu_gov_info for all affected cpus */
117ae04d140SBenjamin Herrenschmidt for_each_cpu (i, policy->cpus) {
118880e7105SChristian Krafft info = &per_cpu(spu_gov_info, i);
119880e7105SChristian Krafft info->policy = NULL;
120880e7105SChristian Krafft }
121880e7105SChristian Krafft }
122880e7105SChristian Krafft
123880e7105SChristian Krafft static struct cpufreq_governor spu_governor = {
124880e7105SChristian Krafft .name = "spudemand",
125e788892bSRafael J. Wysocki .start = spu_gov_start,
126e788892bSRafael J. Wysocki .stop = spu_gov_stop,
127880e7105SChristian Krafft .owner = THIS_MODULE,
128880e7105SChristian Krafft };
129*10dd8573SQuentin Perret cpufreq_governor_init(spu_governor);
130*10dd8573SQuentin Perret cpufreq_governor_exit(spu_governor);
131880e7105SChristian Krafft
132880e7105SChristian Krafft MODULE_LICENSE("GPL");
133880e7105SChristian Krafft MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
134