1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/types.h> 3 #include <linux/errno.h> 4 #include <linux/kernel.h> 5 #include <linux/delay.h> 6 #include <linux/pm_qos.h> 7 #include <linux/slab.h> 8 #include <linux/init.h> 9 #include <linux/wait.h> 10 #include <linux/cpu.h> 11 #include <linux/cpufreq.h> 12 13 #include "windfarm.h" 14 15 #define VERSION "0.3" 16 17 static int clamped; 18 static struct wf_control *clamp_control; 19 static struct freq_qos_request qos_req; 20 static unsigned int min_freq, max_freq; 21 22 static int clamp_set(struct wf_control *ct, s32 value) 23 { 24 unsigned int freq; 25 26 if (value) { 27 freq = min_freq; 28 printk(KERN_INFO "windfarm: Clamping CPU frequency to " 29 "minimum !\n"); 30 } else { 31 freq = max_freq; 32 printk(KERN_INFO "windfarm: CPU frequency unclamped !\n"); 33 } 34 clamped = value; 35 36 return freq_qos_update_request(&qos_req, freq); 37 } 38 39 static int clamp_get(struct wf_control *ct, s32 *value) 40 { 41 *value = clamped; 42 return 0; 43 } 44 45 static s32 clamp_min(struct wf_control *ct) 46 { 47 return 0; 48 } 49 50 static s32 clamp_max(struct wf_control *ct) 51 { 52 return 1; 53 } 54 55 static const struct wf_control_ops clamp_ops = { 56 .set_value = clamp_set, 57 .get_value = clamp_get, 58 .get_min = clamp_min, 59 .get_max = clamp_max, 60 .owner = THIS_MODULE, 61 }; 62 63 static int __init wf_cpufreq_clamp_init(void) 64 { 65 struct cpufreq_policy *policy; 66 struct wf_control *clamp; 67 struct device *dev; 68 int ret; 69 70 policy = cpufreq_cpu_get(0); 71 if (!policy) { 72 pr_warn("%s: cpufreq policy not found cpu0\n", __func__); 73 return -EPROBE_DEFER; 74 } 75 76 min_freq = policy->cpuinfo.min_freq; 77 max_freq = policy->cpuinfo.max_freq; 78 79 ret = freq_qos_add_request(&policy->constraints, &qos_req, FREQ_QOS_MAX, 80 max_freq); 81 82 cpufreq_cpu_put(policy); 83 84 if (ret < 0) { 85 pr_err("%s: Failed to add freq constraint (%d)\n", __func__, 86 ret); 87 return ret; 88 } 89 90 dev = get_cpu_device(0); 91 if (unlikely(!dev)) { 92 pr_warn("%s: No cpu device for cpu0\n", __func__); 93 ret = -ENODEV; 94 goto fail; 95 } 96 97 clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); 98 if (clamp == NULL) { 99 ret = -ENOMEM; 100 goto fail; 101 } 102 103 clamp->ops = &clamp_ops; 104 clamp->name = "cpufreq-clamp"; 105 ret = wf_register_control(clamp); 106 if (ret) 107 goto free; 108 109 clamp_control = clamp; 110 return 0; 111 112 free: 113 kfree(clamp); 114 fail: 115 freq_qos_remove_request(&qos_req); 116 return ret; 117 } 118 119 static void __exit wf_cpufreq_clamp_exit(void) 120 { 121 if (clamp_control) { 122 wf_unregister_control(clamp_control); 123 freq_qos_remove_request(&qos_req); 124 } 125 } 126 127 128 module_init(wf_cpufreq_clamp_init); 129 module_exit(wf_cpufreq_clamp_exit); 130 131 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 132 MODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control"); 133 MODULE_LICENSE("GPL"); 134 135