18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/types.h>
38c2ecf20Sopenharmony_ci#include <linux/errno.h>
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/delay.h>
68c2ecf20Sopenharmony_ci#include <linux/pm_qos.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/wait.h>
108c2ecf20Sopenharmony_ci#include <linux/cpu.h>
118c2ecf20Sopenharmony_ci#include <linux/cpufreq.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <asm/prom.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "windfarm.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define VERSION "0.3"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int clamped;
208c2ecf20Sopenharmony_cistatic struct wf_control *clamp_control;
218c2ecf20Sopenharmony_cistatic struct freq_qos_request qos_req;
228c2ecf20Sopenharmony_cistatic unsigned int min_freq, max_freq;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int clamp_set(struct wf_control *ct, s32 value)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	unsigned int freq;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (value) {
298c2ecf20Sopenharmony_ci		freq = min_freq;
308c2ecf20Sopenharmony_ci		printk(KERN_INFO "windfarm: Clamping CPU frequency to "
318c2ecf20Sopenharmony_ci		       "minimum !\n");
328c2ecf20Sopenharmony_ci	} else {
338c2ecf20Sopenharmony_ci		freq = max_freq;
348c2ecf20Sopenharmony_ci		printk(KERN_INFO "windfarm: CPU frequency unclamped !\n");
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	clamped = value;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return freq_qos_update_request(&qos_req, freq);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int clamp_get(struct wf_control *ct, s32 *value)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	*value = clamped;
448c2ecf20Sopenharmony_ci	return 0;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic s32 clamp_min(struct wf_control *ct)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	return 0;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic s32 clamp_max(struct wf_control *ct)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	return 1;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct wf_control_ops clamp_ops = {
588c2ecf20Sopenharmony_ci	.set_value	= clamp_set,
598c2ecf20Sopenharmony_ci	.get_value	= clamp_get,
608c2ecf20Sopenharmony_ci	.get_min	= clamp_min,
618c2ecf20Sopenharmony_ci	.get_max	= clamp_max,
628c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int __init wf_cpufreq_clamp_init(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct cpufreq_policy *policy;
688c2ecf20Sopenharmony_ci	struct wf_control *clamp;
698c2ecf20Sopenharmony_ci	struct device *dev;
708c2ecf20Sopenharmony_ci	int ret;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	policy = cpufreq_cpu_get(0);
738c2ecf20Sopenharmony_ci	if (!policy) {
748c2ecf20Sopenharmony_ci		pr_warn("%s: cpufreq policy not found cpu0\n", __func__);
758c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	min_freq = policy->cpuinfo.min_freq;
798c2ecf20Sopenharmony_ci	max_freq = policy->cpuinfo.max_freq;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ret = freq_qos_add_request(&policy->constraints, &qos_req, FREQ_QOS_MAX,
828c2ecf20Sopenharmony_ci				   max_freq);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	cpufreq_cpu_put(policy);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (ret < 0) {
878c2ecf20Sopenharmony_ci		pr_err("%s: Failed to add freq constraint (%d)\n", __func__,
888c2ecf20Sopenharmony_ci		       ret);
898c2ecf20Sopenharmony_ci		return ret;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	dev = get_cpu_device(0);
938c2ecf20Sopenharmony_ci	if (unlikely(!dev)) {
948c2ecf20Sopenharmony_ci		pr_warn("%s: No cpu device for cpu0\n", __func__);
958c2ecf20Sopenharmony_ci		ret = -ENODEV;
968c2ecf20Sopenharmony_ci		goto fail;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
1008c2ecf20Sopenharmony_ci	if (clamp == NULL) {
1018c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1028c2ecf20Sopenharmony_ci		goto fail;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	clamp->ops = &clamp_ops;
1068c2ecf20Sopenharmony_ci	clamp->name = "cpufreq-clamp";
1078c2ecf20Sopenharmony_ci	ret = wf_register_control(clamp);
1088c2ecf20Sopenharmony_ci	if (ret)
1098c2ecf20Sopenharmony_ci		goto free;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	clamp_control = clamp;
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci free:
1158c2ecf20Sopenharmony_ci	kfree(clamp);
1168c2ecf20Sopenharmony_ci fail:
1178c2ecf20Sopenharmony_ci	freq_qos_remove_request(&qos_req);
1188c2ecf20Sopenharmony_ci	return ret;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void __exit wf_cpufreq_clamp_exit(void)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	if (clamp_control) {
1248c2ecf20Sopenharmony_ci		wf_unregister_control(clamp_control);
1258c2ecf20Sopenharmony_ci		freq_qos_remove_request(&qos_req);
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cimodule_init(wf_cpufreq_clamp_init);
1318c2ecf20Sopenharmony_cimodule_exit(wf_cpufreq_clamp_exit);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
1348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control");
1358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1368c2ecf20Sopenharmony_ci
137