mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	PM / devfreq: Add new DEVFREQ_TRANSITION_NOTIFIER notifier
This patch adds the new DEVFREQ_TRANSITION_NOTIFIER notifier to send the notification when the frequency of device is changed. This notifier has two state as following: - DEVFREQ_PRECHANGE : Notify it before chaning the frequency of device - DEVFREQ_POSTCHANGE : Notify it after changed the frequency of device And this patch adds the resourced-managed function to release the resource automatically when error happen. Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> [m.reichl and linux.amoon: Tested it on exynos4412-odroidu3 board] Tested-by: Markus Reichl <m.reichl@fivetechno.de> Tested-by: Anand Moon <linux.amoon@gmail.com> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Acked-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
This commit is contained in:
		
							parent
							
								
									8f510aeb22
								
							
						
					
					
						commit
						0fe3a66410
					
				
					 2 changed files with 220 additions and 2 deletions
				
			
		|  | @ -189,6 +189,29 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
| 	return ERR_PTR(-ENODEV); | ||||
| } | ||||
| 
 | ||||
| static int devfreq_notify_transition(struct devfreq *devfreq, | ||||
| 		struct devfreq_freqs *freqs, unsigned int state) | ||||
| { | ||||
| 	if (!devfreq) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	switch (state) { | ||||
| 	case DEVFREQ_PRECHANGE: | ||||
| 		srcu_notifier_call_chain(&devfreq->transition_notifier_list, | ||||
| 				DEVFREQ_PRECHANGE, freqs); | ||||
| 		break; | ||||
| 
 | ||||
| 	case DEVFREQ_POSTCHANGE: | ||||
| 		srcu_notifier_call_chain(&devfreq->transition_notifier_list, | ||||
| 				DEVFREQ_POSTCHANGE, freqs); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Load monitoring helper functions for governors use */ | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -200,7 +223,8 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
|  */ | ||||
| int update_devfreq(struct devfreq *devfreq) | ||||
| { | ||||
| 	unsigned long freq; | ||||
| 	struct devfreq_freqs freqs; | ||||
| 	unsigned long freq, cur_freq; | ||||
| 	int err = 0; | ||||
| 	u32 flags = 0; | ||||
| 
 | ||||
|  | @ -234,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq) | |||
| 		flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ | ||||
| 	} | ||||
| 
 | ||||
| 	if (devfreq->profile->get_cur_freq) | ||||
| 		devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); | ||||
| 	else | ||||
| 		cur_freq = devfreq->previous_freq; | ||||
| 
 | ||||
| 	freqs.old = cur_freq; | ||||
| 	freqs.new = freq; | ||||
| 	devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); | ||||
| 
 | ||||
| 	err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	freqs.new = freq; | ||||
| 	devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); | ||||
| 
 | ||||
| 	if (devfreq->profile->freq_table) | ||||
| 		if (devfreq_update_status(devfreq, freq)) | ||||
| 			dev_err(&devfreq->dev, | ||||
|  | @ -542,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev, | |||
| 		goto err_out; | ||||
| 	} | ||||
| 
 | ||||
| 	srcu_init_notifier_head(&devfreq->transition_notifier_list); | ||||
| 
 | ||||
| 	mutex_unlock(&devfreq->lock); | ||||
| 
 | ||||
| 	mutex_lock(&devfreq_list_lock); | ||||
|  | @ -1310,6 +1348,129 @@ void devm_devfreq_unregister_opp_notifier(struct device *dev, | |||
| } | ||||
| 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. | ||||
|  */ | ||||
| int devfreq_register_notifier(struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (!devfreq) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	switch (list) { | ||||
| 	case DEVFREQ_TRANSITION_NOTIFIER: | ||||
| 		ret = srcu_notifier_chain_register( | ||||
| 				&devfreq->transition_notifier_list, nb); | ||||
| 		break; | ||||
| 	default: | ||||
| 		ret = -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 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. | ||||
|  */ | ||||
| int devfreq_unregister_notifier(struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (!devfreq) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	switch (list) { | ||||
| 	case DEVFREQ_TRANSITION_NOTIFIER: | ||||
| 		ret = srcu_notifier_chain_unregister( | ||||
| 				&devfreq->transition_notifier_list, nb); | ||||
| 		break; | ||||
| 	default: | ||||
| 		ret = -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(devfreq_unregister_notifier); | ||||
| 
 | ||||
| struct devfreq_notifier_devres { | ||||
| 	struct devfreq *devfreq; | ||||
| 	struct notifier_block *nb; | ||||
| 	unsigned int list; | ||||
| }; | ||||
| 
 | ||||
| static void devm_devfreq_notifier_release(struct device *dev, void *res) | ||||
| { | ||||
| 	struct devfreq_notifier_devres *this = res; | ||||
| 
 | ||||
| 	devfreq_unregister_notifier(this->devfreq, this->nb, this->list); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * devm_devfreq_register_notifier() | ||||
| 	- Resource-managed devfreq_register_notifier() | ||||
|  * @dev:	The devfreq user device. (parent of devfreq) | ||||
|  * @devfreq:	The devfreq object. | ||||
|  * @nb:		The notifier block to be unregistered. | ||||
|  * @list:	DEVFREQ_TRANSITION_NOTIFIER. | ||||
|  */ | ||||
| int devm_devfreq_register_notifier(struct device *dev, | ||||
| 				struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list) | ||||
| { | ||||
| 	struct devfreq_notifier_devres *ptr; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr), | ||||
| 				GFP_KERNEL); | ||||
| 	if (!ptr) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ret = devfreq_register_notifier(devfreq, nb, list); | ||||
| 	if (ret) { | ||||
| 		devres_free(ptr); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ptr->devfreq = devfreq; | ||||
| 	ptr->nb = nb; | ||||
| 	ptr->list = list; | ||||
| 	devres_add(dev, ptr); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL(devm_devfreq_register_notifier); | ||||
| 
 | ||||
| /**
 | ||||
|  * devm_devfreq_unregister_notifier() | ||||
| 	- Resource-managed devfreq_unregister_notifier() | ||||
|  * @dev:	The devfreq user device. (parent of devfreq) | ||||
|  * @devfreq:	The devfreq object. | ||||
|  * @nb:		The notifier block to be unregistered. | ||||
|  * @list:	DEVFREQ_TRANSITION_NOTIFIER. | ||||
|  */ | ||||
| void devm_devfreq_unregister_notifier(struct device *dev, | ||||
| 				struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list) | ||||
| { | ||||
| 	WARN_ON(devres_release(dev, devm_devfreq_notifier_release, | ||||
| 			       devm_devfreq_dev_match, devfreq)); | ||||
| } | ||||
| EXPORT_SYMBOL(devm_devfreq_unregister_notifier); | ||||
| 
 | ||||
| MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||||
| MODULE_DESCRIPTION("devfreq class support"); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  |  | |||
|  | @ -19,6 +19,13 @@ | |||
| 
 | ||||
| #define DEVFREQ_NAME_LEN 16 | ||||
| 
 | ||||
| /* DEVFREQ notifier interface */ | ||||
| #define DEVFREQ_TRANSITION_NOTIFIER	(0) | ||||
| 
 | ||||
| /* Transition notifiers of DEVFREQ_TRANSITION_NOTIFIER */ | ||||
| #define	DEVFREQ_PRECHANGE		(0) | ||||
| #define DEVFREQ_POSTCHANGE		(1) | ||||
| 
 | ||||
| struct devfreq; | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -143,6 +150,7 @@ struct devfreq_governor { | |||
|  * @trans_table:	Statistics of devfreq transitions | ||||
|  * @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 | ||||
|  * | ||||
|  * This structure stores the devfreq information for a give device. | ||||
|  * | ||||
|  | @ -177,6 +185,13 @@ struct devfreq { | |||
| 	unsigned int *trans_table; | ||||
| 	unsigned long *time_in_state; | ||||
| 	unsigned long last_stat_updated; | ||||
| 
 | ||||
| 	struct srcu_notifier_head transition_notifier_list; | ||||
| }; | ||||
| 
 | ||||
| struct devfreq_freqs { | ||||
| 	unsigned long old; | ||||
| 	unsigned long new; | ||||
| }; | ||||
| 
 | ||||
| #if defined(CONFIG_PM_DEVFREQ) | ||||
|  | @ -207,7 +222,20 @@ extern int devm_devfreq_register_opp_notifier(struct device *dev, | |||
| 					      struct devfreq *devfreq); | ||||
| extern void devm_devfreq_unregister_opp_notifier(struct device *dev, | ||||
| 						struct devfreq *devfreq); | ||||
| 
 | ||||
| extern int devfreq_register_notifier(struct devfreq *devfreq, | ||||
| 					struct notifier_block *nb, | ||||
| 					unsigned int list); | ||||
| extern int devfreq_unregister_notifier(struct devfreq *devfreq, | ||||
| 					struct notifier_block *nb, | ||||
| 					unsigned int list); | ||||
| extern int devm_devfreq_register_notifier(struct device *dev, | ||||
| 				struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list); | ||||
| extern void devm_devfreq_unregister_notifier(struct device *dev, | ||||
| 				struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list); | ||||
| extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, | ||||
| 						int index); | ||||
| 
 | ||||
|  | @ -310,6 +338,35 @@ static inline void devm_devfreq_unregister_opp_notifier(struct device *dev, | |||
| { | ||||
| } | ||||
| 
 | ||||
| static inline int devfreq_register_notifier(struct devfreq *devfreq, | ||||
| 					struct notifier_block *nb, | ||||
| 					unsigned int list) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int devfreq_unregister_notifier(struct devfreq *devfreq, | ||||
| 					struct notifier_block *nb, | ||||
| 					unsigned int list) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int devm_devfreq_register_notifier(struct device *dev, | ||||
| 				struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline void devm_devfreq_unregister_notifier(struct device *dev, | ||||
| 				struct devfreq *devfreq, | ||||
| 				struct notifier_block *nb, | ||||
| 				unsigned int list) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, | ||||
| 							int index) | ||||
| { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Chanwoo Choi
						Chanwoo Choi