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(req: &qos_req, new_value: 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(cpu: 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(qos: &policy->constraints, req: &qos_req, type: FREQ_QOS_MAX, |
80 | value: 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(cpu: 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(size: 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(ct: clamp); |
106 | if (ret) |
107 | goto free; |
108 | |
109 | clamp_control = clamp; |
110 | return 0; |
111 | |
112 | free: |
113 | kfree(objp: clamp); |
114 | fail: |
115 | freq_qos_remove_request(req: &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(ct: clamp_control); |
123 | freq_qos_remove_request(req: &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 | |