diff options
Diffstat (limited to 'drivers/regulator/core.c')
| -rw-r--r-- | drivers/regulator/core.c | 93 | 
1 files changed, 81 insertions, 12 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 4fcd36055b02..dc741ac156c3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -207,6 +207,71 @@ static void regulator_unlock(struct regulator_dev *rdev)  	mutex_unlock(®ulator_nesting_mutex);  } +/** + * regulator_lock_two - lock two regulators + * @rdev1:		first regulator + * @rdev2:		second regulator + * @ww_ctx:		w/w mutex acquire context + * + * Locks both rdevs using the regulator_ww_class. + */ +static void regulator_lock_two(struct regulator_dev *rdev1, +			       struct regulator_dev *rdev2, +			       struct ww_acquire_ctx *ww_ctx) +{ +	struct regulator_dev *held, *contended; +	int ret; + +	ww_acquire_init(ww_ctx, ®ulator_ww_class); + +	/* Try to just grab both of them */ +	ret = regulator_lock_nested(rdev1, ww_ctx); +	WARN_ON(ret); +	ret = regulator_lock_nested(rdev2, ww_ctx); +	if (ret != -EDEADLOCK) { +		WARN_ON(ret); +		goto exit; +	} + +	held = rdev1; +	contended = rdev2; +	while (true) { +		regulator_unlock(held); + +		ww_mutex_lock_slow(&contended->mutex, ww_ctx); +		contended->ref_cnt++; +		contended->mutex_owner = current; +		swap(held, contended); +		ret = regulator_lock_nested(contended, ww_ctx); + +		if (ret != -EDEADLOCK) { +			WARN_ON(ret); +			break; +		} +	} + +exit: +	ww_acquire_done(ww_ctx); +} + +/** + * regulator_unlock_two - unlock two regulators + * @rdev1:		first regulator + * @rdev2:		second regulator + * @ww_ctx:		w/w mutex acquire context + * + * The inverse of regulator_lock_two(). + */ + +static void regulator_unlock_two(struct regulator_dev *rdev1, +				 struct regulator_dev *rdev2, +				 struct ww_acquire_ctx *ww_ctx) +{ +	regulator_unlock(rdev2); +	regulator_unlock(rdev1); +	ww_acquire_fini(ww_ctx); +} +  static bool regulator_supply_is_couple(struct regulator_dev *rdev)  {  	struct regulator_dev *c_rdev; @@ -334,6 +399,7 @@ static void regulator_lock_dependent(struct regulator_dev *rdev,  			ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx);  			old_contended_rdev = new_contended_rdev;  			old_contended_rdev->ref_cnt++; +			old_contended_rdev->mutex_owner = current;  		}  		err = regulator_lock_recursive(rdev, @@ -1583,9 +1649,6 @@ static int set_machine_constraints(struct regulator_dev *rdev)  			rdev->constraints->always_on = true;  	} -	if (rdev->desc->off_on_delay) -		rdev->last_off = ktime_get_boottime(); -  	/* If the constraints say the regulator should be on at this point  	 * and we have control then make sure it is enabled.  	 */ @@ -1619,6 +1682,8 @@ static int set_machine_constraints(struct regulator_dev *rdev)  		if (rdev->constraints->always_on)  			rdev->use_count++; +	} else if (rdev->desc->off_on_delay) { +		rdev->last_off = ktime_get();  	}  	print_constraints(rdev); @@ -1627,8 +1692,8 @@ static int set_machine_constraints(struct regulator_dev *rdev)  /**   * set_supply - set regulator supply regulator - * @rdev: regulator name - * @supply_rdev: supply regulator name + * @rdev: regulator (locked) + * @supply_rdev: supply regulator (locked))   *   * Called by platform initialisation code to set the supply regulator for this   * regulator. This ensures that a regulators supply will also be enabled by the @@ -1800,6 +1865,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,  	struct regulator *regulator;  	int err = 0; +	lockdep_assert_held_once(&rdev->mutex.base); +  	if (dev) {  		char buf[REG_STR_SIZE];  		int size; @@ -1827,9 +1894,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,  	regulator->rdev = rdev;  	regulator->supply_name = supply_name; -	regulator_lock(rdev);  	list_add(®ulator->list, &rdev->consumer_list); -	regulator_unlock(rdev);  	if (dev) {  		regulator->dev = dev; @@ -1995,6 +2060,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)  {  	struct regulator_dev *r;  	struct device *dev = rdev->dev.parent; +	struct ww_acquire_ctx ww_ctx;  	int ret = 0;  	/* No supply to resolve? */ @@ -2061,23 +2127,23 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)  	 * between rdev->supply null check and setting rdev->supply in  	 * set_supply() from concurrent tasks.  	 */ -	regulator_lock(rdev); +	regulator_lock_two(rdev, r, &ww_ctx);  	/* Supply just resolved by a concurrent task? */  	if (rdev->supply) { -		regulator_unlock(rdev); +		regulator_unlock_two(rdev, r, &ww_ctx);  		put_device(&r->dev);  		goto out;  	}  	ret = set_supply(rdev, r);  	if (ret < 0) { -		regulator_unlock(rdev); +		regulator_unlock_two(rdev, r, &ww_ctx);  		put_device(&r->dev);  		goto out;  	} -	regulator_unlock(rdev); +	regulator_unlock_two(rdev, r, &ww_ctx);  	/*  	 * In set_machine_constraints() we may have turned this regulator on @@ -2190,7 +2256,9 @@ struct regulator *_regulator_get(struct device *dev, const char *id,  		return regulator;  	} +	regulator_lock(rdev);  	regulator = create_regulator(rdev, dev, id); +	regulator_unlock(rdev);  	if (regulator == NULL) {  		regulator = ERR_PTR(-ENOMEM);  		module_put(rdev->owner); @@ -2668,7 +2736,7 @@ static int _regulator_do_enable(struct regulator_dev *rdev)  	trace_regulator_enable(rdev_get_name(rdev)); -	if (rdev->desc->off_on_delay && rdev->last_off) { +	if (rdev->desc->off_on_delay) {  		/* if needed, keep a distance of off_on_delay from last time  		 * this regulator was disabled.  		 */ @@ -6049,6 +6117,7 @@ static void regulator_summary_lock(struct ww_acquire_ctx *ww_ctx)  			ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx);  			old_contended_rdev = new_contended_rdev;  			old_contended_rdev->ref_cnt++; +			old_contended_rdev->mutex_owner = current;  		}  		err = regulator_summary_lock_all(ww_ctx,  |