mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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");
 | 
			
		||||
 | 
			
		||||
	devfreq->previous_freq = new_freq;
 | 
			
		||||
 | 
			
		||||
	if (devfreq->suspend_freq)
 | 
			
		||||
		devfreq->resume_freq = cur_freq;
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -667,6 +671,9 @@ struct devfreq *devfreq_add_device(struct device *dev,
 | 
			
		|||
	}
 | 
			
		||||
	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",
 | 
			
		||||
				atomic_inc_return(&devfreq_no));
 | 
			
		||||
	err = device_register(&devfreq->dev);
 | 
			
		||||
| 
						 | 
				
			
			@ -867,14 +874,28 @@ EXPORT_SYMBOL(devm_devfreq_remove_device);
 | 
			
		|||
 */
 | 
			
		||||
int devfreq_suspend_device(struct devfreq *devfreq)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!devfreq)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!devfreq->governor)
 | 
			
		||||
	if (atomic_inc_return(&devfreq->suspend_count) > 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	return devfreq->governor->event_handler(devfreq,
 | 
			
		||||
	if (devfreq->governor) {
 | 
			
		||||
		ret = devfreq->governor->event_handler(devfreq,
 | 
			
		||||
					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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -888,14 +909,28 @@ EXPORT_SYMBOL(devfreq_suspend_device);
 | 
			
		|||
 */
 | 
			
		||||
int devfreq_resume_device(struct devfreq *devfreq)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!devfreq)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!devfreq->governor)
 | 
			
		||||
	if (atomic_dec_return(&devfreq->suspend_count) >= 1)
 | 
			
		||||
		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);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(devfreq_resume_device);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,6 +131,9 @@ struct devfreq_dev_profile {
 | 
			
		|||
 * @scaling_min_freq:	Limit minimum frequency requested by OPP interface
 | 
			
		||||
 * @scaling_max_freq:	Limit maximum frequency requested by OPP interface
 | 
			
		||||
 * @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
 | 
			
		||||
 * @trans_table:	Statistics of devfreq transitions
 | 
			
		||||
 * @time_in_state:	Statistics of devfreq states
 | 
			
		||||
| 
						 | 
				
			
			@ -167,6 +170,10 @@ struct devfreq {
 | 
			
		|||
	unsigned long scaling_max_freq;
 | 
			
		||||
	bool stop_polling;
 | 
			
		||||
 | 
			
		||||
	unsigned long suspend_freq;
 | 
			
		||||
	unsigned long resume_freq;
 | 
			
		||||
	atomic_t suspend_count;
 | 
			
		||||
 | 
			
		||||
	/* information for device frequency transition */
 | 
			
		||||
	unsigned int total_trans;
 | 
			
		||||
	unsigned int *trans_table;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue