1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ce26c5bbSMyungJoo Ham /*
3c701335eSChanwoo Choi * linux/drivers/devfreq/governor_userspace.c
4ce26c5bbSMyungJoo Ham *
5ce26c5bbSMyungJoo Ham * Copyright (C) 2011 Samsung Electronics
6ce26c5bbSMyungJoo Ham * MyungJoo Ham <myungjoo.ham@samsung.com>
7ce26c5bbSMyungJoo Ham */
8ce26c5bbSMyungJoo Ham
9ce26c5bbSMyungJoo Ham #include <linux/slab.h>
10ce26c5bbSMyungJoo Ham #include <linux/device.h>
11ce26c5bbSMyungJoo Ham #include <linux/devfreq.h>
12ce26c5bbSMyungJoo Ham #include <linux/pm.h>
13ce26c5bbSMyungJoo Ham #include <linux/mutex.h>
14eff607fdSNishanth Menon #include <linux/module.h>
15ce26c5bbSMyungJoo Ham #include "governor.h"
16ce26c5bbSMyungJoo Ham
17ce26c5bbSMyungJoo Ham struct userspace_data {
18ce26c5bbSMyungJoo Ham unsigned long user_frequency;
19ce26c5bbSMyungJoo Ham bool valid;
20ce26c5bbSMyungJoo Ham };
21ce26c5bbSMyungJoo Ham
devfreq_userspace_func(struct devfreq * df,unsigned long * freq)22ce26c5bbSMyungJoo Ham static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
23ce26c5bbSMyungJoo Ham {
24*5fdded84SKant Fan struct userspace_data *data = df->governor_data;
25ce26c5bbSMyungJoo Ham
266ff66e2aSMatthias Kaehlcke if (data->valid)
276ff66e2aSMatthias Kaehlcke *freq = data->user_frequency;
286ff66e2aSMatthias Kaehlcke else
29ce26c5bbSMyungJoo Ham *freq = df->previous_freq; /* No user freq specified yet */
306ff66e2aSMatthias Kaehlcke
31ce26c5bbSMyungJoo Ham return 0;
32ce26c5bbSMyungJoo Ham }
33ce26c5bbSMyungJoo Ham
set_freq_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)341ad4f329SYueHaibing static ssize_t set_freq_store(struct device *dev, struct device_attribute *attr,
35ce26c5bbSMyungJoo Ham const char *buf, size_t count)
36ce26c5bbSMyungJoo Ham {
37ce26c5bbSMyungJoo Ham struct devfreq *devfreq = to_devfreq(dev);
38ce26c5bbSMyungJoo Ham struct userspace_data *data;
39ce26c5bbSMyungJoo Ham unsigned long wanted;
40ce26c5bbSMyungJoo Ham int err = 0;
41ce26c5bbSMyungJoo Ham
42ce26c5bbSMyungJoo Ham mutex_lock(&devfreq->lock);
43*5fdded84SKant Fan data = devfreq->governor_data;
44ce26c5bbSMyungJoo Ham
45ce26c5bbSMyungJoo Ham sscanf(buf, "%lu", &wanted);
46ce26c5bbSMyungJoo Ham data->user_frequency = wanted;
47ce26c5bbSMyungJoo Ham data->valid = true;
48ce26c5bbSMyungJoo Ham err = update_devfreq(devfreq);
49ce26c5bbSMyungJoo Ham if (err == 0)
50ce26c5bbSMyungJoo Ham err = count;
51ce26c5bbSMyungJoo Ham mutex_unlock(&devfreq->lock);
52ce26c5bbSMyungJoo Ham return err;
53ce26c5bbSMyungJoo Ham }
54ce26c5bbSMyungJoo Ham
set_freq_show(struct device * dev,struct device_attribute * attr,char * buf)551ad4f329SYueHaibing static ssize_t set_freq_show(struct device *dev,
561ad4f329SYueHaibing struct device_attribute *attr, char *buf)
57ce26c5bbSMyungJoo Ham {
58ce26c5bbSMyungJoo Ham struct devfreq *devfreq = to_devfreq(dev);
59ce26c5bbSMyungJoo Ham struct userspace_data *data;
60ce26c5bbSMyungJoo Ham int err = 0;
61ce26c5bbSMyungJoo Ham
62ce26c5bbSMyungJoo Ham mutex_lock(&devfreq->lock);
63*5fdded84SKant Fan data = devfreq->governor_data;
64ce26c5bbSMyungJoo Ham
65ce26c5bbSMyungJoo Ham if (data->valid)
66ce26c5bbSMyungJoo Ham err = sprintf(buf, "%lu\n", data->user_frequency);
67ce26c5bbSMyungJoo Ham else
68ce26c5bbSMyungJoo Ham err = sprintf(buf, "undefined\n");
69ce26c5bbSMyungJoo Ham mutex_unlock(&devfreq->lock);
70ce26c5bbSMyungJoo Ham return err;
71ce26c5bbSMyungJoo Ham }
72ce26c5bbSMyungJoo Ham
731ad4f329SYueHaibing static DEVICE_ATTR_RW(set_freq);
74ce26c5bbSMyungJoo Ham static struct attribute *dev_entries[] = {
75ce26c5bbSMyungJoo Ham &dev_attr_set_freq.attr,
76ce26c5bbSMyungJoo Ham NULL,
77ce26c5bbSMyungJoo Ham };
7837d644aaSArvind Yadav static const struct attribute_group dev_attr_group = {
79aa7c352fSChanwoo Choi .name = DEVFREQ_GOV_USERSPACE,
80ce26c5bbSMyungJoo Ham .attrs = dev_entries,
81ce26c5bbSMyungJoo Ham };
82ce26c5bbSMyungJoo Ham
userspace_init(struct devfreq * devfreq)83ce26c5bbSMyungJoo Ham static int userspace_init(struct devfreq *devfreq)
84ce26c5bbSMyungJoo Ham {
85ce26c5bbSMyungJoo Ham int err = 0;
86ce26c5bbSMyungJoo Ham struct userspace_data *data = kzalloc(sizeof(struct userspace_data),
87ce26c5bbSMyungJoo Ham GFP_KERNEL);
88ce26c5bbSMyungJoo Ham
89ce26c5bbSMyungJoo Ham if (!data) {
90ce26c5bbSMyungJoo Ham err = -ENOMEM;
91ce26c5bbSMyungJoo Ham goto out;
92ce26c5bbSMyungJoo Ham }
93ce26c5bbSMyungJoo Ham data->valid = false;
94*5fdded84SKant Fan devfreq->governor_data = data;
95ce26c5bbSMyungJoo Ham
96ce26c5bbSMyungJoo Ham err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
97ce26c5bbSMyungJoo Ham out:
98ce26c5bbSMyungJoo Ham return err;
99ce26c5bbSMyungJoo Ham }
100ce26c5bbSMyungJoo Ham
userspace_exit(struct devfreq * devfreq)101ce26c5bbSMyungJoo Ham static void userspace_exit(struct devfreq *devfreq)
102ce26c5bbSMyungJoo Ham {
103924b9111SChris Diamand /*
104924b9111SChris Diamand * Remove the sysfs entry, unless this is being called after
105924b9111SChris Diamand * device_del(), which should have done this already via kobject_del().
106924b9111SChris Diamand */
107924b9111SChris Diamand if (devfreq->dev.kobj.sd)
108ce26c5bbSMyungJoo Ham sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
109924b9111SChris Diamand
110*5fdded84SKant Fan kfree(devfreq->governor_data);
111*5fdded84SKant Fan devfreq->governor_data = NULL;
112ce26c5bbSMyungJoo Ham }
113ce26c5bbSMyungJoo Ham
devfreq_userspace_handler(struct devfreq * devfreq,unsigned int event,void * data)1147e6fdd4bSRajagopal Venkat static int devfreq_userspace_handler(struct devfreq *devfreq,
1157e6fdd4bSRajagopal Venkat unsigned int event, void *data)
1167e6fdd4bSRajagopal Venkat {
1177e6fdd4bSRajagopal Venkat int ret = 0;
1187e6fdd4bSRajagopal Venkat
1197e6fdd4bSRajagopal Venkat switch (event) {
1207e6fdd4bSRajagopal Venkat case DEVFREQ_GOV_START:
1217e6fdd4bSRajagopal Venkat ret = userspace_init(devfreq);
1227e6fdd4bSRajagopal Venkat break;
1237e6fdd4bSRajagopal Venkat case DEVFREQ_GOV_STOP:
1247e6fdd4bSRajagopal Venkat userspace_exit(devfreq);
1257e6fdd4bSRajagopal Venkat break;
1267e6fdd4bSRajagopal Venkat default:
1277e6fdd4bSRajagopal Venkat break;
1287e6fdd4bSRajagopal Venkat }
1297e6fdd4bSRajagopal Venkat
1307e6fdd4bSRajagopal Venkat return ret;
1317e6fdd4bSRajagopal Venkat }
1327e6fdd4bSRajagopal Venkat
1331b5c1be2SNishanth Menon static struct devfreq_governor devfreq_userspace = {
1344bb1faacSpierre Kuo .name = DEVFREQ_GOV_USERSPACE,
135ce26c5bbSMyungJoo Ham .get_target_freq = devfreq_userspace_func,
1367e6fdd4bSRajagopal Venkat .event_handler = devfreq_userspace_handler,
137ce26c5bbSMyungJoo Ham };
13883116e66SNishanth Menon
devfreq_userspace_init(void)13983116e66SNishanth Menon static int __init devfreq_userspace_init(void)
14083116e66SNishanth Menon {
14183116e66SNishanth Menon return devfreq_add_governor(&devfreq_userspace);
14283116e66SNishanth Menon }
14383116e66SNishanth Menon subsys_initcall(devfreq_userspace_init);
14483116e66SNishanth Menon
devfreq_userspace_exit(void)14583116e66SNishanth Menon static void __exit devfreq_userspace_exit(void)
14683116e66SNishanth Menon {
14783116e66SNishanth Menon int ret;
14883116e66SNishanth Menon
14983116e66SNishanth Menon ret = devfreq_remove_governor(&devfreq_userspace);
15083116e66SNishanth Menon if (ret)
15183116e66SNishanth Menon pr_err("%s: failed remove governor %d\n", __func__, ret);
15283116e66SNishanth Menon
15383116e66SNishanth Menon return;
15483116e66SNishanth Menon }
15583116e66SNishanth Menon module_exit(devfreq_userspace_exit);
156eff607fdSNishanth Menon MODULE_LICENSE("GPL");
157