On Mon, Jul 15, 2019 at 11:01:29AM +0200, Jean-Jacques Hiblot wrote:
Hi Dan,If you actively wanted to get -ENODEV back when there is no regulator
On 12/07/2019 20:49, Dan Murphy wrote:
JJActually in the dts, that will be "power-supply". I lacked the imagination
On 7/8/19 5:35 AM, Jean-Jacques Hiblot wrote:
A LED is usually powered by a voltage/current regulator. Let the LEDLet the LED core know
core
about it. This allows the LED core to turn on or off the power supplyWhat if you move this to leds.h so core and class can both include it.
as needed.
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@xxxxxx>
---
 drivers/leds/led-class.c | 10 ++++++++
 drivers/leds/led-core.c | 53 +++++++++++++++++++++++++++++++++++++---
 include/linux/leds.h | 4 +++
 3 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 4793e77808e2..e01b2d982564 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/timer.h>
+#include <linux/regulator/consumer.h>
 #include <uapi/linux/uleds.h>Is the regulator always going to be called power?
 #include "leds.h"
 @@ -272,6 +273,15 @@ int of_led_classdev_register(struct device
*parent, struct device_node *np,
ÂÂÂÂÂÂÂÂÂ dev_warn(parent, "Led %s renamed to %s due to name collision",
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_cdev->name, dev_name(led_cdev->dev));
 + led_cdev->regulator = devm_regulator_get(led_cdev->dev, "power");
to come up with a better name.
The regulator core will provide a dummy regulator if none is given in the+ if (IS_ERR(led_cdev->regulator)) {This is listed as optional in the DT doc. This appears to be required.
+ÂÂÂÂÂÂÂ dev_err(led_cdev->dev, "Cannot get the power supply for %s\n",
+ÂÂÂÂÂÂÂÂÂÂÂ led_cdev->name);
+ÂÂÂÂÂÂÂ device_unregister(led_cdev->dev);
+ÂÂÂÂÂÂÂ mutex_unlock(&led_cdev->led_access);
+ÂÂÂÂÂÂÂ return PTR_ERR(led_cdev->regulator);
device tree. I would rather have an error in that case, but that is not how
it works.
then you can use devm_regulator_get_optional() for that.
However perhaps be careful what you wish for. If you use get_optional()
then you will have to sprinkle NULL or IS_ERR() checks everywhere. I'd
favour using the current approach!
Daniel.
I prefer to keep it optional. Many LED drivers are connected to fixedok
non-managed supplies.
+ÂÂÂ }Prefer to this to be moved up.
+
ÂÂÂÂÂ if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
ÂÂÂÂÂÂÂÂÂ ret = led_add_brightness_hw_changed(led_cdev);
ÂÂÂÂÂÂÂÂÂ if (ret) {
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 7107cd7e87cf..139de6b08cad 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -16,6 +16,7 @@
 #include <linux/rwsem.h>
 #include <linux/slab.h>
 #include "leds.h"
+#include <linux/regulator/consumer.h>
  DECLARE_RWSEM(leds_list_lock);
 EXPORT_SYMBOL_GPL(leds_list_lock);
@@ -23,6 +24,31 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
 LIST_HEAD(leds_list);
 EXPORT_SYMBOL_GPL(leds_list);
 +static bool __led_need_regulator_update(struct led_classdev
*led_cdev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int brightness)
+{
+ÂÂÂ bool new_regulator_state = (brightness != LED_OFF);
+
+ÂÂÂ return led_cdev->regulator_state != new_regulator_state;
+}
+
+static int __led_handle_regulator(struct led_classdev *led_cdev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int brightness)
+{
+ÂÂÂ if (__led_need_regulator_update(led_cdev, brightness)) {
+ÂÂÂÂÂÂÂ int ret;
ok+new line
+ÂÂÂÂÂÂÂ if (brightness != LED_OFF)
+ÂÂÂÂÂÂÂÂÂÂÂ ret = regulator_enable(led_cdev->regulator);
+ÂÂÂÂÂÂÂ else
+ÂÂÂÂÂÂÂÂÂÂÂ ret = regulator_disable(led_cdev->regulator);
+ÂÂÂÂÂÂÂ if (ret)
+ÂÂÂÂÂÂÂÂÂÂÂ return ret;
+ÂÂÂÂÂÂÂ led_cdev->regulator_state = (brightness != LED_OFF);Again this seems to indicate that the regulator is a required property
+ÂÂÂ }
+ÂÂÂ return 0;
+}
+
 static int __led_set_brightness(struct led_classdev *led_cdev,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ enum led_brightness value)
 {
@@ -80,6 +106,7 @@ static void led_timer_function(struct timer_list *t)
ÂÂÂÂÂ }
 Â led_set_brightness_nosleep(led_cdev, brightness);
+ÂÂÂ __led_handle_regulator(led_cdev, brightness);
for the LEDs
This needs to be made optional. And the same comment through out for
every call.
 Â /* Return in next iteration if led is in one-shot mode andCan't you just return here?
we are in
ÂÂÂÂÂÂ * the final blink state so that the led is toggled each
delay_on +
@@ -115,6 +142,8 @@ static void set_brightness_delayed(struct
work_struct *ws)
ÂÂÂÂÂ if (ret == -ENOTSUPP)
ÂÂÂÂÂÂÂÂÂ ret = __led_set_brightness_blocking(led_cdev,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_cdev->delayed_set_value);
+ÂÂÂ __led_handle_regulator(led_cdev, led_cdev->delayed_set_value);
+
ÂÂÂÂÂ if (ret < 0 &&
ÂÂÂÂÂÂÂÂÂ /* LED HW might have been unplugged, therefore don't warn */
ÂÂÂÂÂÂÂÂÂ !(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
@@ -141,6 +170,7 @@ static void led_set_software_blink(struct
led_classdev *led_cdev,
ÂÂÂÂÂ /* never on - just set to off */
ÂÂÂÂÂ if (!delay_on) {
ÂÂÂÂÂÂÂÂÂ led_set_brightness_nosleep(led_cdev, LED_OFF);
+ÂÂÂÂÂÂÂ __led_handle_regulator(led_cdev, LED_OFF);
ÂÂÂÂÂÂÂÂÂ return;
ÂÂÂÂÂ }
 @@ -148,6 +178,7 @@ static void led_set_software_blink(struct
led_classdev *led_cdev,
ÂÂÂÂÂ if (!delay_off) {
ÂÂÂÂÂÂÂÂÂ led_set_brightness_nosleep(led_cdev,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_cdev->blink_brightness);
+ÂÂÂÂÂÂÂ __led_handle_regulator(led_cdev, led_cdev->blink_brightness);
ÂÂÂÂÂÂÂÂÂ return;
ÂÂÂÂÂ }
 @@ -256,8 +287,14 @@ void led_set_brightness_nopm(struct
led_classdev *led_cdev,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ enum led_brightness value)
 {
ÂÂÂÂÂ /* Use brightness_set op if available, it is guaranteed not to
sleep */
-ÂÂÂ if (!__led_set_brightness(led_cdev, value))
-ÂÂÂÂÂÂÂ return;
+ÂÂÂ if (!__led_set_brightness(led_cdev, value)) {
+ÂÂÂÂÂÂÂ /*
+ÂÂÂÂÂÂÂÂ * if regulator state doesn't need to be changed, that is all/
+ÂÂÂÂÂÂÂÂ * Otherwise delegate the change to a work queue
+ÂÂÂÂÂÂÂÂ */
+ÂÂÂÂÂÂÂ if (!__led_need_regulator_update(led_cdev, value))
+ÂÂÂÂÂÂÂÂÂÂÂ return;
+ÂÂÂ }
 Â /* If brightness setting can sleep, delegate it to a work
queue task */
ÂÂÂÂÂ led_cdev->delayed_set_value = value;
@@ -280,6 +317,8 @@ EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
 int led_set_brightness_sync(struct led_classdev *led_cdev,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ enum led_brightness value)
 {
+ÂÂÂ int ret;
+
ÂÂÂÂÂ if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
ÂÂÂÂÂÂÂÂÂ return -EBUSY;
 @@ -288,7 +327,15 @@ int led_set_brightness_sync(struct
led_classdev *led_cdev,
ÂÂÂÂÂ if (led_cdev->flags & LED_SUSPENDED)
ÂÂÂÂÂÂÂÂÂ return 0;
 - return __led_set_brightness_blocking(led_cdev,
led_cdev->brightness);
+ÂÂÂ ret = __led_set_brightness_blocking(led_cdev,
led_cdev->brightness);
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ ret = __led_handle_regulator(led_cdev, led_cdev->brightness);
thanks for the review
JJ
Dan
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ return 0;
 }
 EXPORT_SYMBOL_GPL(led_set_brightness_sync);
 diff --git a/include/linux/leds.h b/include/linux/leds.h
index 9b2bf574a17a..bee8e3f8dddd 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -123,6 +123,10 @@ struct led_classdev {
 Â /* Ensures consistent access to the LED Flash Class device */
ÂÂÂÂÂ struct mutexÂÂÂÂÂÂÂ led_access;
+
+ÂÂÂ /* regulator */
+ÂÂÂ struct regulatorÂÂÂ *regulator;
+ÂÂÂ boolÂÂÂÂÂÂÂÂÂÂÂ regulator_state;
 };
  extern int of_led_classdev_register(struct device *parent,