forked from mirrors/linux
		
	PM / QoS: Add support for MIN/MAX frequency constraints
This patch introduces the min-frequency and max-frequency device constraints, which will be used by the cpufreq core to begin with. Reviewed-by: Matthias Kaehlcke <mka@chromium.org> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
		
							parent
							
								
									2a79ea5ec5
								
							
						
					
					
						commit
						208637b378
					
				
					 2 changed files with 109 additions and 14 deletions
				
			
		|  | @ -120,6 +120,14 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) | ||||||
| 		ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT | 		ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT | ||||||
| 			: pm_qos_read_value(&qos->resume_latency); | 			: pm_qos_read_value(&qos->resume_latency); | ||||||
| 		break; | 		break; | ||||||
|  | 	case DEV_PM_QOS_MIN_FREQUENCY: | ||||||
|  | 		ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE | ||||||
|  | 			: pm_qos_read_value(&qos->min_frequency); | ||||||
|  | 		break; | ||||||
|  | 	case DEV_PM_QOS_MAX_FREQUENCY: | ||||||
|  | 		ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE | ||||||
|  | 			: pm_qos_read_value(&qos->max_frequency); | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		WARN_ON(1); | 		WARN_ON(1); | ||||||
| 		ret = 0; | 		ret = 0; | ||||||
|  | @ -161,6 +169,14 @@ static int apply_constraint(struct dev_pm_qos_request *req, | ||||||
| 			req->dev->power.set_latency_tolerance(req->dev, value); | 			req->dev->power.set_latency_tolerance(req->dev, value); | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
|  | 	case DEV_PM_QOS_MIN_FREQUENCY: | ||||||
|  | 		ret = pm_qos_update_target(&qos->min_frequency, | ||||||
|  | 					   &req->data.pnode, action, value); | ||||||
|  | 		break; | ||||||
|  | 	case DEV_PM_QOS_MAX_FREQUENCY: | ||||||
|  | 		ret = pm_qos_update_target(&qos->max_frequency, | ||||||
|  | 					   &req->data.pnode, action, value); | ||||||
|  | 		break; | ||||||
| 	case DEV_PM_QOS_FLAGS: | 	case DEV_PM_QOS_FLAGS: | ||||||
| 		ret = pm_qos_update_flags(&qos->flags, &req->data.flr, | 		ret = pm_qos_update_flags(&qos->flags, &req->data.flr, | ||||||
| 					  action, value); | 					  action, value); | ||||||
|  | @ -189,12 +205,11 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | ||||||
| 	if (!qos) | 	if (!qos) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	n = kzalloc(sizeof(*n), GFP_KERNEL); | 	n = kzalloc(3 * sizeof(*n), GFP_KERNEL); | ||||||
| 	if (!n) { | 	if (!n) { | ||||||
| 		kfree(qos); | 		kfree(qos); | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 	} | 	} | ||||||
| 	BLOCKING_INIT_NOTIFIER_HEAD(n); |  | ||||||
| 
 | 
 | ||||||
| 	c = &qos->resume_latency; | 	c = &qos->resume_latency; | ||||||
| 	plist_head_init(&c->list); | 	plist_head_init(&c->list); | ||||||
|  | @ -203,6 +218,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | ||||||
| 	c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; | 	c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; | ||||||
| 	c->type = PM_QOS_MIN; | 	c->type = PM_QOS_MIN; | ||||||
| 	c->notifiers = n; | 	c->notifiers = n; | ||||||
|  | 	BLOCKING_INIT_NOTIFIER_HEAD(n); | ||||||
| 
 | 
 | ||||||
| 	c = &qos->latency_tolerance; | 	c = &qos->latency_tolerance; | ||||||
| 	plist_head_init(&c->list); | 	plist_head_init(&c->list); | ||||||
|  | @ -211,6 +227,24 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | ||||||
| 	c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; | 	c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; | ||||||
| 	c->type = PM_QOS_MIN; | 	c->type = PM_QOS_MIN; | ||||||
| 
 | 
 | ||||||
|  | 	c = &qos->min_frequency; | ||||||
|  | 	plist_head_init(&c->list); | ||||||
|  | 	c->target_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||||||
|  | 	c->default_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||||||
|  | 	c->no_constraint_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||||||
|  | 	c->type = PM_QOS_MAX; | ||||||
|  | 	c->notifiers = ++n; | ||||||
|  | 	BLOCKING_INIT_NOTIFIER_HEAD(n); | ||||||
|  | 
 | ||||||
|  | 	c = &qos->max_frequency; | ||||||
|  | 	plist_head_init(&c->list); | ||||||
|  | 	c->target_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||||||
|  | 	c->default_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||||||
|  | 	c->no_constraint_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||||||
|  | 	c->type = PM_QOS_MIN; | ||||||
|  | 	c->notifiers = ++n; | ||||||
|  | 	BLOCKING_INIT_NOTIFIER_HEAD(n); | ||||||
|  | 
 | ||||||
| 	INIT_LIST_HEAD(&qos->flags.list); | 	INIT_LIST_HEAD(&qos->flags.list); | ||||||
| 
 | 
 | ||||||
| 	spin_lock_irq(&dev->power.lock); | 	spin_lock_irq(&dev->power.lock); | ||||||
|  | @ -264,11 +298,25 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | ||||||
| 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | ||||||
| 		memset(req, 0, sizeof(*req)); | 		memset(req, 0, sizeof(*req)); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	c = &qos->latency_tolerance; | 	c = &qos->latency_tolerance; | ||||||
| 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | ||||||
| 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | ||||||
| 		memset(req, 0, sizeof(*req)); | 		memset(req, 0, sizeof(*req)); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	c = &qos->min_frequency; | ||||||
|  | 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | ||||||
|  | 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE); | ||||||
|  | 		memset(req, 0, sizeof(*req)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c = &qos->max_frequency; | ||||||
|  | 	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | ||||||
|  | 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); | ||||||
|  | 		memset(req, 0, sizeof(*req)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	f = &qos->flags; | 	f = &qos->flags; | ||||||
| 	list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { | 	list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { | ||||||
| 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | ||||||
|  | @ -380,6 +428,8 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, | ||||||
| 	switch(req->type) { | 	switch(req->type) { | ||||||
| 	case DEV_PM_QOS_RESUME_LATENCY: | 	case DEV_PM_QOS_RESUME_LATENCY: | ||||||
| 	case DEV_PM_QOS_LATENCY_TOLERANCE: | 	case DEV_PM_QOS_LATENCY_TOLERANCE: | ||||||
|  | 	case DEV_PM_QOS_MIN_FREQUENCY: | ||||||
|  | 	case DEV_PM_QOS_MAX_FREQUENCY: | ||||||
| 		curr_value = req->data.pnode.prio; | 		curr_value = req->data.pnode.prio; | ||||||
| 		break; | 		break; | ||||||
| 	case DEV_PM_QOS_FLAGS: | 	case DEV_PM_QOS_FLAGS: | ||||||
|  | @ -492,9 +542,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, | ||||||
| { | { | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY)) |  | ||||||
| 		return -EINVAL; |  | ||||||
| 
 |  | ||||||
| 	mutex_lock(&dev_pm_qos_mtx); | 	mutex_lock(&dev_pm_qos_mtx); | ||||||
| 
 | 
 | ||||||
| 	if (IS_ERR(dev->power.qos)) | 	if (IS_ERR(dev->power.qos)) | ||||||
|  | @ -502,10 +549,28 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, | ||||||
| 	else if (!dev->power.qos) | 	else if (!dev->power.qos) | ||||||
| 		ret = dev_pm_qos_constraints_allocate(dev); | 		ret = dev_pm_qos_constraints_allocate(dev); | ||||||
| 
 | 
 | ||||||
| 	if (!ret) | 	if (ret) | ||||||
|  | 		goto unlock; | ||||||
|  | 
 | ||||||
|  | 	switch (type) { | ||||||
|  | 	case DEV_PM_QOS_RESUME_LATENCY: | ||||||
| 		ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, | 		ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, | ||||||
| 						       notifier); | 						       notifier); | ||||||
|  | 		break; | ||||||
|  | 	case DEV_PM_QOS_MIN_FREQUENCY: | ||||||
|  | 		ret = blocking_notifier_chain_register(dev->power.qos->min_frequency.notifiers, | ||||||
|  | 						       notifier); | ||||||
|  | 		break; | ||||||
|  | 	case DEV_PM_QOS_MAX_FREQUENCY: | ||||||
|  | 		ret = blocking_notifier_chain_register(dev->power.qos->max_frequency.notifiers, | ||||||
|  | 						       notifier); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		WARN_ON(1); | ||||||
|  | 		ret = -EINVAL; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
|  | unlock: | ||||||
| 	mutex_unlock(&dev_pm_qos_mtx); | 	mutex_unlock(&dev_pm_qos_mtx); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -526,20 +591,35 @@ int dev_pm_qos_remove_notifier(struct device *dev, | ||||||
| 			       struct notifier_block *notifier, | 			       struct notifier_block *notifier, | ||||||
| 			       enum dev_pm_qos_req_type type) | 			       enum dev_pm_qos_req_type type) | ||||||
| { | { | ||||||
| 	int retval = 0; | 	int ret = 0; | ||||||
| 
 |  | ||||||
| 	if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY)) |  | ||||||
| 		return -EINVAL; |  | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&dev_pm_qos_mtx); | 	mutex_lock(&dev_pm_qos_mtx); | ||||||
| 
 | 
 | ||||||
| 	/* Silently return if the constraints object is not present. */ | 	/* Silently return if the constraints object is not present. */ | ||||||
| 	if (!IS_ERR_OR_NULL(dev->power.qos)) | 	if (IS_ERR_OR_NULL(dev->power.qos)) | ||||||
| 		retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, | 		goto unlock; | ||||||
| 							    notifier); |  | ||||||
| 
 | 
 | ||||||
|  | 	switch (type) { | ||||||
|  | 	case DEV_PM_QOS_RESUME_LATENCY: | ||||||
|  | 		ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, | ||||||
|  | 							 notifier); | ||||||
|  | 		break; | ||||||
|  | 	case DEV_PM_QOS_MIN_FREQUENCY: | ||||||
|  | 		ret = blocking_notifier_chain_unregister(dev->power.qos->min_frequency.notifiers, | ||||||
|  | 							 notifier); | ||||||
|  | 		break; | ||||||
|  | 	case DEV_PM_QOS_MAX_FREQUENCY: | ||||||
|  | 		ret = blocking_notifier_chain_unregister(dev->power.qos->max_frequency.notifiers, | ||||||
|  | 							 notifier); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		WARN_ON(1); | ||||||
|  | 		ret = -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | unlock: | ||||||
| 	mutex_unlock(&dev_pm_qos_mtx); | 	mutex_unlock(&dev_pm_qos_mtx); | ||||||
| 	return retval; | 	return ret; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); | EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); | ||||||
| 
 | 
 | ||||||
|  | @ -599,6 +679,9 @@ static void __dev_pm_qos_drop_user_request(struct device *dev, | ||||||
| 		req = dev->power.qos->flags_req; | 		req = dev->power.qos->flags_req; | ||||||
| 		dev->power.qos->flags_req = NULL; | 		dev->power.qos->flags_req = NULL; | ||||||
| 		break; | 		break; | ||||||
|  | 	default: | ||||||
|  | 		WARN_ON(1); | ||||||
|  | 		return; | ||||||
| 	} | 	} | ||||||
| 	__dev_pm_qos_remove_request(req); | 	__dev_pm_qos_remove_request(req); | ||||||
| 	kfree(req); | 	kfree(req); | ||||||
|  |  | ||||||
|  | @ -40,6 +40,8 @@ enum pm_qos_flags_status { | ||||||
| #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT	PM_QOS_LATENCY_ANY | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT	PM_QOS_LATENCY_ANY | ||||||
| #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS	PM_QOS_LATENCY_ANY_NS | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS	PM_QOS_LATENCY_ANY_NS | ||||||
| #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE	0 | #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE	0 | ||||||
|  | #define PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE	0 | ||||||
|  | #define PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE	(-1) | ||||||
| #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT	(-1) | #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT	(-1) | ||||||
| 
 | 
 | ||||||
| #define PM_QOS_FLAG_NO_POWER_OFF	(1 << 0) | #define PM_QOS_FLAG_NO_POWER_OFF	(1 << 0) | ||||||
|  | @ -58,6 +60,8 @@ struct pm_qos_flags_request { | ||||||
| enum dev_pm_qos_req_type { | enum dev_pm_qos_req_type { | ||||||
| 	DEV_PM_QOS_RESUME_LATENCY = 1, | 	DEV_PM_QOS_RESUME_LATENCY = 1, | ||||||
| 	DEV_PM_QOS_LATENCY_TOLERANCE, | 	DEV_PM_QOS_LATENCY_TOLERANCE, | ||||||
|  | 	DEV_PM_QOS_MIN_FREQUENCY, | ||||||
|  | 	DEV_PM_QOS_MAX_FREQUENCY, | ||||||
| 	DEV_PM_QOS_FLAGS, | 	DEV_PM_QOS_FLAGS, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -99,10 +103,14 @@ struct pm_qos_flags { | ||||||
| struct dev_pm_qos { | struct dev_pm_qos { | ||||||
| 	struct pm_qos_constraints resume_latency; | 	struct pm_qos_constraints resume_latency; | ||||||
| 	struct pm_qos_constraints latency_tolerance; | 	struct pm_qos_constraints latency_tolerance; | ||||||
|  | 	struct pm_qos_constraints min_frequency; | ||||||
|  | 	struct pm_qos_constraints max_frequency; | ||||||
| 	struct pm_qos_flags flags; | 	struct pm_qos_flags flags; | ||||||
| 	struct dev_pm_qos_request *resume_latency_req; | 	struct dev_pm_qos_request *resume_latency_req; | ||||||
| 	struct dev_pm_qos_request *latency_tolerance_req; | 	struct dev_pm_qos_request *latency_tolerance_req; | ||||||
| 	struct dev_pm_qos_request *flags_req; | 	struct dev_pm_qos_request *flags_req; | ||||||
|  | 	struct dev_pm_qos_request *min_frequency_req; | ||||||
|  | 	struct dev_pm_qos_request *max_frequency_req; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Action requested to pm_qos_update_target */ | /* Action requested to pm_qos_update_target */ | ||||||
|  | @ -197,6 +205,10 @@ static inline s32 dev_pm_qos_read_value(struct device *dev, | ||||||
| 	switch (type) { | 	switch (type) { | ||||||
| 	case DEV_PM_QOS_RESUME_LATENCY: | 	case DEV_PM_QOS_RESUME_LATENCY: | ||||||
| 		return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; | 		return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; | ||||||
|  | 	case DEV_PM_QOS_MIN_FREQUENCY: | ||||||
|  | 		return PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | ||||||
|  | 	case DEV_PM_QOS_MAX_FREQUENCY: | ||||||
|  | 		return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | ||||||
| 	default: | 	default: | ||||||
| 		WARN_ON(1); | 		WARN_ON(1); | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Viresh Kumar
						Viresh Kumar