1/* 2 * linux/drivers/devfreq/governor_simpleondemand.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 54 mutex_lock(&devfreq->lock); 55 data = devfreq->data; 56 57 sscanf(buf, "%lu", &wanted); 58 data->user_frequency = wanted; 59 data->valid = true; 60 err = update_devfreq(devfreq); 61 if (err == 0) 62 err = count; 63 mutex_unlock(&devfreq->lock); 64 return err; 65} 66 67static ssize_t show_freq(struct device *dev, struct device_attribute *attr, 68 char *buf) 69{ 70 struct devfreq *devfreq = to_devfreq(dev); 71 struct userspace_data *data; 72 int err = 0; 73 74 mutex_lock(&devfreq->lock); 75 data = devfreq->data; 76 77 if (data->valid) 78 err = sprintf(buf, "%lu\n", data->user_frequency); 79 else 80 err = sprintf(buf, "undefined\n"); 81 mutex_unlock(&devfreq->lock); 82 return err; 83} 84 85static DEVICE_ATTR(set_freq, 0644, show_freq, store_freq); 86static struct attribute *dev_entries[] = { 87 &dev_attr_set_freq.attr, 88 NULL, 89}; 90static struct attribute_group dev_attr_group = { 91 .name = "userspace", 92 .attrs = dev_entries, 93}; 94 95static int userspace_init(struct devfreq *devfreq) 96{ 97 int err = 0; 98 struct userspace_data *data = kzalloc(sizeof(struct userspace_data), 99 GFP_KERNEL); 100 101 if (!data) { 102 err = -ENOMEM; 103 goto out; 104 } 105 data->valid = false; 106 devfreq->data = data; 107 108 err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group); 109out: 110 return err; 111} 112 113static void userspace_exit(struct devfreq *devfreq) 114{ 115 sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group); 116 kfree(devfreq->data); 117 devfreq->data = NULL; 118} 119 120static int devfreq_userspace_handler(struct devfreq *devfreq, 121 unsigned int event, void *data) 122{ 123 int ret = 0; 124 125 switch (event) { 126 case DEVFREQ_GOV_START: 127 ret = userspace_init(devfreq); 128 break; 129 case DEVFREQ_GOV_STOP: 130 userspace_exit(devfreq); 131 break; 132 default: 133 break; 134 } 135 136 return ret; 137} 138 139static struct devfreq_governor devfreq_userspace = { 140 .name = "userspace", 141 .get_target_freq = devfreq_userspace_func, 142 .event_handler = devfreq_userspace_handler, 143}; 144 145static int __init devfreq_userspace_init(void) 146{ 147 return devfreq_add_governor(&devfreq_userspace); 148} 149subsys_initcall(devfreq_userspace_init); 150 151static void __exit devfreq_userspace_exit(void) 152{ 153 int ret; 154 155 ret = devfreq_remove_governor(&devfreq_userspace); 156 if (ret) 157 pr_err("%s: failed remove governor %d\n", __func__, ret); 158 159 return; 160} 161module_exit(devfreq_userspace_exit); 162MODULE_LICENSE("GPL"); 163