[PATCH v4 1/2] regulator: implement basic reference counter

Eugen Hristev eugen.hristev at collabora.com
Mon Apr 3 11:59:07 CEST 2023


On 4/2/23 13:45, Jonas Karlman wrote:
> Hi Eugen,
> 
> On 2023-03-31 11:15, Eugen Hristev wrote:
>> Some devices share a regulator supply, when the first one will request
>> regulator disable, the second device will have it's supply cut off before
>> graciously shutting down. Hence there will be timeouts and other failed
>> operations.
>> Implement a reference counter mechanism similar with what is done in
>> Linux, to keep track of enable and disable requests, and only disable the
>> regulator when the last of the consumers has requested shutdown.
>>
>> Signed-off-by: Eugen Hristev <eugen.hristev at collabora.com>
>> Reviewed-by: Simon Glass <sjg at chromium.org>
>> ---
>> Changes in v4:
>>   - add documentation for error codes
>> Changes in v3:
>>   - add error return codes
>> Changes in v2:
>>   - add info in header regarding the function
>>
>>   drivers/power/regulator/regulator_common.c | 22 ++++++++++++++++++++++
>>   drivers/power/regulator/regulator_common.h | 21 +++++++++++++++++++++
>>   2 files changed, 43 insertions(+)
>>
>> diff --git a/drivers/power/regulator/regulator_common.c b/drivers/power/regulator/regulator_common.c
>> index 93d8196b381e..484a4fc31ef7 100644
>> --- a/drivers/power/regulator/regulator_common.c
>> +++ b/drivers/power/regulator/regulator_common.c
>> @@ -73,6 +73,23 @@ int regulator_common_set_enable(const struct udevice *dev,
>>   		return 0;
>>   	}
>>   
>> +	/* If previously enabled, increase count */
>> +	if (enable && dev_pdata->enable_count > 0) {
>> +		dev_pdata->enable_count++;
>> +		return -EALREADY;
>> +	}
>> +
>> +	if (!enable) {
>> +		if (dev_pdata->enable_count > 1) {
>> +			/* If enabled multiple times, decrease count */
>> +			dev_pdata->enable_count--;
>> +			return -EBUSY;
>> +		} else if (!dev_pdata->enable_count) {
>> +			/* If already disabled, do nothing */
>> +			return -EALREADY;
>> +		}
>> +	}
>> +
>>   	ret = dm_gpio_set_value(&dev_pdata->gpio, enable);
>>   	if (ret) {
>>   		pr_err("Can't set regulator : %s gpio to: %d\n", dev->name,
>> @@ -87,5 +104,10 @@ int regulator_common_set_enable(const struct udevice *dev,
>>   	if (!enable && dev_pdata->off_on_delay_us)
>>   		udelay(dev_pdata->off_on_delay_us);
>>   
>> +	if (enable)
>> +		dev_pdata->enable_count++;
>> +	else
>> +		dev_pdata->enable_count--;
>> +
>>   	return 0;
>>   }
>> diff --git a/drivers/power/regulator/regulator_common.h b/drivers/power/regulator/regulator_common.h
>> index c10492f01675..0faab447d099 100644
>> --- a/drivers/power/regulator/regulator_common.h
>> +++ b/drivers/power/regulator/regulator_common.h
>> @@ -13,6 +13,7 @@ struct regulator_common_plat {
>>   	struct gpio_desc gpio; /* GPIO for regulator enable control */
>>   	unsigned int startup_delay_us;
>>   	unsigned int off_on_delay_us;
>> +	unsigned int enable_count;
>>   };
>>   
>>   int regulator_common_of_to_plat(struct udevice *dev,
>> @@ -20,6 +21,26 @@ int regulator_common_of_to_plat(struct udevice *dev,
>>   				char *enable_gpio_name);
>>   int regulator_common_get_enable(const struct udevice *dev,
>>   	struct regulator_common_plat *dev_pdata);
>> +/*
>> + * Enable or Disable a regulator
>> + *
>> + * This is a reentrant function and subsequent calls that enable will
>> + * increase an internal counter, and disable calls will decrease the counter.
>> + * The actual resource will be enabled when the counter gets to 1 coming from 0,
>> + * and disabled when it reaches 0 coming from 1.
>> + *
>> + * @dev: regulator device
>> + * @dev_pdata: Platform data
>> + * @enable: bool indicating whether to enable or disable the regulator
>> + * @return:
>> + * 0 on Success
>> + * -EBUSY if the regulator cannot be disabled because it's requested by
>> + *        another device
>> + * -EALREADY if the regulator has already been enabled or has already been
>> + *        disabled
> 
> With this new return value added this has potential to break IO voltage
> switching for mmc UHS support. Main pattern for IO voltage switch seem
> to be: disable regulator, set voltage, enable regulator.

How can the breakage happen in this case ?

> 
> regulator_set_enable_if_allowed should probably be updated to return
> success on -EALREADY, but that also has the potential to hide regulators
> enabled at boot not being disabled unless initial enable_count reflects
> the initial regulator state.

If the regulators enabled at boot are fixed/gpio regulators (which have 
the enable count), then at boot , the autoset should call the 
enable_common, thus taking into account the enable_count.

So I think that if I updated regulator_set_enable_if_allowed to return 
success on -EALREADY it should be fine. Do you have any other scenario 
in mind ?
> 
> Guessing initial enable_count will match for regulator-boot-on regulators
> in Rockchip and few other platforms/boards because they the call
> regulators_enable_boot_on, does not look like all platform will have
> initial enable_count match regulator initial state.

If the other platforms do not call regulator enable, then they will have 
the enable_count==0, which is expected right ?
You refer to specific platforms where the regulator is already enabled 
from hardware but no code is called to enable, and by looking at 
enable_count it would appear disabled ? There are such cases ? And those 
regulators are not initialized at all ?

> 
> Regards,
> Jonas
> 
>> + * -EACCES if there is no possibility to enable/disable the regulator
>> + * -ve on different error situation
>> + */
>>   int regulator_common_set_enable(const struct udevice *dev,
>>   	struct regulator_common_plat *dev_pdata, bool enable);
>>   
> 



More information about the U-Boot mailing list