18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * spu aware cpufreq governor for the cell processor
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * © Copyright IBM Corporation 2006-2008
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Christian Krafft <krafft@de.ibm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/cpufreq.h>
118c2ecf20Sopenharmony_ci#include <linux/sched.h>
128c2ecf20Sopenharmony_ci#include <linux/sched/loadavg.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/timer.h>
158c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
168c2ecf20Sopenharmony_ci#include <linux/atomic.h>
178c2ecf20Sopenharmony_ci#include <asm/machdep.h>
188c2ecf20Sopenharmony_ci#include <asm/spu.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define POLL_TIME	100000		/* in µs */
218c2ecf20Sopenharmony_ci#define EXP		753		/* exp(-1) in fixed-point */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct spu_gov_info_struct {
248c2ecf20Sopenharmony_ci	unsigned long busy_spus;	/* fixed-point */
258c2ecf20Sopenharmony_ci	struct cpufreq_policy *policy;
268c2ecf20Sopenharmony_ci	struct delayed_work work;
278c2ecf20Sopenharmony_ci	unsigned int poll_int;		/* µs */
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int calc_freq(struct spu_gov_info_struct *info)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	int cpu;
348c2ecf20Sopenharmony_ci	int busy_spus;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	cpu = info->policy->cpu;
378c2ecf20Sopenharmony_ci	busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	info->busy_spus = calc_load(info->busy_spus, EXP, busy_spus * FIXED_1);
408c2ecf20Sopenharmony_ci	pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n",
418c2ecf20Sopenharmony_ci			cpu, busy_spus, info->busy_spus);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return info->policy->max * info->busy_spus / FIXED_1;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void spu_gov_work(struct work_struct *work)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct spu_gov_info_struct *info;
498c2ecf20Sopenharmony_ci	int delay;
508c2ecf20Sopenharmony_ci	unsigned long target_freq;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	info = container_of(work, struct spu_gov_info_struct, work.work);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* after cancel_delayed_work_sync we unset info->policy */
558c2ecf20Sopenharmony_ci	BUG_ON(info->policy == NULL);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	target_freq = calc_freq(info);
588c2ecf20Sopenharmony_ci	__cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	delay = usecs_to_jiffies(info->poll_int);
618c2ecf20Sopenharmony_ci	schedule_delayed_work_on(info->policy->cpu, &info->work, delay);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void spu_gov_init_work(struct spu_gov_info_struct *info)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int delay = usecs_to_jiffies(info->poll_int);
678c2ecf20Sopenharmony_ci	INIT_DEFERRABLE_WORK(&info->work, spu_gov_work);
688c2ecf20Sopenharmony_ci	schedule_delayed_work_on(info->policy->cpu, &info->work, delay);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void spu_gov_cancel_work(struct spu_gov_info_struct *info)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&info->work);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int spu_gov_start(struct cpufreq_policy *policy)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	unsigned int cpu = policy->cpu;
798c2ecf20Sopenharmony_ci	struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu);
808c2ecf20Sopenharmony_ci	struct spu_gov_info_struct *affected_info;
818c2ecf20Sopenharmony_ci	int i;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (!cpu_online(cpu)) {
848c2ecf20Sopenharmony_ci		printk(KERN_ERR "cpu %d is not online\n", cpu);
858c2ecf20Sopenharmony_ci		return -EINVAL;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (!policy->cur) {
898c2ecf20Sopenharmony_ci		printk(KERN_ERR "no cpu specified in policy\n");
908c2ecf20Sopenharmony_ci		return -EINVAL;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* initialize spu_gov_info for all affected cpus */
948c2ecf20Sopenharmony_ci	for_each_cpu(i, policy->cpus) {
958c2ecf20Sopenharmony_ci		affected_info = &per_cpu(spu_gov_info, i);
968c2ecf20Sopenharmony_ci		affected_info->policy = policy;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	info->poll_int = POLL_TIME;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* setup timer */
1028c2ecf20Sopenharmony_ci	spu_gov_init_work(info);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic void spu_gov_stop(struct cpufreq_policy *policy)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	unsigned int cpu = policy->cpu;
1108c2ecf20Sopenharmony_ci	struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu);
1118c2ecf20Sopenharmony_ci	int i;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* cancel timer */
1148c2ecf20Sopenharmony_ci	spu_gov_cancel_work(info);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* clean spu_gov_info for all affected cpus */
1178c2ecf20Sopenharmony_ci	for_each_cpu (i, policy->cpus) {
1188c2ecf20Sopenharmony_ci		info = &per_cpu(spu_gov_info, i);
1198c2ecf20Sopenharmony_ci		info->policy = NULL;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic struct cpufreq_governor spu_governor = {
1248c2ecf20Sopenharmony_ci	.name = "spudemand",
1258c2ecf20Sopenharmony_ci	.start = spu_gov_start,
1268c2ecf20Sopenharmony_ci	.stop = spu_gov_stop,
1278c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_cicpufreq_governor_init(spu_governor);
1308c2ecf20Sopenharmony_cicpufreq_governor_exit(spu_governor);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
134