// SPDX-License-Identifier: GPL-2.0-only #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/pm_qos.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/cpu.h> #include <linux/cpufreq.h> #include "windfarm.h" #define VERSION "0.3" static int clamped; static struct wf_control *clamp_control; static struct freq_qos_request qos_req; static unsigned int min_freq, max_freq; static int clamp_set(struct wf_control *ct, s32 value) { unsigned int freq; if (value) { freq = min_freq; printk(KERN_INFO "windfarm: Clamping CPU frequency to " "minimum !\n"); } else { freq = max_freq; printk(KERN_INFO "windfarm: CPU frequency unclamped !\n"); } clamped = value; return freq_qos_update_request(&qos_req, freq); } static int clamp_get(struct wf_control *ct, s32 *value) { *value = clamped; return 0; } static s32 clamp_min(struct wf_control *ct) { return 0; } static s32 clamp_max(struct wf_control *ct) { return 1; } static const struct wf_control_ops clamp_ops = { .set_value = clamp_set, .get_value = clamp_get, .get_min = clamp_min, .get_max = clamp_max, .owner = THIS_MODULE, }; static int __init wf_cpufreq_clamp_init(void) { struct cpufreq_policy *policy; struct wf_control *clamp; struct device *dev; int ret; policy = cpufreq_cpu_get(0); if (!policy) { pr_warn("%s: cpufreq policy not found cpu0\n", __func__); return -EPROBE_DEFER; } min_freq = policy->cpuinfo.min_freq; max_freq = policy->cpuinfo.max_freq; ret = freq_qos_add_request(&policy->constraints, &qos_req, FREQ_QOS_MAX, max_freq); cpufreq_cpu_put(policy); if (ret < 0) { pr_err("%s: Failed to add freq constraint (%d)\n", __func__, ret); return ret; } dev = get_cpu_device(0); if (unlikely(!dev)) { pr_warn("%s: No cpu device for cpu0\n", __func__); ret = -ENODEV; goto fail; } clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); if (clamp == NULL) { ret = -ENOMEM; goto fail; } clamp->ops = &clamp_ops; clamp->name = "cpufreq-clamp"; ret = wf_register_control(clamp); if (ret) goto free; clamp_control = clamp; return 0; free: kfree(clamp); fail: freq_qos_remove_request(&qos_req); return ret; } static void __exit wf_cpufreq_clamp_exit(void) { if (clamp_control) { wf_unregister_control(clamp_control); freq_qos_remove_request(&qos_req); } } module_init(wf_cpufreq_clamp_init); module_exit(wf_cpufreq_clamp_exit); MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); MODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control"); MODULE_LICENSE("GPL");