Skip to content

Commit

Permalink
PM / devfreq: add relation of recommended frequency.
Browse files Browse the repository at this point in the history
The semantics of "target frequency" given to devfreq driver from
devfreq framework has always been interpretted as "at least" or GLB
(greatest lower bound). However, the framework might want the
device driver to limit its max frequency (LUB: least upper bound),
especially if it is given by thermal framework (it's too hot).

Thus, the target fuction should have another parameter to express
whether the framework wants GLB or LUB. And, the additional parameter,
"u32 flags", does it.

With the update, devfreq_recommended_opp() is also updated.

Signed-off-by: MyungJoo Ham <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
Reviewed-by: Mike Turquette <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
  • Loading branch information
myungjoo authored and rjwysocki committed Mar 17, 2012
1 parent e4c9d8e commit ab5f299
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 11 deletions.
42 changes: 38 additions & 4 deletions drivers/devfreq/devfreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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;

Expand Down Expand Up @@ -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;
}

Expand Down
14 changes: 10 additions & 4 deletions drivers/devfreq/exynos4_bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 13 additions & 3 deletions include/linux/devfreq.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit ab5f299

Please sign in to comment.