diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
| -rw-r--r-- | drivers/devfreq/devfreq.c | 114 | 
1 files changed, 52 insertions, 62 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 47206a21bb90..551a271353d2 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -111,18 +111,16 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)  		return;  	} -	rcu_read_lock();  	for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {  		opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq);  		if (IS_ERR(opp)) {  			devm_kfree(devfreq->dev.parent, profile->freq_table);  			profile->max_state = 0; -			rcu_read_unlock();  			return;  		} +		dev_pm_opp_put(opp);  		profile->freq_table[i] = freq;  	} -	rcu_read_unlock();  }  /** @@ -130,7 +128,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)   * @devfreq:	the devfreq instance   * @freq:	the update target frequency   */ -static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) +int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)  {  	int lev, prev_lev, ret = 0;  	unsigned long cur_time; @@ -166,6 +164,7 @@ out:  	devfreq->last_stat_updated = cur_time;  	return ret;  } +EXPORT_SYMBOL(devfreq_update_status);  /**   * find_devfreq_governor() - find devfreq governor from name @@ -474,11 +473,15 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,  }  /** - * _remove_devfreq() - Remove devfreq from the list and release its resources. - * @devfreq:	the devfreq struct + * devfreq_dev_release() - Callback for struct device to release the device. + * @dev:	the devfreq device + * + * Remove devfreq from the list and release its resources.   */ -static void _remove_devfreq(struct devfreq *devfreq) +static void devfreq_dev_release(struct device *dev)  { +	struct devfreq *devfreq = to_devfreq(dev); +  	mutex_lock(&devfreq_list_lock);  	if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {  		mutex_unlock(&devfreq_list_lock); @@ -500,19 +503,6 @@ static void _remove_devfreq(struct devfreq *devfreq)  }  /** - * devfreq_dev_release() - Callback for struct device to release the device. - * @dev:	the devfreq device - * - * This calls _remove_devfreq() if _remove_devfreq() is not called. - */ -static void devfreq_dev_release(struct device *dev) -{ -	struct devfreq *devfreq = to_devfreq(dev); - -	_remove_devfreq(devfreq); -} - -/**   * devfreq_add_device() - Add devfreq feature to the device   * @dev:	the device to add devfreq feature.   * @profile:	device-specific profile to run devfreq. @@ -527,6 +517,7 @@ struct devfreq *devfreq_add_device(struct device *dev,  {  	struct devfreq *devfreq;  	struct devfreq_governor *governor; +	static atomic_t devfreq_no = ATOMIC_INIT(-1);  	int err = 0;  	if (!dev || !profile || !governor_name) { @@ -538,15 +529,14 @@ struct devfreq *devfreq_add_device(struct device *dev,  	devfreq = find_device_devfreq(dev);  	mutex_unlock(&devfreq_list_lock);  	if (!IS_ERR(devfreq)) { -		dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__); +		dev_err(dev, "%s: Unable to create devfreq for the device.\n", +			__func__);  		err = -EINVAL;  		goto err_out;  	}  	devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);  	if (!devfreq) { -		dev_err(dev, "%s: Unable to create devfreq for the device\n", -			__func__);  		err = -ENOMEM;  		goto err_out;  	} @@ -569,18 +559,21 @@ struct devfreq *devfreq_add_device(struct device *dev,  		mutex_lock(&devfreq->lock);  	} -	dev_set_name(&devfreq->dev, "%s", dev_name(dev)); +	dev_set_name(&devfreq->dev, "devfreq%d", +				atomic_inc_return(&devfreq_no));  	err = device_register(&devfreq->dev);  	if (err) {  		mutex_unlock(&devfreq->lock);  		goto err_out;  	} -	devfreq->trans_table =	devm_kzalloc(&devfreq->dev, sizeof(unsigned int) * +	devfreq->trans_table =	devm_kzalloc(&devfreq->dev, +						sizeof(unsigned int) *  						devfreq->profile->max_state *  						devfreq->profile->max_state,  						GFP_KERNEL); -	devfreq->time_in_state = devm_kzalloc(&devfreq->dev, sizeof(unsigned long) * +	devfreq->time_in_state = devm_kzalloc(&devfreq->dev, +						sizeof(unsigned long) *  						devfreq->profile->max_state,  						GFP_KERNEL);  	devfreq->last_stat_updated = jiffies; @@ -939,6 +932,9 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,  	if (df->governor == governor) {  		ret = 0;  		goto out; +	} else if (df->governor->immutable || governor->immutable) { +		ret = -EINVAL; +		goto out;  	}  	if (df->governor) { @@ -968,13 +964,33 @@ static ssize_t available_governors_show(struct device *d,  					struct device_attribute *attr,  					char *buf)  { -	struct devfreq_governor *tmp_governor; +	struct devfreq *df = to_devfreq(d);  	ssize_t count = 0;  	mutex_lock(&devfreq_list_lock); -	list_for_each_entry(tmp_governor, &devfreq_governor_list, node) -		count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), -				   "%s ", tmp_governor->name); + +	/* +	 * The devfreq with immutable governor (e.g., passive) shows +	 * only own governor. +	 */ +	if (df->governor->immutable) { +		count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, +				   "%s ", df->governor_name); +	/* +	 * The devfreq device shows the registered governor except for +	 * immutable governors such as passive governor . +	 */ +	} else { +		struct devfreq_governor *governor; + +		list_for_each_entry(governor, &devfreq_governor_list, node) { +			if (governor->immutable) +				continue; +			count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), +					   "%s ", governor->name); +		} +	} +  	mutex_unlock(&devfreq_list_lock);  	/* Truncate the trailing space */ @@ -995,7 +1011,7 @@ static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr,  	if (devfreq->profile->get_cur_freq &&  		!devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) -			return sprintf(buf, "%lu\n", freq); +		return sprintf(buf, "%lu\n", freq);  	return sprintf(buf, "%lu\n", devfreq->previous_freq);  } @@ -1112,17 +1128,16 @@ static ssize_t available_frequencies_show(struct device *d,  	ssize_t count = 0;  	unsigned long freq = 0; -	rcu_read_lock();  	do {  		opp = dev_pm_opp_find_freq_ceil(dev, &freq);  		if (IS_ERR(opp))  			break; +		dev_pm_opp_put(opp);  		count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),  				   "%lu ", freq);  		freq++;  	} while (1); -	rcu_read_unlock();  	/* Truncate the trailing space */  	if (count) @@ -1224,11 +1239,8 @@ subsys_initcall(devfreq_init);   * @freq:	The frequency given to target function   * @flags:	Flags handed from devfreq framework.   * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use.   */  struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,  					   unsigned long *freq, @@ -1265,18 +1277,7 @@ EXPORT_SYMBOL(devfreq_recommended_opp);   */  int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)  { -	struct srcu_notifier_head *nh; -	int ret = 0; - -	rcu_read_lock(); -	nh = dev_pm_opp_get_notifier(dev); -	if (IS_ERR(nh)) -		ret = PTR_ERR(nh); -	rcu_read_unlock(); -	if (!ret) -		ret = srcu_notifier_chain_register(nh, &devfreq->nb); - -	return ret; +	return dev_pm_opp_register_notifier(dev, &devfreq->nb);  }  EXPORT_SYMBOL(devfreq_register_opp_notifier); @@ -1292,18 +1293,7 @@ EXPORT_SYMBOL(devfreq_register_opp_notifier);   */  int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)  { -	struct srcu_notifier_head *nh; -	int ret = 0; - -	rcu_read_lock(); -	nh = dev_pm_opp_get_notifier(dev); -	if (IS_ERR(nh)) -		ret = PTR_ERR(nh); -	rcu_read_unlock(); -	if (!ret) -		ret = srcu_notifier_chain_unregister(nh, &devfreq->nb); - -	return ret; +	return dev_pm_opp_unregister_notifier(dev, &devfreq->nb);  }  EXPORT_SYMBOL(devfreq_unregister_opp_notifier);  | 
