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 dev_pm_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 dev_pm_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 cpufreq_cpu_put(policy); 81 82 dev = get_cpu_device(0); 83 if (unlikely(!dev)) { 84 pr_warn("%s: No cpu device for cpu0\n", __func__); 85 return -ENODEV; 86 } 87 88 clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); 89 if (clamp == NULL) 90 return -ENOMEM; 91 92 ret = dev_pm_qos_add_request(dev, &qos_req, DEV_PM_QOS_MAX_FREQUENCY, 93 max_freq); 94 if (ret < 0) { 95 pr_err("%s: Failed to add freq constraint (%d)\n", __func__, 96 ret); 97 goto free; 98 } 99 100 clamp->ops = &clamp_ops; 101 clamp->name = "cpufreq-clamp"; 102 ret = wf_register_control(clamp); 103 if (ret) 104 goto fail; 105 clamp_control = clamp; 106 return 0; 107 fail: 108 dev_pm_qos_remove_request(&qos_req); 109 110 free: 111 kfree(clamp); 112 return ret; 113 } 114 115 static void __exit wf_cpufreq_clamp_exit(void) 116 { 117 if (clamp_control) { 118 wf_unregister_control(clamp_control); 119 dev_pm_qos_remove_request(&qos_req); 120 } 121 } 122 123 124 module_init(wf_cpufreq_clamp_init); 125 module_exit(wf_cpufreq_clamp_exit); 126 127 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 128 MODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control"); 129 MODULE_LICENSE("GPL"); 130 131