diff options
| -rw-r--r-- | drivers/devfreq/devfreq.c | 42 | ||||
| -rw-r--r-- | drivers/devfreq/exynos4_bus.c | 14 | ||||
| -rw-r--r-- | include/linux/devfreq.h | 16 | 
3 files changed, 61 insertions, 11 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a129a7b6bfd..70c31d43fff 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -83,6 +83,7 @@ int update_devfreq(struct devfreq *devfreq)  {  	unsigned long freq;  	int err = 0; +	u32 flags = 0;  	if (!mutex_is_locked(&devfreq->lock)) {  		WARN(true, "devfreq->lock must be locked by the caller.\n"); @@ -94,7 +95,24 @@ int update_devfreq(struct devfreq *devfreq)  	if (err)  		return err; -	err = devfreq->profile->target(devfreq->dev.parent, &freq); +	/* +	 * Adjust the freuqency with user freq and QoS. +	 * +	 * List from the highest proiority +	 * max_freq (probably called by thermal when it's too hot) +	 * min_freq +	 */ + +	if (devfreq->min_freq && freq < devfreq->min_freq) { +		freq = devfreq->min_freq; +		flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */ +	} +	if (devfreq->max_freq && freq > devfreq->max_freq) { +		freq = devfreq->max_freq; +		flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ +	} + +	err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);  	if (err)  		return err; @@ -625,14 +643,30 @@ module_exit(devfreq_exit);   *			     freq value given to target callback.   * @dev		The devfreq user device. (parent of devfreq)   * @freq	The frequency given to target function + * @flags	Flags handed from devfreq framework.   *   */ -struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq) +struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, +				    u32 flags)  { -	struct opp *opp = opp_find_freq_ceil(dev, freq); +	struct opp *opp; -	if (opp == ERR_PTR(-ENODEV)) +	if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) { +		/* The freq is an upper bound. opp should be lower */  		opp = opp_find_freq_floor(dev, freq); + +		/* If not available, use the closest opp */ +		if (opp == ERR_PTR(-ENODEV)) +			opp = opp_find_freq_ceil(dev, freq); +	} else { +		/* The freq is an lower bound. opp should be higher */ +		opp = opp_find_freq_ceil(dev, freq); + +		/* If not available, use the closest opp */ +		if (opp == ERR_PTR(-ENODEV)) +			opp = opp_find_freq_floor(dev, freq); +	} +  	return opp;  } diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c index 590d6865e38..1a361e99965 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos4_bus.c @@ -619,13 +619,19 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,  	return err;  } -static int exynos4_bus_target(struct device *dev, unsigned long *_freq) +static int exynos4_bus_target(struct device *dev, unsigned long *_freq, +			      u32 flags)  {  	int err = 0; -	struct busfreq_data *data = dev_get_drvdata(dev); -	struct opp *opp = devfreq_recommended_opp(dev, _freq); -	unsigned long old_freq = opp_get_freq(data->curr_opp); +	struct platform_device *pdev = container_of(dev, struct platform_device, +						    dev); +	struct busfreq_data *data = platform_get_drvdata(pdev); +	struct opp *opp = devfreq_recommended_opp(dev, _freq, flags);  	unsigned long freq = opp_get_freq(opp); +	unsigned long old_freq = opp_get_freq(data->curr_opp); + +	if (IS_ERR(opp)) +		return PTR_ERR(opp);  	if (old_freq == freq)  		return 0; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 5862475d05f..281c72a3b9d 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -44,6 +44,14 @@ struct devfreq_dev_status {  	void *private_data;  }; +/* + * The resulting frequency should be at most this. (this bound is the + * least upper bound; thus, the resulting freq should be lower or same) + * If the flag is not set, the resulting frequency should be at most the + * bound (greatest lower bound) + */ +#define DEVFREQ_FLAG_LEAST_UPPER_BOUND		0x1 +  /**   * struct devfreq_dev_profile - Devfreq's user device profile   * @initial_freq	The operating frequency when devfreq_add_device() is @@ -54,6 +62,8 @@ struct devfreq_dev_status {   *			higher than any operable frequency, set maximum.   *			Before returning, target function should set   *			freq at the current frequency. + *			The "flags" parameter's possible values are + *			explained above with "DEVFREQ_FLAG_*" macros.   * @get_dev_status	The device should provide the current performance   *			status to devfreq, which is used by governors.   * @exit		An optional callback that is called when devfreq @@ -66,7 +76,7 @@ struct devfreq_dev_profile {  	unsigned long initial_freq;  	unsigned int polling_ms; -	int (*target)(struct device *dev, unsigned long *freq); +	int (*target)(struct device *dev, unsigned long *freq, u32 flags);  	int (*get_dev_status)(struct device *dev,  			      struct devfreq_dev_status *stat);  	void (*exit)(struct device *dev); @@ -165,7 +175,7 @@ extern int devfreq_remove_device(struct devfreq *devfreq);  /* Helper functions for devfreq user device driver with OPP. */  extern struct opp *devfreq_recommended_opp(struct device *dev, -					   unsigned long *freq); +					   unsigned long *freq, u32 flags);  extern int devfreq_register_opp_notifier(struct device *dev,  					 struct devfreq *devfreq);  extern int devfreq_unregister_opp_notifier(struct device *dev, @@ -216,7 +226,7 @@ static int devfreq_remove_device(struct devfreq *devfreq)  }  static struct opp *devfreq_recommended_opp(struct device *dev, -					   unsigned long *freq) +					   unsigned long *freq, u32 flags)  {  	return -EINVAL;  }  |