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