diff options
Diffstat (limited to 'drivers/regulator/core.c')
| -rw-r--r-- | drivers/regulator/core.c | 244 | 
1 files changed, 203 insertions, 41 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d4803460a557..6ed568b96c0e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -147,6 +147,56 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)  }  /** + * regulator_lock_nested - lock a single regulator + * @rdev:		regulator source + * @subclass:		mutex subclass used for lockdep + * + * This function can be called many times by one task on + * a single regulator and its mutex will be locked only + * once. If a task, which is calling this function is other + * than the one, which initially locked the mutex, it will + * wait on mutex. + */ +static void regulator_lock_nested(struct regulator_dev *rdev, +				  unsigned int subclass) +{ +	if (!mutex_trylock(&rdev->mutex)) { +		if (rdev->mutex_owner == current) { +			rdev->ref_cnt++; +			return; +		} +		mutex_lock_nested(&rdev->mutex, subclass); +	} + +	rdev->ref_cnt = 1; +	rdev->mutex_owner = current; +} + +static inline void regulator_lock(struct regulator_dev *rdev) +{ +	regulator_lock_nested(rdev, 0); +} + +/** + * regulator_unlock - unlock a single regulator + * @rdev:		regulator_source + * + * This function unlocks the mutex when the + * reference counter reaches 0. + */ +static void regulator_unlock(struct regulator_dev *rdev) +{ +	if (rdev->ref_cnt != 0) { +		rdev->ref_cnt--; + +		if (!rdev->ref_cnt) { +			rdev->mutex_owner = NULL; +			mutex_unlock(&rdev->mutex); +		} +	} +} + +/**   * regulator_lock_supply - lock a regulator and its supplies   * @rdev:         regulator source   */ @@ -155,7 +205,7 @@ static void regulator_lock_supply(struct regulator_dev *rdev)  	int i;  	for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++) -		mutex_lock_nested(&rdev->mutex, i); +		regulator_lock_nested(rdev, i);  }  /** @@ -167,7 +217,7 @@ static void regulator_unlock_supply(struct regulator_dev *rdev)  	struct regulator *supply;  	while (1) { -		mutex_unlock(&rdev->mutex); +		regulator_unlock(rdev);  		supply = rdev->supply;  		if (!rdev->supply) @@ -350,9 +400,9 @@ static ssize_t regulator_uV_show(struct device *dev,  	struct regulator_dev *rdev = dev_get_drvdata(dev);  	ssize_t ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev)); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -416,9 +466,9 @@ static ssize_t regulator_state_show(struct device *dev,  	struct regulator_dev *rdev = dev_get_drvdata(dev);  	ssize_t ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	ret = regulator_print_state(buf, _regulator_is_enabled(rdev)); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -526,10 +576,10 @@ static ssize_t regulator_total_uA_show(struct device *dev,  	struct regulator *regulator;  	int uA = 0; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	list_for_each_entry(regulator, &rdev->consumer_list, list)  		uA += regulator->uA_load; -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return sprintf(buf, "%d\n", uA);  }  static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL); @@ -886,6 +936,18 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,  	    rdev->constraints->min_uV && rdev->constraints->max_uV) {  		int target_min, target_max;  		int current_uV = _regulator_get_voltage(rdev); + +		if (current_uV == -ENOTRECOVERABLE) { +			/* This regulator can't be read and must be initted */ +			rdev_info(rdev, "Setting %d-%duV\n", +				  rdev->constraints->min_uV, +				  rdev->constraints->max_uV); +			_regulator_do_set_voltage(rdev, +						  rdev->constraints->min_uV, +						  rdev->constraints->max_uV); +			current_uV = _regulator_get_voltage(rdev); +		} +  		if (current_uV < 0) {  			rdev_err(rdev,  				 "failed to get the current voltage(%d)\n", @@ -1321,7 +1383,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,  	if (regulator == NULL)  		return NULL; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	regulator->rdev = rdev;  	list_add(®ulator->list, &rdev->consumer_list); @@ -1376,12 +1438,12 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,  	    _regulator_is_enabled(rdev))  		regulator->always_on = true; -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return regulator;  overflow_err:  	list_del(®ulator->list);  	kfree(regulator); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return NULL;  } @@ -1770,13 +1832,13 @@ static void _regulator_put(struct regulator *regulator)  	/* remove any sysfs entries */  	if (regulator->dev)  		sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	list_del(®ulator->list);  	rdev->open_count--;  	rdev->exclusive = 0;  	put_device(&rdev->dev); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	kfree_const(regulator->supply_name);  	kfree(regulator); @@ -2384,7 +2446,7 @@ static void regulator_disable_work(struct work_struct *work)  						  disable_work.work);  	int count, i, ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	BUG_ON(!rdev->deferred_disables); @@ -2405,7 +2467,7 @@ static void regulator_disable_work(struct work_struct *work)  			rdev_err(rdev, "Deferred disable failed: %d\n", ret);  	} -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	if (rdev->supply) {  		for (i = 0; i < count; i++) { @@ -2440,11 +2502,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)  	if (!ms)  		return regulator_disable(regulator); -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	rdev->deferred_disables++;  	mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,  			 msecs_to_jiffies(ms)); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return 0;  } @@ -2476,10 +2538,10 @@ static int _regulator_list_voltage(struct regulator_dev *rdev,  		if (selector >= rdev->desc->n_voltages)  			return -EINVAL;  		if (lock) -			mutex_lock(&rdev->mutex); +			regulator_lock(rdev);  		ret = ops->list_voltage(rdev, selector);  		if (lock) -			mutex_unlock(&rdev->mutex); +			regulator_unlock(rdev);  	} else if (rdev->is_switch && rdev->supply) {  		ret = _regulator_list_voltage(rdev->supply->rdev,  					      selector, lock); @@ -3252,7 +3314,7 @@ int regulator_sync_voltage(struct regulator *regulator)  	struct regulator_voltage *voltage = ®ulator->voltage[PM_SUSPEND_ON];  	int ret, min_uV, max_uV; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	if (!rdev->desc->ops->set_voltage &&  	    !rdev->desc->ops->set_voltage_sel) { @@ -3281,7 +3343,7 @@ int regulator_sync_voltage(struct regulator *regulator)  	ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);  out: -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  }  EXPORT_SYMBOL_GPL(regulator_sync_voltage); @@ -3374,7 +3436,7 @@ int regulator_set_current_limit(struct regulator *regulator,  	struct regulator_dev *rdev = regulator->rdev;  	int ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	/* sanity check */  	if (!rdev->desc->ops->set_current_limit) { @@ -3389,7 +3451,7 @@ int regulator_set_current_limit(struct regulator *regulator,  	ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA);  out: -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  }  EXPORT_SYMBOL_GPL(regulator_set_current_limit); @@ -3398,7 +3460,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev)  {  	int ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	/* sanity check */  	if (!rdev->desc->ops->get_current_limit) { @@ -3408,7 +3470,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev)  	ret = rdev->desc->ops->get_current_limit(rdev);  out: -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -3444,7 +3506,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)  	int ret;  	int regulator_curr_mode; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	/* sanity check */  	if (!rdev->desc->ops->set_mode) { @@ -3468,7 +3530,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)  	ret = rdev->desc->ops->set_mode(rdev, mode);  out: -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  }  EXPORT_SYMBOL_GPL(regulator_set_mode); @@ -3477,7 +3539,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev)  {  	int ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	/* sanity check */  	if (!rdev->desc->ops->get_mode) { @@ -3487,7 +3549,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev)  	ret = rdev->desc->ops->get_mode(rdev);  out: -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -3508,7 +3570,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev,  {  	int ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	/* sanity check */  	if (!rdev->desc->ops->get_error_flags) { @@ -3518,7 +3580,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev,  	ret = rdev->desc->ops->get_error_flags(rdev, flags);  out: -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -3567,10 +3629,10 @@ int regulator_set_load(struct regulator *regulator, int uA_load)  	struct regulator_dev *rdev = regulator->rdev;  	int ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	regulator->uA_load = uA_load;  	ret = drms_uA_update(rdev); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -3598,7 +3660,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)  	if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS))  		return 0; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	if (enable && !regulator->bypass) {  		rdev->bypass_count++; @@ -3622,7 +3684,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)  	if (ret == 0)  		regulator->bypass = enable; -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -4067,6 +4129,96 @@ static int regulator_register_resolve_supply(struct device *dev, void *data)  	return 0;  } +static int regulator_fill_coupling_array(struct regulator_dev *rdev) +{ +	struct coupling_desc *c_desc = &rdev->coupling_desc; +	int n_coupled = c_desc->n_coupled; +	struct regulator_dev *c_rdev; +	int i; + +	for (i = 1; i < n_coupled; i++) { +		/* already resolved */ +		if (c_desc->coupled_rdevs[i]) +			continue; + +		c_rdev = of_parse_coupled_regulator(rdev, i - 1); + +		if (c_rdev) { +			c_desc->coupled_rdevs[i] = c_rdev; +			c_desc->n_resolved++; +		} +	} + +	if (rdev->coupling_desc.n_resolved < n_coupled) +		return -1; +	else +		return 0; +} + +static int regulator_register_fill_coupling_array(struct device *dev, +						  void *data) +{ +	struct regulator_dev *rdev = dev_to_rdev(dev); + +	if (!IS_ENABLED(CONFIG_OF)) +		return 0; + +	if (regulator_fill_coupling_array(rdev)) +		rdev_dbg(rdev, "unable to resolve coupling\n"); + +	return 0; +} + +static int regulator_resolve_coupling(struct regulator_dev *rdev) +{ +	int n_phandles; + +	if (!IS_ENABLED(CONFIG_OF)) +		n_phandles = 0; +	else +		n_phandles = of_get_n_coupled(rdev); + +	if (n_phandles + 1 > MAX_COUPLED) { +		rdev_err(rdev, "too many regulators coupled\n"); +		return -EPERM; +	} + +	/* +	 * Every regulator should always have coupling descriptor filled with +	 * at least pointer to itself. +	 */ +	rdev->coupling_desc.coupled_rdevs[0] = rdev; +	rdev->coupling_desc.n_coupled = n_phandles + 1; +	rdev->coupling_desc.n_resolved++; + +	/* regulator isn't coupled */ +	if (n_phandles == 0) +		return 0; + +	/* regulator, which can't change its voltage, can't be coupled */ +	if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { +		rdev_err(rdev, "voltage operation not allowed\n"); +		return -EPERM; +	} + +	if (rdev->constraints->max_spread <= 0) { +		rdev_err(rdev, "wrong max_spread value\n"); +		return -EPERM; +	} + +	if (!of_check_coupling_data(rdev)) +		return -EPERM; + +	/* +	 * After everything has been checked, try to fill rdevs array +	 * with pointers to regulators parsed from device tree. If some +	 * regulators are not registered yet, retry in late init call +	 */ +	regulator_fill_coupling_array(rdev); + +	return 0; +} +  /**   * regulator_register - register regulator   * @regulator_desc: regulator to register @@ -4200,6 +4352,13 @@ regulator_register(const struct regulator_desc *regulator_desc,  	if (ret < 0)  		goto wash; +	mutex_lock(®ulator_list_mutex); +	ret = regulator_resolve_coupling(rdev); +	mutex_unlock(®ulator_list_mutex); + +	if (ret != 0) +		goto wash; +  	/* add consumers devices */  	if (init_data) {  		mutex_lock(®ulator_list_mutex); @@ -4288,9 +4447,9 @@ static int _regulator_suspend_late(struct device *dev, void *data)  	suspend_state_t *state = data;  	int ret; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	ret = suspend_set_state(rdev, *state); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -4320,14 +4479,14 @@ static int _regulator_resume_early(struct device *dev, void *data)  	if (rstate == NULL)  		return 0; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	if (rdev->desc->ops->resume_early &&  	    (rstate->enabled == ENABLE_IN_SUSPEND ||  	     rstate->enabled == DISABLE_IN_SUSPEND))  		ret = rdev->desc->ops->resume_early(rdev); -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return ret;  } @@ -4629,7 +4788,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data)  	if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS))  		return 0; -	mutex_lock(&rdev->mutex); +	regulator_lock(rdev);  	if (rdev->use_count)  		goto unlock; @@ -4660,7 +4819,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data)  	}  unlock: -	mutex_unlock(&rdev->mutex); +	regulator_unlock(rdev);  	return 0;  } @@ -4694,6 +4853,9 @@ static int __init regulator_init_complete(void)  	class_for_each_device(®ulator_class, NULL, NULL,  			      regulator_late_cleanup); +	class_for_each_device(®ulator_class, NULL, NULL, +			      regulator_register_fill_coupling_array); +  	return 0;  }  late_initcall_sync(regulator_init_complete);  |