xref: /openbmc/linux/arch/powerpc/platforms/cell/cpufreq_spudemand.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
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