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 19static int clamped; 20static struct wf_control *clamp_control; 21static struct freq_qos_request qos_req; 22static unsigned int min_freq, max_freq; 23 24static 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 41static int clamp_get(struct wf_control *ct, s32 *value) 42{ 43 *value = clamped; 44 return 0; 45} 46 47static s32 clamp_min(struct wf_control *ct) 48{ 49 return 0; 50} 51 52static s32 clamp_max(struct wf_control *ct) 53{ 54 return 1; 55} 56 57static 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 65static 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 121static 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 130module_init(wf_cpufreq_clamp_init); 131module_exit(wf_cpufreq_clamp_exit); 132 133MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 134MODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control"); 135MODULE_LICENSE("GPL"); 136 137