leds: lm3530: fix handling of already enabled regulators
PATCH 1/1 leds: lm3530: fix handling of already enabled regulators
It is possible that the regulator has been previously enabled by bootloader or kernel board initialization code. Thus, don't assume the regulator is disabled by default. Get the correct status by regulator_is_enabled().
The leds-lm3530 driver was ignoring the initial status of the regulator; this resulted in rdev->use_count being incremented to 2 after calling lm3530_init_registers() in the .probe method when a regulator was already enabled at insmod time, which made it impossible to ever disable the regulator.
--- drivers/leds/leds-lm3530.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/leds/leds-lm3530.c +1 -1
@@ -397,7 +397,6 @@ static int __devinit lm3530_probe(struct i2c_client *client, drvdata->client = client; drvdata->pdata = pdata; drvdata->brightness =LED_OFF ; - drvdata->enable = false; drvdata->led_dev.name =LM3530_LED_DEV ; drvdata->led_dev.brightness_set = lm3530_brightness_set; drvdata->led_dev.max_brightness =MAX_BRIGHTNESS ;
@@ -411,6 +410,7 @@ static int __devinit lm3530_probe(struct i2c_client *client, drvdata->regulator =NULL ; goto err_regulator_get; } + drvdata->enable =regulator_is_enabled (drvdata->regulator);if (drvdata->pdata->brt_val) { err =lm3530_init_registers (drvdata);
Signature · 1 line
1.7.5.4
drivers/leds/leds-lm3530.c static int __devinit lm3530_probe(struct i2c_client *client, · line 413
Quoted 5 lines · Axel Lin
drivers/leds/leds-lm3530.c +1
-0drvdata->regulator =NULL ; goto err_regulator_get; } + drvdata->enable =regulator_is_enabled (drvdata->regulator);
This isn't sufficient for what the driver is doing - it also needs to enable the regulator. Regulator enables are reference counted (since the regulator can be shared by many device supplies) so if this driver doesn't reference the regulator when it's needed then it could get powered off.
If this regulator is the actual LED then the current default of assuming the regulator is off on boot isn't ideal either as it means that if the regulator is enabled on boot then the LED may be left on even though the driver thinks it's off.
2012/4/23 Mark Brown <[email protected]>:
Quoted 8 lines · Axel Lin
Quoted 6 lines
drvdata->regulator= NULL;
gotoerr_regulator_get;
}
+drvdata->enable=regulator_is_enabled(drvdata->regulator);
Quoted 5 lines
This isn't sufficient for what the driver is doing - it also needs to enable the regulator. Regulator enables are reference counted (since the regulator can be shared by many device supplies) so if this driver doesn't reference the regulator when it's needed then it could get powered off.
The regulator_enable() is called in lm3530_init_registers().
Quoted 5 lines
If this regulator is the actual LED then the current default of assuming the regulator is off on boot isn't ideal either as it means that if the regulator is enabled on boot then the LED may be left on even though the driver thinks it's off.
Hi Shreshtha,
I think without this patch you can see the symptom Mark mentioned if the regulator is enabled on boot.
Any chance to test if this patch works or not?
Regards,
Axel
Quoted 9 lines
Quoted 9 lines · Axel Lin
Quoted 6 lines
drvdata->regulator= NULL;
gotoerr_regulator_get;
}
+drvdata->enable=regulator_is_enabled(drvdata->regulator);
Quoted 8 lines
Quoted 3 lines
This isn't sufficient for what the driver is doing - it also needs to enable the regulator. Regulator enables are reference counted (since the regulator can be shared by many device supplies) so if this
driver
Quoted 2 lines
doesn't reference the regulator when it's needed then it could get powered off.
The
regulator_enable()is called inlm3530_init_registers().
I think this patch has a mismatched condition between the value of variable and actual status of the regulator.
Let's suppose that the regulator (named "vin") is disabled after the lm3530 driver is probed.
Step 1. The 'vin' regulator is on
Step 2. lm3530 driver is probedlm3530_probe()
{
/* drvdata->enabled is true at this moment */
drvdata->enable = regulator_is_enabled(drvdata->regulator);
...
}Step 3. lm3530_init_registers() is calledlm3530_init_registers()
{
/* skip enabling the regulator because the condition is true */
if (!drvdata->enable) {
ret = regulator_enable(drvdata->regulator);
...
}
}Step 4. The regulator is disabled by another driver.
(It is possible because the regulator reference count is not incremented yet by the lm3530 driver)
Step 5. If the brightness mode is changed from the ALS to manual, lm3530_init_registers() is called again.
lm3530_init_registers()
{
/* In this case, regulator should be on,
but it will skip again because the variable is still true */
if (!drvdata->enable) {
ret = regulator_enable(drvdata->regulator);
...
}
}The lm3530_probe() is called once.
But lm3530_init_registers() can be called whenever the brightness mode is changed. (e.g from ALS to manual or manual to PWM, and so on)
So the status of regulator can be mismatched.
In my opinion, current source code is the simplest way.
1. The variable 'drvdata->enabled' is initialized as false.
2. The regulator is enabled and the variable is updated when the driver is probed. (If the regulator is already on, it's ok - keep going ...)
3. The variable is updated when regulator_enable()/regulator_disable() is called by the lm3530 driver. (If another driver tries to turn off the regulator, it can't be allowed
because the reference count was incremented by the lm3530 driver.)
Thanks & BR
Milo -
Quoted 8 lines
In my opinion, current source code is the simplest way.
1. The variable '
drvdata->enabled' is initialized as false.
2. The regulator is enabled and the variable is updated when the driver is probed. (If the regulator is already on, it's ok - keep going ...)
3. The variable is updated when regulator_enable()/regulator_disable() is called by the lm3530 driver. (If another driver tries to turn off the regulator, it can't be allowed
because the reference count was incremented by the lm3530 driver.)
No. Current code is buggy.
Let me put it this way: Assume only this driver is using the regulator. If the regulator is enabled on boot, which means the reference count is init to 1.
In probe, the driver assumes the enable variable is false by default and it calls regulator_enable() in lm3530_init_registers(). This increases the reference count to 2.
Then if we set brt_val to 0 in lm3530_brightness_set, which will then call regulator_disable(). In this case, the reference count becomes 1, note that the reference count is not 0 so the LED may be left on even though the driver thinks it's off.
Regards,
Axel
Quoted 5 lines
Let me put it this way: Assume only this driver is using the regulator. If the regulator is enabled on boot, which means the reference count is init to 1.
Could you explain what 'enabled on boot' means ?
1) The regulator_enable() is called in initial time or
2) boot_on = 1 in regulator_init_data ?
If you mean 'boot_on = 1' in regulator_init_data, then no use_count increase.
Quoted 3 lines
Then if we set
brt_valto 0 inlm3530_brightness_set, which will then callregulator_disable().
It looks buggy code in lm3530_brightness_set().
Even the regulator_disable() is failed, drvdata->enable is set to false.
I think drvdata->enable is changed to false only when the regulator is off successfully.
Thanks & BR
Milo -
Quoted 6 lines
Could you explain what 'enabled on boot' means ?
1) Theregulator_enable()is called in initial time or
2)boot_on= 1 inregulator_init_data?If you mean '
boot_on= 1' inregulator_init_data, then nouse_countincrease.
I mean boot_on = 1 in regulator_init_data.
I just checked the regulator core, and you are right that no use_count increase if boot_on = 1 in this case.
use_count will be increased if boot_on = 1 && using regulator_get_exclusive.
Regards,
Axel