1ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham/*
2ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham *  linux/drivers/devfreq/governor_simpleondemand.c
3ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham *
4ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham *  Copyright (C) 2011 Samsung Electronics
5ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham *	MyungJoo Ham <myungjoo.ham@samsung.com>
6ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham *
7ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham * This program is free software; you can redistribute it and/or modify
8ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham * it under the terms of the GNU General Public License version 2 as
9ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham * published by the Free Software Foundation.
10ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham */
11ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
12ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham#include <linux/slab.h>
13ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham#include <linux/device.h>
14ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham#include <linux/devfreq.h>
15ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham#include <linux/pm.h>
16ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham#include <linux/mutex.h>
17ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham#include "governor.h"
18ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
19ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstruct userspace_data {
20ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	unsigned long user_frequency;
21ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	bool valid;
22ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham};
23ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
24ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
25ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham{
26ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	struct userspace_data *data = df->data;
27ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
286530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham	if (data->valid) {
296530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham		unsigned long adjusted_freq = data->user_frequency;
306530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham
316530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham		if (df->max_freq && adjusted_freq > df->max_freq)
326530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham			adjusted_freq = df->max_freq;
336530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham
346530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham		if (df->min_freq && adjusted_freq < df->min_freq)
356530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham			adjusted_freq = df->min_freq;
366530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham
376530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham		*freq = adjusted_freq;
386530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham	} else {
39ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham		*freq = df->previous_freq; /* No user freq specified yet */
406530b9dea1b7f33eaf79ba625e3a99f2455f3eb1MyungJoo Ham	}
41ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	return 0;
42ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham}
43ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
44ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic ssize_t store_freq(struct device *dev, struct device_attribute *attr,
45ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham			  const char *buf, size_t count)
46ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham{
47ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	struct devfreq *devfreq = to_devfreq(dev);
48ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	struct userspace_data *data;
49ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	unsigned long wanted;
50ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	int err = 0;
51ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
52ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
53ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	mutex_lock(&devfreq->lock);
54ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	data = devfreq->data;
55ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
56ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	sscanf(buf, "%lu", &wanted);
57ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	data->user_frequency = wanted;
58ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	data->valid = true;
59ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	err = update_devfreq(devfreq);
60ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	if (err == 0)
61ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham		err = count;
62ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	mutex_unlock(&devfreq->lock);
63ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	return err;
64ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham}
65ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
66ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic ssize_t show_freq(struct device *dev, struct device_attribute *attr,
67ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham			 char *buf)
68ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham{
69ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	struct devfreq *devfreq = to_devfreq(dev);
70ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	struct userspace_data *data;
71ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	int err = 0;
72ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
73ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	mutex_lock(&devfreq->lock);
74ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	data = devfreq->data;
75ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
76ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	if (data->valid)
77ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham		err = sprintf(buf, "%lu\n", data->user_frequency);
78ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	else
79ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham		err = sprintf(buf, "undefined\n");
80ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	mutex_unlock(&devfreq->lock);
81ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	return err;
82ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham}
83ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
84ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic DEVICE_ATTR(set_freq, 0644, show_freq, store_freq);
85ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic struct attribute *dev_entries[] = {
86ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	&dev_attr_set_freq.attr,
87ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	NULL,
88ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham};
89ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic struct attribute_group dev_attr_group = {
90ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	.name	= "userspace",
91ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	.attrs	= dev_entries,
92ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham};
93ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
94ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic int userspace_init(struct devfreq *devfreq)
95ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham{
96ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	int err = 0;
97ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	struct userspace_data *data = kzalloc(sizeof(struct userspace_data),
98ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham					      GFP_KERNEL);
99ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
100ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	if (!data) {
101ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham		err = -ENOMEM;
102ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham		goto out;
103ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	}
104ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	data->valid = false;
105ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	devfreq->data = data;
106ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
107ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
108ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamout:
109ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	return err;
110ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham}
111ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
112ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamstatic void userspace_exit(struct devfreq *devfreq)
113ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham{
114ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
115ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	kfree(devfreq->data);
116ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	devfreq->data = NULL;
117ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham}
118ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham
119ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Hamconst struct devfreq_governor devfreq_userspace = {
120ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	.name = "userspace",
121ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	.get_target_freq = devfreq_userspace_func,
122ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	.init = userspace_init,
123ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	.exit = userspace_exit,
124ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham	.no_central_polling = true,
125ce26c5bb9569d8b826f01b8620fc16d8da6821e9MyungJoo Ham};
126