Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  linux/drivers/devfreq/governor_userspace.c
0004  *
0005  *  Copyright (C) 2011 Samsung Electronics
0006  *  MyungJoo Ham <myungjoo.ham@samsung.com>
0007  */
0008 
0009 #include <linux/slab.h>
0010 #include <linux/device.h>
0011 #include <linux/devfreq.h>
0012 #include <linux/pm.h>
0013 #include <linux/mutex.h>
0014 #include <linux/module.h>
0015 #include "governor.h"
0016 
0017 struct userspace_data {
0018     unsigned long user_frequency;
0019     bool valid;
0020 };
0021 
0022 static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
0023 {
0024     struct userspace_data *data = df->data;
0025 
0026     if (data->valid)
0027         *freq = data->user_frequency;
0028     else
0029         *freq = df->previous_freq; /* No user freq specified yet */
0030 
0031     return 0;
0032 }
0033 
0034 static ssize_t set_freq_store(struct device *dev, struct device_attribute *attr,
0035                   const char *buf, size_t count)
0036 {
0037     struct devfreq *devfreq = to_devfreq(dev);
0038     struct userspace_data *data;
0039     unsigned long wanted;
0040     int err = 0;
0041 
0042     mutex_lock(&devfreq->lock);
0043     data = devfreq->data;
0044 
0045     sscanf(buf, "%lu", &wanted);
0046     data->user_frequency = wanted;
0047     data->valid = true;
0048     err = update_devfreq(devfreq);
0049     if (err == 0)
0050         err = count;
0051     mutex_unlock(&devfreq->lock);
0052     return err;
0053 }
0054 
0055 static ssize_t set_freq_show(struct device *dev,
0056                  struct device_attribute *attr, char *buf)
0057 {
0058     struct devfreq *devfreq = to_devfreq(dev);
0059     struct userspace_data *data;
0060     int err = 0;
0061 
0062     mutex_lock(&devfreq->lock);
0063     data = devfreq->data;
0064 
0065     if (data->valid)
0066         err = sprintf(buf, "%lu\n", data->user_frequency);
0067     else
0068         err = sprintf(buf, "undefined\n");
0069     mutex_unlock(&devfreq->lock);
0070     return err;
0071 }
0072 
0073 static DEVICE_ATTR_RW(set_freq);
0074 static struct attribute *dev_entries[] = {
0075     &dev_attr_set_freq.attr,
0076     NULL,
0077 };
0078 static const struct attribute_group dev_attr_group = {
0079     .name   = DEVFREQ_GOV_USERSPACE,
0080     .attrs  = dev_entries,
0081 };
0082 
0083 static int userspace_init(struct devfreq *devfreq)
0084 {
0085     int err = 0;
0086     struct userspace_data *data = kzalloc(sizeof(struct userspace_data),
0087                           GFP_KERNEL);
0088 
0089     if (!data) {
0090         err = -ENOMEM;
0091         goto out;
0092     }
0093     data->valid = false;
0094     devfreq->data = data;
0095 
0096     err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
0097 out:
0098     return err;
0099 }
0100 
0101 static void userspace_exit(struct devfreq *devfreq)
0102 {
0103     /*
0104      * Remove the sysfs entry, unless this is being called after
0105      * device_del(), which should have done this already via kobject_del().
0106      */
0107     if (devfreq->dev.kobj.sd)
0108         sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
0109 
0110     kfree(devfreq->data);
0111     devfreq->data = NULL;
0112 }
0113 
0114 static int devfreq_userspace_handler(struct devfreq *devfreq,
0115             unsigned int event, void *data)
0116 {
0117     int ret = 0;
0118 
0119     switch (event) {
0120     case DEVFREQ_GOV_START:
0121         ret = userspace_init(devfreq);
0122         break;
0123     case DEVFREQ_GOV_STOP:
0124         userspace_exit(devfreq);
0125         break;
0126     default:
0127         break;
0128     }
0129 
0130     return ret;
0131 }
0132 
0133 static struct devfreq_governor devfreq_userspace = {
0134     .name = DEVFREQ_GOV_USERSPACE,
0135     .get_target_freq = devfreq_userspace_func,
0136     .event_handler = devfreq_userspace_handler,
0137 };
0138 
0139 static int __init devfreq_userspace_init(void)
0140 {
0141     return devfreq_add_governor(&devfreq_userspace);
0142 }
0143 subsys_initcall(devfreq_userspace_init);
0144 
0145 static void __exit devfreq_userspace_exit(void)
0146 {
0147     int ret;
0148 
0149     ret = devfreq_remove_governor(&devfreq_userspace);
0150     if (ret)
0151         pr_err("%s: failed remove governor %d\n", __func__, ret);
0152 
0153     return;
0154 }
0155 module_exit(devfreq_userspace_exit);
0156 MODULE_LICENSE("GPL");