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;
19*3000ce3cSRafael 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
36*3000ce3cSRafael 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;
78*3000ce3cSRafael J. Wysocki
79*3000ce3cSRafael J. Wysocki ret = freq_qos_add_request(&policy->constraints, &qos_req, FREQ_QOS_MAX,
80*3000ce3cSRafael J. Wysocki max_freq);
81*3000ce3cSRafael J. Wysocki
82dce2e3a8SViresh Kumar cpufreq_cpu_put(policy);
83dce2e3a8SViresh Kumar
84*3000ce3cSRafael J. Wysocki if (ret < 0) {
85*3000ce3cSRafael J. Wysocki pr_err("%s: Failed to add freq constraint (%d)\n", __func__,
86*3000ce3cSRafael J. Wysocki ret);
87*3000ce3cSRafael J. Wysocki return ret;
88*3000ce3cSRafael J. Wysocki }
89*3000ce3cSRafael 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__);
93*3000ce3cSRafael J. Wysocki ret = -ENODEV;
94*3000ce3cSRafael J. Wysocki goto fail;
95dce2e3a8SViresh Kumar }
9675722d39SBenjamin Herrenschmidt
9775722d39SBenjamin Herrenschmidt clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
98*3000ce3cSRafael J. Wysocki if (clamp == NULL) {
99*3000ce3cSRafael J. Wysocki ret = -ENOMEM;
100*3000ce3cSRafael 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)
107*3000ce3cSRafael J. Wysocki goto free;
108*3000ce3cSRafael J. Wysocki
10975722d39SBenjamin Herrenschmidt clamp_control = clamp;
11075722d39SBenjamin Herrenschmidt return 0;
111dce2e3a8SViresh Kumar
112dce2e3a8SViresh Kumar free:
11375722d39SBenjamin Herrenschmidt kfree(clamp);
114*3000ce3cSRafael J. Wysocki fail:
115*3000ce3cSRafael 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);
123*3000ce3cSRafael 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