forked from mirrors/linux
		
	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);
 | 
						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 */
 | 
					/* 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)
 | 
					int update_devfreq(struct devfreq *devfreq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned long freq;
 | 
						struct devfreq_freqs freqs;
 | 
				
			||||||
 | 
						unsigned long freq, cur_freq;
 | 
				
			||||||
	int err = 0;
 | 
						int err = 0;
 | 
				
			||||||
	u32 flags = 0;
 | 
						u32 flags = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq)
 | 
				
			||||||
		flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
 | 
							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);
 | 
						err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						freqs.new = freq;
 | 
				
			||||||
 | 
						devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (devfreq->profile->freq_table)
 | 
						if (devfreq->profile->freq_table)
 | 
				
			||||||
		if (devfreq_update_status(devfreq, freq))
 | 
							if (devfreq_update_status(devfreq, freq))
 | 
				
			||||||
			dev_err(&devfreq->dev,
 | 
								dev_err(&devfreq->dev,
 | 
				
			||||||
| 
						 | 
					@ -542,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev,
 | 
				
			||||||
		goto err_out;
 | 
							goto err_out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srcu_init_notifier_head(&devfreq->transition_notifier_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_unlock(&devfreq->lock);
 | 
						mutex_unlock(&devfreq->lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&devfreq_list_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);
 | 
					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_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 | 
				
			||||||
MODULE_DESCRIPTION("devfreq class support");
 | 
					MODULE_DESCRIPTION("devfreq class support");
 | 
				
			||||||
MODULE_LICENSE("GPL");
 | 
					MODULE_LICENSE("GPL");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,13 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEVFREQ_NAME_LEN 16
 | 
					#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;
 | 
					struct devfreq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -143,6 +150,7 @@ struct devfreq_governor {
 | 
				
			||||||
 * @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
 | 
				
			||||||
 * @last_stat_updated:	The last time stat updated
 | 
					 * @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.
 | 
					 * This structure stores the devfreq information for a give device.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -177,6 +185,13 @@ struct devfreq {
 | 
				
			||||||
	unsigned int *trans_table;
 | 
						unsigned int *trans_table;
 | 
				
			||||||
	unsigned long *time_in_state;
 | 
						unsigned long *time_in_state;
 | 
				
			||||||
	unsigned long last_stat_updated;
 | 
						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)
 | 
					#if defined(CONFIG_PM_DEVFREQ)
 | 
				
			||||||
| 
						 | 
					@ -207,7 +222,20 @@ extern int devm_devfreq_register_opp_notifier(struct device *dev,
 | 
				
			||||||
					      struct devfreq *devfreq);
 | 
										      struct devfreq *devfreq);
 | 
				
			||||||
extern void devm_devfreq_unregister_opp_notifier(struct device *dev,
 | 
					extern void devm_devfreq_unregister_opp_notifier(struct device *dev,
 | 
				
			||||||
						struct devfreq *devfreq);
 | 
											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,
 | 
					extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
 | 
				
			||||||
						int index);
 | 
											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,
 | 
					static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
 | 
				
			||||||
							int index)
 | 
												int index)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue