diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 0034ae1b1d1e..938bd02d805f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -258,6 +258,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq, */ int update_devfreq(struct devfreq *devfreq) { + struct devfreq_policy *policy = &devfreq->policy; struct devfreq_freqs freqs; unsigned long freq, cur_freq, min_freq, max_freq; int err = 0; @@ -271,6 +272,11 @@ int update_devfreq(struct devfreq *devfreq) if (!devfreq->governor) return -EINVAL; + policy->max = devfreq->scaling_max_freq; + policy->min = devfreq->scaling_min_freq; + srcu_notifier_call_chain(&devfreq->policy_notifier_list, DEVFREQ_ADJUST, + policy); + /* Reevaluate the proper frequency */ err = devfreq->governor->get_target_freq(devfreq, &freq); if (err) @@ -283,8 +289,8 @@ int update_devfreq(struct devfreq *devfreq) * max_freq * min_freq */ - max_freq = min(devfreq->scaling_max_freq, devfreq->max_freq); - min_freq = max(devfreq->scaling_min_freq, devfreq->min_freq); + max_freq = min(policy->max, devfreq->max_freq); + min_freq = max(policy->min, devfreq->min_freq); if (freq < min_freq) { freq = min_freq; @@ -613,6 +619,7 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_dev; } devfreq->min_freq = devfreq->scaling_min_freq; + devfreq->policy.min = devfreq->min_freq; devfreq->scaling_max_freq = find_available_max_freq(devfreq); if (!devfreq->scaling_max_freq) { @@ -621,6 +628,7 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_dev; } devfreq->max_freq = devfreq->scaling_max_freq; + devfreq->policy.max = devfreq->max_freq; dev_set_name(&devfreq->dev, "%s", dev_name(dev)); err = device_register(&devfreq->dev); @@ -642,6 +650,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->last_stat_updated = jiffies; srcu_init_notifier_head(&devfreq->transition_notifier_list); + srcu_init_notifier_head(&devfreq->policy_notifier_list); mutex_unlock(&devfreq->lock); @@ -1161,7 +1170,7 @@ static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, { struct devfreq *df = to_devfreq(dev); - return sprintf(buf, "%lu\n", max(df->scaling_min_freq, df->min_freq)); + return sprintf(buf, "%lu\n", max(df->policy.min, df->min_freq)); } static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, @@ -1206,7 +1215,7 @@ static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, { struct devfreq *df = to_devfreq(dev); - return sprintf(buf, "%lu\n", min(df->scaling_max_freq, df->max_freq)); + return sprintf(buf, "%lu\n", min(df->policy.max, df->max_freq)); } static DEVICE_ATTR_RW(max_freq); @@ -1473,7 +1482,7 @@ EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); * devfreq_register_notifier() - Register a driver with devfreq * @devfreq: The devfreq object. * @nb: The notifier block to register. - * @list: DEVFREQ_TRANSITION_NOTIFIER. + * @list: DEVFREQ_TRANSITION_NOTIFIER or DEVFREQ_POLICY_NOTIFIER. */ int devfreq_register_notifier(struct devfreq *devfreq, struct notifier_block *nb, @@ -1489,6 +1498,10 @@ int devfreq_register_notifier(struct devfreq *devfreq, ret = srcu_notifier_chain_register( &devfreq->transition_notifier_list, nb); break; + case DEVFREQ_POLICY_NOTIFIER: + ret = srcu_notifier_chain_register( + &devfreq->policy_notifier_list, nb); + break; default: ret = -EINVAL; } @@ -1501,7 +1514,7 @@ EXPORT_SYMBOL(devfreq_register_notifier); * devfreq_unregister_notifier() - Unregister a driver with devfreq * @devfreq: The devfreq object. * @nb: The notifier block to be unregistered. - * @list: DEVFREQ_TRANSITION_NOTIFIER. + * @list: DEVFREQ_TRANSITION_NOTIFIER or DEVFREQ_POLICY_NOTIFIER. */ int devfreq_unregister_notifier(struct devfreq *devfreq, struct notifier_block *nb, @@ -1517,6 +1530,10 @@ int devfreq_unregister_notifier(struct devfreq *devfreq, ret = srcu_notifier_chain_unregister( &devfreq->transition_notifier_list, nb); break; + case DEVFREQ_POLICY_NOTIFIER: + ret = srcu_notifier_chain_unregister( + &devfreq->policy_notifier_list, nb); + break; default: ret = -EINVAL; } diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 4393ab38d59b..978b3d369d0e 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -21,11 +21,15 @@ /* DEVFREQ notifier interface */ #define DEVFREQ_TRANSITION_NOTIFIER (0) +#define DEVFREQ_POLICY_NOTIFIER (1) /* Transition notifiers of DEVFREQ_TRANSITION_NOTIFIER */ #define DEVFREQ_PRECHANGE (0) #define DEVFREQ_POSTCHANGE (1) +/* Policy Notifiers */ +#define DEVFREQ_ADJUST (0) + struct devfreq; /** @@ -130,6 +134,16 @@ struct devfreq_governor { unsigned int event, void *data); }; +/** + * struct devfreq_policy - Devfreq frequency limits + * @min_: minimum frequency (adjustable by policy notifiers) + * @max: maximum frequency (adjustable by policy notifiers) + */ +struct devfreq_policy { + unsigned long min; + unsigned long max; +}; + /** * struct devfreq - Device devfreq structure * @node: list node - contains the devices with devfreq that have been @@ -147,6 +161,7 @@ struct devfreq_governor { * @previous_freq: previously configured frequency value. * @data: Private data of the governor. The devfreq framework does not * touch this. + * @policy: Frequency limits of the device * @min_freq: Limit minimum frequency requested by user (0: none) * @max_freq: Limit maximum frequency requested by user (0: none) * @scaling_min_freq: Limit minimum frequency requested by OPP interface @@ -157,6 +172,7 @@ struct devfreq_governor { * @time_in_state: Statistics of devfreq states * @last_stat_updated: The last time stat updated * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier + * @policy_notifier_list: list head of DEVFREQ_POLICY_NOTIFIER notifier * * This structure stores the devfreq information for a give device. * @@ -182,6 +198,8 @@ struct devfreq { void *data; /* private data for governors */ + struct devfreq_policy policy; + unsigned long min_freq; unsigned long max_freq; unsigned long scaling_min_freq; @@ -195,6 +213,7 @@ struct devfreq { unsigned long last_stat_updated; struct srcu_notifier_head transition_notifier_list; + struct srcu_notifier_head policy_notifier_list; }; struct devfreq_freqs { @@ -260,6 +279,29 @@ static inline int devfreq_update_stats(struct devfreq *df) return df->profile->get_dev_status(df->dev.parent, &df->last_status); } +/** + * devfreq_verify_within_limits() - Adjust a devfreq policy if needed to make + * sure its min/max values are within a + * specified range. + * @policy: the policy + * @min: the minimum frequency + * @max: the maximum frequency + */ +static inline void devfreq_verify_within_limits(struct devfreq_policy *policy, + unsigned int min, unsigned int max) +{ + if (policy->min < min) + policy->min = min; + if (policy->max < min) + policy->max = min; + if (policy->min > max) + policy->min = max; + if (policy->max > max) + policy->max = max; + if (policy->min > policy->max) + policy->min = policy->max; +} + #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) /** * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq @@ -381,6 +423,11 @@ static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, return ERR_PTR(-ENODEV); } +static inline void devfreq_verify_within_limits(struct devfreq_policy *policy, + unsigned int min, unsigned int max) +{ +} + static inline int devfreq_update_stats(struct devfreq *df) { return -EINVAL;