diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-class-devfreq | 8 | ||||
| -rw-r--r-- | drivers/devfreq/Kconfig | 36 | ||||
| -rw-r--r-- | drivers/devfreq/Makefile | 4 | ||||
| -rw-r--r-- | drivers/devfreq/governor_performance.c | 29 | ||||
| -rw-r--r-- | drivers/devfreq/governor_powersave.c | 29 | ||||
| -rw-r--r-- | drivers/devfreq/governor_simpleondemand.c | 88 | ||||
| -rw-r--r-- | drivers/devfreq/governor_userspace.c | 116 | ||||
| -rw-r--r-- | include/linux/devfreq.h | 35 | 
8 files changed, 345 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 0ec855f479c..23d78b5aab1 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -42,3 +42,11 @@ Description:  		devfreq-provided central polling  		(/sys/class/devfreq/.../central_polling is 0), this value  		may be useless. + +What:		/sys/class/devfreq/.../userspace/set_freq +Date:		September 2011 +Contact:	MyungJoo Ham <myungjoo.ham@samsung.com> +Description: +		The /sys/class/devfreq/.../userspace/set_freq shows and +		sets the requested frequency for the devfreq object if +		userspace governor is in effect. diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 1fb42de4f42..643b055ed3c 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -34,6 +34,42 @@ menuconfig PM_DEVFREQ  if PM_DEVFREQ +comment "DEVFREQ Governors" + +config DEVFREQ_GOV_SIMPLE_ONDEMAND +	bool "Simple Ondemand" +	help +	  Chooses frequency based on the recent load on the device. Works +	  similar as ONDEMAND governor of CPUFREQ does. A device with +	  Simple-Ondemand should be able to provide busy/total counter +	  values that imply the usage rate. A device may provide tuned +	  values to the governor with data field at devfreq_add_device(). + +config DEVFREQ_GOV_PERFORMANCE +	bool "Performance" +	help +	  Sets the frequency at the maximum available frequency. +	  This governor always returns UINT_MAX as frequency so that +	  the DEVFREQ framework returns the highest frequency available +	  at any time. + +config DEVFREQ_GOV_POWERSAVE +	bool "Powersave" +	help +	  Sets the frequency at the minimum available frequency. +	  This governor always returns 0 as frequency so that +	  the DEVFREQ framework returns the lowest frequency available +	  at any time. + +config DEVFREQ_GOV_USERSPACE +	bool "Userspace" +	help +	  Sets the frequency at the user specified one. +	  This governor returns the user configured frequency if there +	  has been an input to /sys/devices/.../power/devfreq_set_freq. +	  Otherwise, the governor does not change the frequnecy +	  given at the initialization. +  comment "DEVFREQ Drivers"  endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 168934a12b3..4564a89e970 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -1 +1,5 @@  obj-$(CONFIG_PM_DEVFREQ)	+= devfreq.o +obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)	+= governor_simpleondemand.o +obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE)	+= governor_performance.o +obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE)	+= governor_powersave.o +obj-$(CONFIG_DEVFREQ_GOV_USERSPACE)	+= governor_userspace.o diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c new file mode 100644 index 00000000000..c0596b29176 --- /dev/null +++ b/drivers/devfreq/governor_performance.c @@ -0,0 +1,29 @@ +/* + *  linux/drivers/devfreq/governor_performance.c + * + *  Copyright (C) 2011 Samsung Electronics + *	MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/devfreq.h> + +static int devfreq_performance_func(struct devfreq *df, +				    unsigned long *freq) +{ +	/* +	 * target callback should be able to get floor value as +	 * said in devfreq.h +	 */ +	*freq = UINT_MAX; +	return 0; +} + +const struct devfreq_governor devfreq_performance = { +	.name = "performance", +	.get_target_freq = devfreq_performance_func, +	.no_central_polling = true, +}; diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c new file mode 100644 index 00000000000..2483a85a266 --- /dev/null +++ b/drivers/devfreq/governor_powersave.c @@ -0,0 +1,29 @@ +/* + *  linux/drivers/devfreq/governor_powersave.c + * + *  Copyright (C) 2011 Samsung Electronics + *	MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/devfreq.h> + +static int devfreq_powersave_func(struct devfreq *df, +				  unsigned long *freq) +{ +	/* +	 * target callback should be able to get ceiling value as +	 * said in devfreq.h +	 */ +	*freq = 0; +	return 0; +} + +const struct devfreq_governor devfreq_powersave = { +	.name = "powersave", +	.get_target_freq = devfreq_powersave_func, +	.no_central_polling = true, +}; diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c new file mode 100644 index 00000000000..efad8dcf902 --- /dev/null +++ b/drivers/devfreq/governor_simpleondemand.c @@ -0,0 +1,88 @@ +/* + *  linux/drivers/devfreq/governor_simpleondemand.c + * + *  Copyright (C) 2011 Samsung Electronics + *	MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/errno.h> +#include <linux/devfreq.h> +#include <linux/math64.h> + +/* Default constants for DevFreq-Simple-Ondemand (DFSO) */ +#define DFSO_UPTHRESHOLD	(90) +#define DFSO_DOWNDIFFERENCTIAL	(5) +static int devfreq_simple_ondemand_func(struct devfreq *df, +					unsigned long *freq) +{ +	struct devfreq_dev_status stat; +	int err = df->profile->get_dev_status(df->dev.parent, &stat); +	unsigned long long a, b; +	unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD; +	unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL; +	struct devfreq_simple_ondemand_data *data = df->data; + +	if (err) +		return err; + +	if (data) { +		if (data->upthreshold) +			dfso_upthreshold = data->upthreshold; +		if (data->downdifferential) +			dfso_downdifferential = data->downdifferential; +	} +	if (dfso_upthreshold > 100 || +	    dfso_upthreshold < dfso_downdifferential) +		return -EINVAL; + +	/* Assume MAX if it is going to be divided by zero */ +	if (stat.total_time == 0) { +		*freq = UINT_MAX; +		return 0; +	} + +	/* Prevent overflow */ +	if (stat.busy_time >= (1 << 24) || stat.total_time >= (1 << 24)) { +		stat.busy_time >>= 7; +		stat.total_time >>= 7; +	} + +	/* Set MAX if it's busy enough */ +	if (stat.busy_time * 100 > +	    stat.total_time * dfso_upthreshold) { +		*freq = UINT_MAX; +		return 0; +	} + +	/* Set MAX if we do not know the initial frequency */ +	if (stat.current_frequency == 0) { +		*freq = UINT_MAX; +		return 0; +	} + +	/* Keep the current frequency */ +	if (stat.busy_time * 100 > +	    stat.total_time * (dfso_upthreshold - dfso_downdifferential)) { +		*freq = stat.current_frequency; +		return 0; +	} + +	/* Set the desired frequency based on the load */ +	a = stat.busy_time; +	a *= stat.current_frequency; +	b = div_u64(a, stat.total_time); +	b *= 100; +	b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2)); +	*freq = (unsigned long) b; + +	return 0; +} + +const struct devfreq_governor devfreq_simple_ondemand = { +	.name = "simple_ondemand", +	.get_target_freq = devfreq_simple_ondemand_func, +}; diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c new file mode 100644 index 00000000000..4f8b563da78 --- /dev/null +++ b/drivers/devfreq/governor_userspace.c @@ -0,0 +1,116 @@ +/* + *  linux/drivers/devfreq/governor_simpleondemand.c + * + *  Copyright (C) 2011 Samsung Electronics + *	MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/devfreq.h> +#include <linux/pm.h> +#include <linux/mutex.h> +#include "governor.h" + +struct userspace_data { +	unsigned long user_frequency; +	bool valid; +}; + +static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq) +{ +	struct userspace_data *data = df->data; + +	if (!data->valid) +		*freq = df->previous_freq; /* No user freq specified yet */ +	else +		*freq = data->user_frequency; +	return 0; +} + +static ssize_t store_freq(struct device *dev, struct device_attribute *attr, +			  const char *buf, size_t count) +{ +	struct devfreq *devfreq = to_devfreq(dev); +	struct userspace_data *data; +	unsigned long wanted; +	int err = 0; + + +	mutex_lock(&devfreq->lock); +	data = devfreq->data; + +	sscanf(buf, "%lu", &wanted); +	data->user_frequency = wanted; +	data->valid = true; +	err = update_devfreq(devfreq); +	if (err == 0) +		err = count; +	mutex_unlock(&devfreq->lock); +	return err; +} + +static ssize_t show_freq(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct devfreq *devfreq = to_devfreq(dev); +	struct userspace_data *data; +	int err = 0; + +	mutex_lock(&devfreq->lock); +	data = devfreq->data; + +	if (data->valid) +		err = sprintf(buf, "%lu\n", data->user_frequency); +	else +		err = sprintf(buf, "undefined\n"); +	mutex_unlock(&devfreq->lock); +	return err; +} + +static DEVICE_ATTR(set_freq, 0644, show_freq, store_freq); +static struct attribute *dev_entries[] = { +	&dev_attr_set_freq.attr, +	NULL, +}; +static struct attribute_group dev_attr_group = { +	.name	= "userspace", +	.attrs	= dev_entries, +}; + +static int userspace_init(struct devfreq *devfreq) +{ +	int err = 0; +	struct userspace_data *data = kzalloc(sizeof(struct userspace_data), +					      GFP_KERNEL); + +	if (!data) { +		err = -ENOMEM; +		goto out; +	} +	data->valid = false; +	devfreq->data = data; + +	err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group); +out: +	return err; +} + +static void userspace_exit(struct devfreq *devfreq) +{ +	sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group); +	kfree(devfreq->data); +	devfreq->data = NULL; +} + +const struct devfreq_governor devfreq_userspace = { +	.name = "userspace", +	.get_target_freq = devfreq_userspace_func, +	.init = userspace_init, +	.exit = userspace_exit, +	.no_central_polling = true, +}; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index b3be3d3cbaa..afb94583960 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -166,6 +166,36 @@ extern int devfreq_register_opp_notifier(struct device *dev,  extern int devfreq_unregister_opp_notifier(struct device *dev,  					   struct devfreq *devfreq); +#ifdef CONFIG_DEVFREQ_GOV_POWERSAVE +extern const struct devfreq_governor devfreq_powersave; +#endif +#ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE +extern const struct devfreq_governor devfreq_performance; +#endif +#ifdef CONFIG_DEVFREQ_GOV_USERSPACE +extern const struct devfreq_governor devfreq_userspace; +#endif +#ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND +extern const struct devfreq_governor devfreq_simple_ondemand; +/** + * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq + *	and devfreq_add_device + * @ upthreshold	If the load is over this value, the frequency jumps. + *			Specify 0 to use the default. Valid value = 0 to 100. + * @ downdifferential	If the load is under upthreshold - downdifferential, + *			the governor may consider slowing the frequency down. + *			Specify 0 to use the default. Valid value = 0 to 100. + *			downdifferential < upthreshold must hold. + * + * If the fed devfreq_simple_ondemand_data pointer is NULL to the governor, + * the governor uses the default values. + */ +struct devfreq_simple_ondemand_data { +	unsigned int upthreshold; +	unsigned int downdifferential; +}; +#endif +  #else /* !CONFIG_PM_DEVFREQ */  static struct devfreq *devfreq_add_device(struct device *dev,  					  struct devfreq_dev_profile *profile, @@ -198,6 +228,11 @@ static int devfreq_unregister_opp_notifier(struct device *dev,  	return -EINVAL;  } +#define devfreq_powersave	NULL +#define devfreq_performance	NULL +#define devfreq_userspace	NULL +#define devfreq_simple_ondemand	NULL +  #endif /* CONFIG_PM_DEVFREQ */  #endif /* __LINUX_DEVFREQ_H__ */  |