forked from mirrors/linux
		
	PM / devfreq: add support for suspend/resume of a devfreq device
The patch prepares devfreq device for handling suspend/resume functionality. The new fields will store needed information during this process. Devfreq framework handles opp-suspend DT entry and there is no need of modyfications in the drivers code. It uses atomic variables to make sure no race condition affects the process. Suggested-by: Tobias Jakobi <tjakobi@math.uni-bielefeld.de> Suggested-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
This commit is contained in:
		
							parent
							
								
									633141721b
								
							
						
					
					
						commit
						83f8ca45af
					
				
					 2 changed files with 48 additions and 6 deletions
				
			
		|  | @ -316,6 +316,10 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, | ||||||
| 			"Couldn't update frequency transition information.\n"); | 			"Couldn't update frequency transition information.\n"); | ||||||
| 
 | 
 | ||||||
| 	devfreq->previous_freq = new_freq; | 	devfreq->previous_freq = new_freq; | ||||||
|  | 
 | ||||||
|  | 	if (devfreq->suspend_freq) | ||||||
|  | 		devfreq->resume_freq = cur_freq; | ||||||
|  | 
 | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -667,6 +671,9 @@ struct devfreq *devfreq_add_device(struct device *dev, | ||||||
| 	} | 	} | ||||||
| 	devfreq->max_freq = devfreq->scaling_max_freq; | 	devfreq->max_freq = devfreq->scaling_max_freq; | ||||||
| 
 | 
 | ||||||
|  | 	devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); | ||||||
|  | 	atomic_set(&devfreq->suspend_count, 0); | ||||||
|  | 
 | ||||||
| 	dev_set_name(&devfreq->dev, "devfreq%d", | 	dev_set_name(&devfreq->dev, "devfreq%d", | ||||||
| 				atomic_inc_return(&devfreq_no)); | 				atomic_inc_return(&devfreq_no)); | ||||||
| 	err = device_register(&devfreq->dev); | 	err = device_register(&devfreq->dev); | ||||||
|  | @ -867,14 +874,28 @@ EXPORT_SYMBOL(devm_devfreq_remove_device); | ||||||
|  */ |  */ | ||||||
| int devfreq_suspend_device(struct devfreq *devfreq) | int devfreq_suspend_device(struct devfreq *devfreq) | ||||||
| { | { | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
| 	if (!devfreq) | 	if (!devfreq) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	if (!devfreq->governor) | 	if (atomic_inc_return(&devfreq->suspend_count) > 1) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	return devfreq->governor->event_handler(devfreq, | 	if (devfreq->governor) { | ||||||
|  | 		ret = devfreq->governor->event_handler(devfreq, | ||||||
| 					DEVFREQ_GOV_SUSPEND, NULL); | 					DEVFREQ_GOV_SUSPEND, NULL); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (devfreq->suspend_freq) { | ||||||
|  | 		ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(devfreq_suspend_device); | EXPORT_SYMBOL(devfreq_suspend_device); | ||||||
| 
 | 
 | ||||||
|  | @ -888,14 +909,28 @@ EXPORT_SYMBOL(devfreq_suspend_device); | ||||||
|  */ |  */ | ||||||
| int devfreq_resume_device(struct devfreq *devfreq) | int devfreq_resume_device(struct devfreq *devfreq) | ||||||
| { | { | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
| 	if (!devfreq) | 	if (!devfreq) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	if (!devfreq->governor) | 	if (atomic_dec_return(&devfreq->suspend_count) >= 1) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	return devfreq->governor->event_handler(devfreq, | 	if (devfreq->resume_freq) { | ||||||
|  | 		ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (devfreq->governor) { | ||||||
|  | 		ret = devfreq->governor->event_handler(devfreq, | ||||||
| 					DEVFREQ_GOV_RESUME, NULL); | 					DEVFREQ_GOV_RESUME, NULL); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(devfreq_resume_device); | EXPORT_SYMBOL(devfreq_resume_device); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -131,6 +131,9 @@ struct devfreq_dev_profile { | ||||||
|  * @scaling_min_freq:	Limit minimum frequency requested by OPP interface |  * @scaling_min_freq:	Limit minimum frequency requested by OPP interface | ||||||
|  * @scaling_max_freq:	Limit maximum frequency requested by OPP interface |  * @scaling_max_freq:	Limit maximum frequency requested by OPP interface | ||||||
|  * @stop_polling:	 devfreq polling status of a device. |  * @stop_polling:	 devfreq polling status of a device. | ||||||
|  |  * @suspend_freq:	 frequency of a device set during suspend phase. | ||||||
|  |  * @resume_freq:	 frequency of a device set in resume phase. | ||||||
|  |  * @suspend_count:	 suspend requests counter for a device. | ||||||
|  * @total_trans:	Number of devfreq transitions |  * @total_trans:	Number of devfreq transitions | ||||||
|  * @trans_table:	Statistics of devfreq transitions |  * @trans_table:	Statistics of devfreq transitions | ||||||
|  * @time_in_state:	Statistics of devfreq states |  * @time_in_state:	Statistics of devfreq states | ||||||
|  | @ -167,6 +170,10 @@ struct devfreq { | ||||||
| 	unsigned long scaling_max_freq; | 	unsigned long scaling_max_freq; | ||||||
| 	bool stop_polling; | 	bool stop_polling; | ||||||
| 
 | 
 | ||||||
|  | 	unsigned long suspend_freq; | ||||||
|  | 	unsigned long resume_freq; | ||||||
|  | 	atomic_t suspend_count; | ||||||
|  | 
 | ||||||
| 	/* information for device frequency transition */ | 	/* information for device frequency transition */ | ||||||
| 	unsigned int total_trans; | 	unsigned int total_trans; | ||||||
| 	unsigned int *trans_table; | 	unsigned int *trans_table; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Lukasz Luba
						Lukasz Luba