aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/macintosh/windfarm_cpufreq_clamp.c
blob: 705c6200814b633abf9794b044f3a7b97aa7d269 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>

#include <asm/prom.h>

#include "windfarm.h"

#define VERSION "0.3"

static int clamped;
static struct wf_control *clamp_control;
static struct dev_pm_qos_request qos_req;
static unsigned int min_freq, max_freq;

static int clamp_set(struct wf_control *ct, s32 value)
{
	unsigned int freq;

	if (value) {
		freq = min_freq;
		printk(KERN_INFO "windfarm: Clamping CPU frequency to "
		       "minimum !\n");
	} else {
		freq = max_freq;
		printk(KERN_INFO "windfarm: CPU frequency unclamped !\n");
	}
	clamped = value;

	return dev_pm_qos_update_request(&qos_req, freq);
}

static int clamp_get(struct wf_control *ct, s32 *value)
{
	*value = clamped;
	return 0;
}

static s32 clamp_min(struct wf_control *ct)
{
	return 0;
}

static s32 clamp_max(struct wf_control *ct)
{
	return 1;
}

static const struct wf_control_ops clamp_ops = {
	.set_value	= clamp_set,
	.get_value	= clamp_get,
	.get_min	= clamp_min,
	.get_max	= clamp_max,
	.owner		= THIS_MODULE,
};

static int __init wf_cpufreq_clamp_init(void)
{
	struct cpufreq_policy *policy;
	struct wf_control *clamp;
	struct device *dev;
	int ret;

	policy = cpufreq_cpu_get(0);
	if (!policy) {
		pr_warn("%s: cpufreq policy not found cpu0\n", __func__);
		return -EPROBE_DEFER;
	}

	min_freq = policy->cpuinfo.min_freq;
	max_freq = policy->cpuinfo.max_freq;
	cpufreq_cpu_put(policy);

	dev = get_cpu_device(0);
	if (unlikely(!dev)) {
		pr_warn("%s: No cpu device for cpu0\n", __func__);
		return -ENODEV;
	}

	clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
	if (clamp == NULL)
		return -ENOMEM;

	ret = dev_pm_qos_add_request(dev, &qos_req, DEV_PM_QOS_MAX_FREQUENCY,
				     max_freq);
	if (ret < 0) {
		pr_err("%s: Failed to add freq constraint (%d)\n", __func__,
		       ret);
		goto free;
	}

	clamp->ops = &clamp_ops;
	clamp->name = "cpufreq-clamp";
	ret = wf_register_control(clamp);
	if (ret)
		goto fail;
	clamp_control = clamp;
	return 0;
 fail:
	dev_pm_qos_remove_request(&qos_req);

 free:
	kfree(clamp);
	return ret;
}

static void __exit wf_cpufreq_clamp_exit(void)
{
	if (clamp_control) {
		wf_unregister_control(clamp_control);
		dev_pm_qos_remove_request(&qos_req);
	}
}


module_init(wf_cpufreq_clamp_init);
module_exit(wf_cpufreq_clamp_exit);

MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("CPU frequency clamp for PowerMacs thermal control");
MODULE_LICENSE("GPL");