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