Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/devfreq/governor_userspace.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* linux/drivers/devfreq/governor_userspace.c
4
*
5
* Copyright (C) 2011 Samsung Electronics
6
* MyungJoo Ham <[email protected]>
7
*/
8
9
#include <linux/slab.h>
10
#include <linux/device.h>
11
#include <linux/devfreq.h>
12
#include <linux/kstrtox.h>
13
#include <linux/pm.h>
14
#include <linux/mutex.h>
15
#include <linux/module.h>
16
#include "governor.h"
17
18
struct userspace_data {
19
unsigned long user_frequency;
20
bool valid;
21
};
22
23
static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
24
{
25
struct userspace_data *data = df->governor_data;
26
27
if (data->valid)
28
*freq = data->user_frequency;
29
else
30
*freq = df->previous_freq; /* No user freq specified yet */
31
32
return 0;
33
}
34
35
static ssize_t set_freq_store(struct device *dev, struct device_attribute *attr,
36
const char *buf, size_t count)
37
{
38
struct devfreq *devfreq = to_devfreq(dev);
39
struct userspace_data *data;
40
unsigned long wanted;
41
int err = 0;
42
43
err = kstrtoul(buf, 0, &wanted);
44
if (err)
45
return err;
46
47
mutex_lock(&devfreq->lock);
48
data = devfreq->governor_data;
49
50
data->user_frequency = wanted;
51
data->valid = true;
52
err = update_devfreq(devfreq);
53
if (err == 0)
54
err = count;
55
mutex_unlock(&devfreq->lock);
56
return err;
57
}
58
59
static ssize_t set_freq_show(struct device *dev,
60
struct device_attribute *attr, char *buf)
61
{
62
struct devfreq *devfreq = to_devfreq(dev);
63
struct userspace_data *data;
64
int err = 0;
65
66
mutex_lock(&devfreq->lock);
67
data = devfreq->governor_data;
68
69
if (data->valid)
70
err = sprintf(buf, "%lu\n", data->user_frequency);
71
else
72
err = sprintf(buf, "undefined\n");
73
mutex_unlock(&devfreq->lock);
74
return err;
75
}
76
77
static DEVICE_ATTR_RW(set_freq);
78
static struct attribute *dev_entries[] = {
79
&dev_attr_set_freq.attr,
80
NULL,
81
};
82
static const struct attribute_group dev_attr_group = {
83
.name = DEVFREQ_GOV_USERSPACE,
84
.attrs = dev_entries,
85
};
86
87
static int userspace_init(struct devfreq *devfreq)
88
{
89
int err = 0;
90
struct userspace_data *data = kzalloc(sizeof(struct userspace_data),
91
GFP_KERNEL);
92
93
if (!data) {
94
err = -ENOMEM;
95
goto out;
96
}
97
data->valid = false;
98
devfreq->governor_data = data;
99
100
err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
101
out:
102
return err;
103
}
104
105
static void userspace_exit(struct devfreq *devfreq)
106
{
107
/*
108
* Remove the sysfs entry, unless this is being called after
109
* device_del(), which should have done this already via kobject_del().
110
*/
111
if (devfreq->dev.kobj.sd)
112
sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
113
114
kfree(devfreq->governor_data);
115
devfreq->governor_data = NULL;
116
}
117
118
static int devfreq_userspace_handler(struct devfreq *devfreq,
119
unsigned int event, void *data)
120
{
121
int ret = 0;
122
123
switch (event) {
124
case DEVFREQ_GOV_START:
125
ret = userspace_init(devfreq);
126
break;
127
case DEVFREQ_GOV_STOP:
128
userspace_exit(devfreq);
129
break;
130
default:
131
break;
132
}
133
134
return ret;
135
}
136
137
static struct devfreq_governor devfreq_userspace = {
138
.name = DEVFREQ_GOV_USERSPACE,
139
.get_target_freq = devfreq_userspace_func,
140
.event_handler = devfreq_userspace_handler,
141
};
142
143
static int __init devfreq_userspace_init(void)
144
{
145
return devfreq_add_governor(&devfreq_userspace);
146
}
147
subsys_initcall(devfreq_userspace_init);
148
149
static void __exit devfreq_userspace_exit(void)
150
{
151
int ret;
152
153
ret = devfreq_remove_governor(&devfreq_userspace);
154
if (ret)
155
pr_err("%s: failed remove governor %d\n", __func__, ret);
156
157
return;
158
}
159
module_exit(devfreq_userspace_exit);
160
MODULE_DESCRIPTION("DEVFREQ Userspace governor");
161
MODULE_LICENSE("GPL");
162
163