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