109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
275722d39SBenjamin Herrenschmidt #include <linux/types.h>
375722d39SBenjamin Herrenschmidt #include <linux/errno.h>
475722d39SBenjamin Herrenschmidt #include <linux/kernel.h>
575722d39SBenjamin Herrenschmidt #include <linux/delay.h>
6dce2e3a8SViresh Kumar #include <linux/pm_qos.h>
775722d39SBenjamin Herrenschmidt #include <linux/slab.h>
875722d39SBenjamin Herrenschmidt #include <linux/init.h>
975722d39SBenjamin Herrenschmidt #include <linux/wait.h>
10dce2e3a8SViresh Kumar #include <linux/cpu.h>
1175722d39SBenjamin Herrenschmidt #include <linux/cpufreq.h>
1275722d39SBenjamin Herrenschmidt 
1375722d39SBenjamin Herrenschmidt #include "windfarm.h"
1475722d39SBenjamin Herrenschmidt 
1575722d39SBenjamin Herrenschmidt #define VERSION "0.3"
1675722d39SBenjamin Herrenschmidt 
1775722d39SBenjamin Herrenschmidt static int clamped;
1875722d39SBenjamin Herrenschmidt static struct wf_control *clamp_control;
193000ce3cSRafael J. Wysocki static struct freq_qos_request qos_req;
20dce2e3a8SViresh Kumar static unsigned int min_freq, max_freq;
2175722d39SBenjamin Herrenschmidt 
clamp_set(struct wf_control * ct,s32 value)2275722d39SBenjamin Herrenschmidt static int clamp_set(struct wf_control *ct, s32 value)
2375722d39SBenjamin Herrenschmidt {
24dce2e3a8SViresh Kumar 	unsigned int freq;
25dce2e3a8SViresh Kumar 
26dce2e3a8SViresh Kumar 	if (value) {
27dce2e3a8SViresh Kumar 		freq = min_freq;
2875722d39SBenjamin Herrenschmidt 		printk(KERN_INFO "windfarm: Clamping CPU frequency to "
2975722d39SBenjamin Herrenschmidt 		       "minimum !\n");
30dce2e3a8SViresh Kumar 	} else {
31dce2e3a8SViresh Kumar 		freq = max_freq;
3275722d39SBenjamin Herrenschmidt 		printk(KERN_INFO "windfarm: CPU frequency unclamped !\n");
33dce2e3a8SViresh Kumar 	}
3475722d39SBenjamin Herrenschmidt 	clamped = value;
35dce2e3a8SViresh Kumar 
363000ce3cSRafael J. Wysocki 	return freq_qos_update_request(&qos_req, freq);
3775722d39SBenjamin Herrenschmidt }
3875722d39SBenjamin Herrenschmidt 
clamp_get(struct wf_control * ct,s32 * value)3975722d39SBenjamin Herrenschmidt static int clamp_get(struct wf_control *ct, s32 *value)
4075722d39SBenjamin Herrenschmidt {
4175722d39SBenjamin Herrenschmidt 	*value = clamped;
4275722d39SBenjamin Herrenschmidt 	return 0;
4375722d39SBenjamin Herrenschmidt }
4475722d39SBenjamin Herrenschmidt 
clamp_min(struct wf_control * ct)4575722d39SBenjamin Herrenschmidt static s32 clamp_min(struct wf_control *ct)
4675722d39SBenjamin Herrenschmidt {
4775722d39SBenjamin Herrenschmidt 	return 0;
4875722d39SBenjamin Herrenschmidt }
4975722d39SBenjamin Herrenschmidt 
clamp_max(struct wf_control * ct)5075722d39SBenjamin Herrenschmidt static s32 clamp_max(struct wf_control *ct)
5175722d39SBenjamin Herrenschmidt {
5275722d39SBenjamin Herrenschmidt 	return 1;
5375722d39SBenjamin Herrenschmidt }
5475722d39SBenjamin Herrenschmidt 
551ad35f6eSBhumika Goyal static const struct wf_control_ops clamp_ops = {
5675722d39SBenjamin Herrenschmidt 	.set_value	= clamp_set,
5775722d39SBenjamin Herrenschmidt 	.get_value	= clamp_get,
5875722d39SBenjamin Herrenschmidt 	.get_min	= clamp_min,
5975722d39SBenjamin Herrenschmidt 	.get_max	= clamp_max,
6075722d39SBenjamin Herrenschmidt 	.owner		= THIS_MODULE,
6175722d39SBenjamin Herrenschmidt };
6275722d39SBenjamin Herrenschmidt 
wf_cpufreq_clamp_init(void)6375722d39SBenjamin Herrenschmidt static int __init wf_cpufreq_clamp_init(void)
6475722d39SBenjamin Herrenschmidt {
65dce2e3a8SViresh Kumar 	struct cpufreq_policy *policy;
6675722d39SBenjamin Herrenschmidt 	struct wf_control *clamp;
67dce2e3a8SViresh Kumar 	struct device *dev;
68dce2e3a8SViresh Kumar 	int ret;
69dce2e3a8SViresh Kumar 
70dce2e3a8SViresh Kumar 	policy = cpufreq_cpu_get(0);
71dce2e3a8SViresh Kumar 	if (!policy) {
72dce2e3a8SViresh Kumar 		pr_warn("%s: cpufreq policy not found cpu0\n", __func__);
73dce2e3a8SViresh Kumar 		return -EPROBE_DEFER;
74dce2e3a8SViresh Kumar 	}
75dce2e3a8SViresh Kumar 
76dce2e3a8SViresh Kumar 	min_freq = policy->cpuinfo.min_freq;
77dce2e3a8SViresh Kumar 	max_freq = policy->cpuinfo.max_freq;
783000ce3cSRafael J. Wysocki 
793000ce3cSRafael J. Wysocki 	ret = freq_qos_add_request(&policy->constraints, &qos_req, FREQ_QOS_MAX,
803000ce3cSRafael J. Wysocki 				   max_freq);
813000ce3cSRafael J. Wysocki 
82dce2e3a8SViresh Kumar 	cpufreq_cpu_put(policy);
83dce2e3a8SViresh Kumar 
843000ce3cSRafael J. Wysocki 	if (ret < 0) {
853000ce3cSRafael J. Wysocki 		pr_err("%s: Failed to add freq constraint (%d)\n", __func__,
863000ce3cSRafael J. Wysocki 		       ret);
873000ce3cSRafael J. Wysocki 		return ret;
883000ce3cSRafael J. Wysocki 	}
893000ce3cSRafael J. Wysocki 
90dce2e3a8SViresh Kumar 	dev = get_cpu_device(0);
91dce2e3a8SViresh Kumar 	if (unlikely(!dev)) {
92dce2e3a8SViresh Kumar 		pr_warn("%s: No cpu device for cpu0\n", __func__);
933000ce3cSRafael J. Wysocki 		ret = -ENODEV;
943000ce3cSRafael J. Wysocki 		goto fail;
95dce2e3a8SViresh Kumar 	}
9675722d39SBenjamin Herrenschmidt 
9775722d39SBenjamin Herrenschmidt 	clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
983000ce3cSRafael J. Wysocki 	if (clamp == NULL) {
993000ce3cSRafael J. Wysocki 		ret = -ENOMEM;
1003000ce3cSRafael J. Wysocki 		goto fail;
101dce2e3a8SViresh Kumar 	}
102dce2e3a8SViresh Kumar 
10375722d39SBenjamin Herrenschmidt 	clamp->ops = &clamp_ops;
10475722d39SBenjamin Herrenschmidt 	clamp->name = "cpufreq-clamp";
105dce2e3a8SViresh Kumar 	ret = wf_register_control(clamp);
106dce2e3a8SViresh Kumar 	if (ret)
1073000ce3cSRafael J. Wysocki 		goto free;
1083000ce3cSRafael J. Wysocki 
10975722d39SBenjamin Herrenschmidt 	clamp_control = clamp;
11075722d39SBenjamin Herrenschmidt 	return 0;
111dce2e3a8SViresh Kumar 
112dce2e3a8SViresh Kumar  free:
11375722d39SBenjamin Herrenschmidt 	kfree(clamp);
1143000ce3cSRafael J. Wysocki  fail:
1153000ce3cSRafael J. Wysocki 	freq_qos_remove_request(&qos_req);
116dce2e3a8SViresh Kumar 	return ret;
11775722d39SBenjamin Herrenschmidt }
11875722d39SBenjamin Herrenschmidt 
wf_cpufreq_clamp_exit(void)11975722d39SBenjamin Herrenschmidt static void __exit wf_cpufreq_clamp_exit(void)
12075722d39SBenjamin Herrenschmidt {
121dce2e3a8SViresh Kumar 	if (clamp_control) {
12275722d39SBenjamin Herrenschmidt 		wf_unregister_control(clamp_control);
1233000ce3cSRafael J. Wysocki 		freq_qos_remove_request(&qos_req);
124dce2e3a8SViresh Kumar 	}
12575722d39SBenjamin Herrenschmidt }
12675722d39SBenjamin Herrenschmidt 
12775722d39SBenjamin Herrenschmidt 
12875722d39SBenjamin Herrenschmidt module_init(wf_cpufreq_clamp_init);
12975722d39SBenjamin Herrenschmidt module_exit(wf_cpufreq_clamp_exit);
13075722d39SBenjamin Herrenschmidt 
13175722d39SBenjamin Herrenschmidt MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
13275722d39SBenjamin Herrenschmidt MODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control");
13375722d39SBenjamin Herrenschmidt MODULE_LICENSE("GPL");
13475722d39SBenjamin Herrenschmidt 
135