[U-Boot] [PATCH 0/8] dm: led: Expand the LED interface and add a command

The LED interface for driver model is a little primitive. This series expands it to match the legacy interface and adds a command to match.
Simon Glass (8): sandbox: Add some test LEDs dm: led: Rename struct led_uclass_plat dm: led: Adjust the LED uclass dm: led: Add support for getting the state of an LED dm: led: Support toggling LEDs dm: led: Add support for blinking LEDs led: Mark existing driver as legacy dm: led: Add a new 'led' command
arch/sandbox/dts/sandbox.dts | 14 +++ cmd/Kconfig | 9 ++ cmd/Makefile | 3 +- cmd/led.c | 252 +++++++++++++++++-------------------------- cmd/legacy_led.c | 187 ++++++++++++++++++++++++++++++++ drivers/led/led-uclass.c | 30 +++++- drivers/led/led_gpio.c | 39 ++++++- include/led.h | 62 +++++++++-- test/dm/led.c | 54 +++++++++- 9 files changed, 476 insertions(+), 174 deletions(-) create mode 100644 cmd/legacy_led.c

Add some LEDs to the standard sandbox device tree.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/sandbox/dts/sandbox.dts | 14 ++++++++++++++ cmd/{led.c => legacy_led.c} | 0 2 files changed, 14 insertions(+) rename cmd/{led.c => legacy_led.c} (100%)
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 20614646f7..40f423da25 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -123,6 +123,20 @@ yres = <768>; };
+ leds { + compatible = "gpio-leds"; + + iracibble { + gpios = <&gpio_a 1 0>; + label = "sandbox:red"; + }; + + martinet { + gpios = <&gpio_a 2 0>; + label = "sandbox:green"; + }; + }; + pci: pci-controller { compatible = "sandbox,pci"; device_type = "pci"; diff --git a/cmd/led.c b/cmd/legacy_led.c similarity index 100% rename from cmd/led.c rename to cmd/legacy_led.c

These structures are normally named with 'uc' instead of 'uclass'. Change this one for consistency.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/led/led-uclass.c | 4 ++-- drivers/led/led_gpio.c | 4 ++-- include/led.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c index 784ac870e2..ca4f98c0b3 100644 --- a/drivers/led/led-uclass.c +++ b/drivers/led/led-uclass.c @@ -22,7 +22,7 @@ int led_get_by_label(const char *label, struct udevice **devp) if (ret) return ret; uclass_foreach_dev(dev, uc) { - struct led_uclass_plat *uc_plat = dev_get_uclass_platdata(dev); + struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
/* Ignore the top-level LED node */ if (uc_plat->label && !strcmp(label, uc_plat->label)) @@ -45,5 +45,5 @@ int led_set_on(struct udevice *dev, int on) UCLASS_DRIVER(led) = { .id = UCLASS_LED, .name = "led", - .per_device_platdata_auto_alloc_size = sizeof(struct led_uclass_plat), + .per_device_platdata_auto_alloc_size = sizeof(struct led_uc_plat), }; diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c index 5b119903f5..97b5da35cd 100644 --- a/drivers/led/led_gpio.c +++ b/drivers/led/led_gpio.c @@ -30,7 +30,7 @@ static int gpio_led_set_on(struct udevice *dev, int on)
static int led_gpio_probe(struct udevice *dev) { - struct led_uclass_plat *uc_plat = dev_get_uclass_platdata(dev); + struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); struct led_gpio_priv *priv = dev_get_priv(dev);
/* Ignore the top-level LED node */ @@ -65,7 +65,7 @@ static int led_gpio_bind(struct udevice *parent) for (node = fdt_first_subnode(blob, dev_of_offset(parent)); node > 0; node = fdt_next_subnode(blob, node)) { - struct led_uclass_plat *uc_plat; + struct led_uc_plat *uc_plat; const char *label;
label = fdt_getprop(blob, node, "label", NULL); diff --git a/include/led.h b/include/led.h index b929d0ca3c..a856b3d9ff 100644 --- a/include/led.h +++ b/include/led.h @@ -9,11 +9,11 @@ #define __LED_H
/** - * struct led_uclass_plat - Platform data the uclass stores about each device + * struct led_uc_plat - Platform data the uclass stores about each device * * @label: LED label */ -struct led_uclass_plat { +struct led_uc_plat { const char *label; };

At present this is very simple, supporting only on and off. We want to also support toggling and blinking. As a first step, change the name of the main method and use an enum to indicate the state.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/led/led-uclass.c | 6 +++--- drivers/led/led_gpio.c | 13 ++++++++++--- include/led.h | 19 +++++++++++++------ test/dm/led.c | 5 +++-- 4 files changed, 29 insertions(+), 14 deletions(-)
diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c index ca4f98c0b3..b30346913b 100644 --- a/drivers/led/led-uclass.c +++ b/drivers/led/led-uclass.c @@ -32,14 +32,14 @@ int led_get_by_label(const char *label, struct udevice **devp) return -ENODEV; }
-int led_set_on(struct udevice *dev, int on) +int led_set_state(struct udevice *dev, enum led_state_t state) { struct led_ops *ops = led_get_ops(dev);
- if (!ops->set_on) + if (!ops->set_state) return -ENOSYS;
- return ops->set_on(dev, on); + return ops->set_state(dev, state); }
UCLASS_DRIVER(led) = { diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c index 97b5da35cd..8206cb2cf3 100644 --- a/drivers/led/led_gpio.c +++ b/drivers/led/led_gpio.c @@ -18,14 +18,21 @@ struct led_gpio_priv { struct gpio_desc gpio; };
-static int gpio_led_set_on(struct udevice *dev, int on) +static int gpio_led_set_state(struct udevice *dev, enum led_state_t state) { struct led_gpio_priv *priv = dev_get_priv(dev);
if (!dm_gpio_is_valid(&priv->gpio)) return -EREMOTEIO; + switch (state) { + case LEDST_OFF: + case LEDST_ON: + break; + default: + return -ENOSYS; + }
- return dm_gpio_set_value(&priv->gpio, on); + return dm_gpio_set_value(&priv->gpio, state); }
static int led_gpio_probe(struct udevice *dev) @@ -87,7 +94,7 @@ static int led_gpio_bind(struct udevice *parent) }
static const struct led_ops gpio_led_ops = { - .set_on = gpio_led_set_on, + .set_state = gpio_led_set_state, };
static const struct udevice_id led_gpio_ids[] = { diff --git a/include/led.h b/include/led.h index a856b3d9ff..8af87ea8ea 100644 --- a/include/led.h +++ b/include/led.h @@ -17,15 +17,22 @@ struct led_uc_plat { const char *label; };
+enum led_state_t { + LEDST_OFF = 0, + LEDST_ON = 1, + + LEDST_COUNT, +}; + struct led_ops { /** - * set_on() - set the state of an LED + * set_state() - set the state of an LED * * @dev: LED device to change - * @on: 1 to turn the LED on, 0 to turn it off + * @state: LED state to set * @return 0 if OK, -ve on error */ - int (*set_on)(struct udevice *dev, int on); + int (*set_state)(struct udevice *dev, enum led_state_t state); };
#define led_get_ops(dev) ((struct led_ops *)(dev)->driver->ops) @@ -40,12 +47,12 @@ struct led_ops { int led_get_by_label(const char *label, struct udevice **devp);
/** - * led_set_on() - set the state of an LED + * led_set_state() - set the state of an LED * * @dev: LED device to change - * @on: 1 to turn the LED on, 0 to turn it off + * @state: LED state to set * @return 0 if OK, -ve on error */ -int led_set_on(struct udevice *dev, int on); +int led_set_state(struct udevice *dev, enum led_state_t state);
#endif diff --git a/test/dm/led.c b/test/dm/led.c index 8ee075cf1c..ebb9b46584 100644 --- a/test/dm/led.c +++ b/test/dm/led.c @@ -41,9 +41,10 @@ static int dm_test_led_gpio(struct unit_test_state *uts) ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); - led_set_on(dev, 1); + ut_assertok(led_set_state(dev, LEDST_ON)); ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); - led_set_on(dev, 0); + + ut_assertok(led_set_state(dev, LEDST_OFF)); ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
return 0;

It is useful to be able to read the LED as well as write it. Add this to the uclass and update the GPIO driver.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/led/led-uclass.c | 10 ++++++++++ drivers/led/led_gpio.c | 15 +++++++++++++++ include/led.h | 16 ++++++++++++++++ test/dm/led.c | 2 ++ 4 files changed, 43 insertions(+)
diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c index b30346913b..ea5fbabadf 100644 --- a/drivers/led/led-uclass.c +++ b/drivers/led/led-uclass.c @@ -42,6 +42,16 @@ int led_set_state(struct udevice *dev, enum led_state_t state) return ops->set_state(dev, state); }
+enum led_state_t led_get_state(struct udevice *dev) +{ + struct led_ops *ops = led_get_ops(dev); + + if (!ops->get_state) + return -ENOSYS; + + return ops->get_state(dev); +} + UCLASS_DRIVER(led) = { .id = UCLASS_LED, .name = "led", diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c index 8206cb2cf3..789d15600f 100644 --- a/drivers/led/led_gpio.c +++ b/drivers/led/led_gpio.c @@ -35,6 +35,20 @@ static int gpio_led_set_state(struct udevice *dev, enum led_state_t state) return dm_gpio_set_value(&priv->gpio, state); }
+static enum led_state_t gpio_led_get_state(struct udevice *dev) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + int ret; + + if (!dm_gpio_is_valid(&priv->gpio)) + return -EREMOTEIO; + ret = dm_gpio_get_value(&priv->gpio); + if (ret < 0) + return ret; + + return ret ? LEDST_ON : LEDST_OFF; +} + static int led_gpio_probe(struct udevice *dev) { struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); @@ -95,6 +109,7 @@ static int led_gpio_bind(struct udevice *parent)
static const struct led_ops gpio_led_ops = { .set_state = gpio_led_set_state, + .get_state = gpio_led_get_state, };
static const struct udevice_id led_gpio_ids[] = { diff --git a/include/led.h b/include/led.h index 8af87ea8ea..bbab4d14c9 100644 --- a/include/led.h +++ b/include/led.h @@ -33,6 +33,14 @@ struct led_ops { * @return 0 if OK, -ve on error */ int (*set_state)(struct udevice *dev, enum led_state_t state); + + /** + * led_get_state() - get the state of an LED + * + * @dev: LED device to change + * @return LED state led_state_t, or -ve on error + */ + enum led_state_t (*get_state)(struct udevice *dev); };
#define led_get_ops(dev) ((struct led_ops *)(dev)->driver->ops) @@ -55,4 +63,12 @@ int led_get_by_label(const char *label, struct udevice **devp); */ int led_set_state(struct udevice *dev, enum led_state_t state);
+/** + * led_get_state() - get the state of an LED + * + * @dev: LED device to change + * @return LED state led_state_t, or -ve on error + */ +enum led_state_t led_get_state(struct udevice *dev); + #endif diff --git a/test/dm/led.c b/test/dm/led.c index ebb9b46584..68aa39bd4d 100644 --- a/test/dm/led.c +++ b/test/dm/led.c @@ -43,9 +43,11 @@ static int dm_test_led_gpio(struct unit_test_state *uts) ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); ut_assertok(led_set_state(dev, LEDST_ON)); ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_ON, led_get_state(dev));
ut_assertok(led_set_state(dev, LEDST_OFF)); ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_OFF, led_get_state(dev));
return 0; }

Add support for toggling an LED into the uclass interface. This can be efficiently implemented by the driver.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/led/led_gpio.c | 7 +++++++ include/led.h | 1 + test/dm/led.c | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+)
diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c index 789d15600f..4106ecb679 100644 --- a/drivers/led/led_gpio.c +++ b/drivers/led/led_gpio.c @@ -21,6 +21,7 @@ struct led_gpio_priv { static int gpio_led_set_state(struct udevice *dev, enum led_state_t state) { struct led_gpio_priv *priv = dev_get_priv(dev); + int ret;
if (!dm_gpio_is_valid(&priv->gpio)) return -EREMOTEIO; @@ -28,6 +29,12 @@ static int gpio_led_set_state(struct udevice *dev, enum led_state_t state) case LEDST_OFF: case LEDST_ON: break; + case LEDST_TOGGLE: + ret = dm_gpio_get_value(&priv->gpio); + if (ret < 0) + return ret; + state = !ret; + break; default: return -ENOSYS; } diff --git a/include/led.h b/include/led.h index bbab4d14c9..84a365228f 100644 --- a/include/led.h +++ b/include/led.h @@ -20,6 +20,7 @@ struct led_uc_plat { enum led_state_t { LEDST_OFF = 0, LEDST_ON = 1, + LEDST_TOGGLE = 2,
LEDST_COUNT, }; diff --git a/test/dm/led.c b/test/dm/led.c index 68aa39bd4d..2cc24127e2 100644 --- a/test/dm/led.c +++ b/test/dm/led.c @@ -53,6 +53,31 @@ static int dm_test_led_gpio(struct unit_test_state *uts) } DM_TEST(dm_test_led_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+/* Test that we can toggle LEDs */ +static int dm_test_led_toggle(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we can manipulate an LED. LED 1 is connected to GPIO + * bank gpio_a, offset 1. + */ + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_assertok(led_set_state(dev, LEDST_TOGGLE)); + ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_ON, led_get_state(dev)); + + ut_assertok(led_set_state(dev, LEDST_TOGGLE)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_OFF, led_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_led_toggle, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + /* Test obtaining an LED by label */ static int dm_test_led_label(struct unit_test_state *uts) {

Allow LEDs to be blinked if the driver supports it.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/led/led-uclass.c | 10 ++++++++++ include/led.h | 22 ++++++++++++++++++++++ test/dm/led.c | 22 ++++++++++++++++++++++ 3 files changed, 54 insertions(+)
diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c index ea5fbabadf..95b3be425a 100644 --- a/drivers/led/led-uclass.c +++ b/drivers/led/led-uclass.c @@ -52,6 +52,16 @@ enum led_state_t led_get_state(struct udevice *dev) return ops->get_state(dev); }
+int led_set_period(struct udevice *dev, int period_ms) +{ + struct led_ops *ops = led_get_ops(dev); + + if (!ops->set_period) + return -ENOSYS; + + return ops->set_period(dev, period_ms); +} + UCLASS_DRIVER(led) = { .id = UCLASS_LED, .name = "led", diff --git a/include/led.h b/include/led.h index 84a365228f..ebb6faa34b 100644 --- a/include/led.h +++ b/include/led.h @@ -21,6 +21,7 @@ enum led_state_t { LEDST_OFF = 0, LEDST_ON = 1, LEDST_TOGGLE = 2, + LEDST_BLINK = 3,
LEDST_COUNT, }; @@ -42,6 +43,18 @@ struct led_ops { * @return LED state led_state_t, or -ve on error */ enum led_state_t (*get_state)(struct udevice *dev); + + /** + * led_set_period() - set the blink period of an LED + * + * Thie records the period if supported, or returns -ENOSYS if not. + * To start the LED blinking, use set_state(). + * + * @dev: LED device to change + * @period_ms: LED blink period in milliseconds + * @return 0 if OK, -ve on error + */ + int (*set_period)(struct udevice *dev, int period_ms); };
#define led_get_ops(dev) ((struct led_ops *)(dev)->driver->ops) @@ -72,4 +85,13 @@ int led_set_state(struct udevice *dev, enum led_state_t state); */ enum led_state_t led_get_state(struct udevice *dev);
+/** + * led_set_period() - set the blink period of an LED + * + * @dev: LED device to change + * @period_ms: LED blink period in milliseconds + * @return 0 if OK, -ve on error + */ +int led_set_period(struct udevice *dev, int period_ms); + #endif diff --git a/test/dm/led.c b/test/dm/led.c index 2cc24127e2..79575bd565 100644 --- a/test/dm/led.c +++ b/test/dm/led.c @@ -98,3 +98,25 @@ static int dm_test_led_label(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_led_label, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test LED blinking */ +static int dm_test_led_blink(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we get an error when trying to blink an LED, since it is + * not supported by the GPIO LED driver. + */ + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(-ENOSYS, led_set_state(dev, LEDST_BLINK)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_OFF, led_get_state(dev)); + ut_asserteq(-ENOSYS, led_set_period(dev, 100)); + + return 0; +} +DM_TEST(dm_test_led_blink, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

The existing 'led' command does not support driver model. Rename it to indicate that it is legacy code.
Signed-off-by: Simon Glass sjg@chromium.org ---
cmd/Makefile | 2 +- cmd/legacy_led.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/cmd/Makefile b/cmd/Makefile index f13bb8c11e..c54734a58c 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -79,7 +79,7 @@ obj-$(CONFIG_CMD_ITEST) += itest.o obj-$(CONFIG_CMD_JFFS2) += jffs2.o obj-$(CONFIG_CMD_CRAMFS) += cramfs.o obj-$(CONFIG_CMD_LDRINFO) += ldrinfo.o -obj-$(CONFIG_LED_STATUS_CMD) += led.o +obj-$(CONFIG_LED_STATUS_CMD) += legacy_led.o obj-$(CONFIG_CMD_LICENSE) += license.o obj-y += load.o obj-$(CONFIG_LOGBUFFER) += log.o diff --git a/cmd/legacy_led.c b/cmd/legacy_led.c index 951a5e242f..1ec2e43e50 100644 --- a/cmd/legacy_led.c +++ b/cmd/legacy_led.c @@ -86,7 +86,7 @@ void __weak __led_blink(led_id_t mask, int freq) { }
-int do_led (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_legacy_led(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int i, match = 0; enum led_cmd cmd; @@ -148,7 +148,7 @@ int do_led (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
U_BOOT_CMD( - led, 4, 1, do_led, + led, 4, 1, do_legacy_led, "[" #ifdef CONFIG_LED_STATUS_BOARD_SPECIFIC #ifdef CONFIG_LED_STATUS0

On 31.03.2017 19:55, Simon Glass wrote:
The existing 'led' command does not support driver model. Rename it to indicate that it is legacy code.
Signed-off-by: Simon Glass sjg@chromium.org
Reviewed-by: Stefan Roese sr@denx.de
Thanks, Stefan

When driver model is used for LEDs, provide a command to allow LED access.
Signed-off-by: Simon Glass sjg@chromium.org ---
cmd/Kconfig | 9 ++++ cmd/Makefile | 1 + cmd/led.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 cmd/led.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 25e3b783a8..3c6924a8dc 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -661,6 +661,15 @@ config CMD_CACHE help Enable the "icache" and "dcache" commands
+config CMD_LED + bool "led" + default y if LED + help + Enable the 'led' command which allows for control of LEDs supported + by the board. The LEDs can be listed with 'led list' and controlled + with led on/off/togle/blink. Any LED drivers can be controlled with + this command, e.g. led_gpio. + config CMD_TIME bool "time" help diff --git a/cmd/Makefile b/cmd/Makefile index c54734a58c..a3797ba904 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_CMD_JFFS2) += jffs2.o obj-$(CONFIG_CMD_CRAMFS) += cramfs.o obj-$(CONFIG_CMD_LDRINFO) += ldrinfo.o obj-$(CONFIG_LED_STATUS_CMD) += legacy_led.o +obj-$(CONFIG_CMD_LED) += led.o obj-$(CONFIG_CMD_LICENSE) += license.o obj-y += load.o obj-$(CONFIG_LOGBUFFER) += log.o diff --git a/cmd/led.c b/cmd/led.c new file mode 100644 index 0000000000..6716ccaa1c --- /dev/null +++ b/cmd/led.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <led.h> +#include <dm/uclass-internal.h> + +#define LED_TOGGLE LEDST_COUNT + +static const char *const state_label[] = { + [LEDST_OFF] = "off", + [LEDST_ON] = "on", + [LEDST_TOGGLE] = "toggle", + [LEDST_BLINK] = "blink", +}; + +enum led_state_t get_led_cmd(char *var) +{ + int i; + + for (i = 0; i < LEDST_COUNT; i++) { + if (!strncmp(var, state_label[i], strlen(var))) + return i; + } + + return -1; +} + +static int show_led_state(struct udevice *dev) +{ + int ret; + + ret = led_get_state(dev); + if (ret >= LEDST_COUNT) + ret = -EINVAL; + if (ret >= 0) + printf("%s\n", state_label[ret]); + + return ret; +} + +static int list_leds(void) +{ + struct udevice *dev; + int ret; + + for (uclass_find_first_device(UCLASS_LED, &dev); + dev; + uclass_find_next_device(&dev)) { + struct led_uc_plat *plat = dev_get_uclass_platdata(dev); + + if (!plat->label) + continue; + printf("%-15s ", plat->label); + if (device_active(dev)) { + ret = show_led_state(dev); + if (ret < 0) + printf("Error %d\n", ret); + } else { + printf("<inactive>\n"); + } + } + + return 0; +} + +int do_led(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + enum led_state_t cmd; + const char *led_label; + struct udevice *dev; + int freq_ms = 0; + int ret; + + /* Validate arguments */ + if (argc < 2) + return CMD_RET_USAGE; + led_label = argv[1]; + if (*led_label == 'l') + return list_leds(); + + cmd = argc > 2 ? get_led_cmd(argv[2]) : LEDST_COUNT; + if (cmd < 0) + return CMD_RET_USAGE; + if (cmd == LEDST_BLINK) { + if (argc < 4) + return CMD_RET_USAGE; + freq_ms = simple_strtoul(argv[3], NULL, 10); + } + + ret = led_get_by_label(led_label, &dev); + if (ret) { + printf("LED '%s' not found (err=%d)\n", led_label, ret); + return CMD_RET_FAILURE; + } + switch (cmd) { + case LEDST_OFF: + case LEDST_ON: + case LEDST_TOGGLE: + ret = led_set_state(dev, cmd); + break; + case LEDST_BLINK: + ret = led_set_period(dev, freq_ms); + if (!ret) + ret = led_set_state(dev, LEDST_BLINK); + break; + case LEDST_COUNT: + printf("LED '%s': ", led_label); + ret = show_led_state(dev); + break; + } + if (ret < 0) { + printf("LED '%s' operation failed (err=%d)\n", led_label, ret); + return CMD_RET_FAILURE; + } + + return 0; +} + +U_BOOT_CMD( + led, 4, 1, do_led, + "manage LEDs", + "<led_label> on|off|toggle|blink [blink-freq in ms]\t" + "Change LED state\n" + "led [<led_label>\tGet LED state\n" + "led list\t\tshow a list of LEDs" +);

On 31.03.2017 19:55, Simon Glass wrote:
When driver model is used for LEDs, provide a command to allow LED access.
Signed-off-by: Simon Glass sjg@chromium.org
Reviewed-by: Stefan Roese sr@denx.de
One minor comment below.
cmd/Kconfig | 9 ++++ cmd/Makefile | 1 + cmd/led.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 cmd/led.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 25e3b783a8..3c6924a8dc 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -661,6 +661,15 @@ config CMD_CACHE help Enable the "icache" and "dcache" commands
+config CMD_LED
- bool "led"
- default y if LED
- help
Enable the 'led' command which allows for control of LEDs supported
by the board. The LEDs can be listed with 'led list' and controlled
with led on/off/togle/blink. Any LED drivers can be controlled with
this command, e.g. led_gpio.
config CMD_TIME bool "time" help diff --git a/cmd/Makefile b/cmd/Makefile index c54734a58c..a3797ba904 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_CMD_JFFS2) += jffs2.o obj-$(CONFIG_CMD_CRAMFS) += cramfs.o obj-$(CONFIG_CMD_LDRINFO) += ldrinfo.o obj-$(CONFIG_LED_STATUS_CMD) += legacy_led.o +obj-$(CONFIG_CMD_LED) += led.o obj-$(CONFIG_CMD_LICENSE) += license.o obj-y += load.o obj-$(CONFIG_LOGBUFFER) += log.o diff --git a/cmd/led.c b/cmd/led.c new file mode 100644 index 0000000000..6716ccaa1c --- /dev/null +++ b/cmd/led.c @@ -0,0 +1,133 @@ +/*
- Copyright (c) 2017 Google, Inc
- Written by Simon Glass sjg@chromium.org
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <command.h> +#include <dm.h> +#include <led.h> +#include <dm/uclass-internal.h>
+#define LED_TOGGLE LEDST_COUNT
+static const char *const state_label[] = {
- [LEDST_OFF] = "off",
- [LEDST_ON] = "on",
- [LEDST_TOGGLE] = "toggle",
- [LEDST_BLINK] = "blink",
+};
+enum led_state_t get_led_cmd(char *var) +{
- int i;
- for (i = 0; i < LEDST_COUNT; i++) {
if (!strncmp(var, state_label[i], strlen(var)))
return i;
- }
- return -1;
Perhaps return an error code here instead?
Thanks, Stefan
+}
+static int show_led_state(struct udevice *dev) +{
- int ret;
- ret = led_get_state(dev);
- if (ret >= LEDST_COUNT)
ret = -EINVAL;
- if (ret >= 0)
printf("%s\n", state_label[ret]);
- return ret;
+}
+static int list_leds(void) +{
- struct udevice *dev;
- int ret;
- for (uclass_find_first_device(UCLASS_LED, &dev);
dev;
uclass_find_next_device(&dev)) {
struct led_uc_plat *plat = dev_get_uclass_platdata(dev);
if (!plat->label)
continue;
printf("%-15s ", plat->label);
if (device_active(dev)) {
ret = show_led_state(dev);
if (ret < 0)
printf("Error %d\n", ret);
} else {
printf("<inactive>\n");
}
- }
- return 0;
+}
+int do_led(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- enum led_state_t cmd;
- const char *led_label;
- struct udevice *dev;
- int freq_ms = 0;
- int ret;
- /* Validate arguments */
- if (argc < 2)
return CMD_RET_USAGE;
- led_label = argv[1];
- if (*led_label == 'l')
return list_leds();
- cmd = argc > 2 ? get_led_cmd(argv[2]) : LEDST_COUNT;
- if (cmd < 0)
return CMD_RET_USAGE;
- if (cmd == LEDST_BLINK) {
if (argc < 4)
return CMD_RET_USAGE;
freq_ms = simple_strtoul(argv[3], NULL, 10);
- }
- ret = led_get_by_label(led_label, &dev);
- if (ret) {
printf("LED '%s' not found (err=%d)\n", led_label, ret);
return CMD_RET_FAILURE;
- }
- switch (cmd) {
- case LEDST_OFF:
- case LEDST_ON:
- case LEDST_TOGGLE:
ret = led_set_state(dev, cmd);
break;
- case LEDST_BLINK:
ret = led_set_period(dev, freq_ms);
if (!ret)
ret = led_set_state(dev, LEDST_BLINK);
break;
- case LEDST_COUNT:
printf("LED '%s': ", led_label);
ret = show_led_state(dev);
break;
- }
- if (ret < 0) {
printf("LED '%s' operation failed (err=%d)\n", led_label, ret);
return CMD_RET_FAILURE;
- }
- return 0;
+}
+U_BOOT_CMD(
- led, 4, 1, do_led,
- "manage LEDs",
- "<led_label> on|off|toggle|blink [blink-freq in ms]\t"
"Change LED state\n"
- "led [<led_label>\tGet LED state\n"
- "led list\t\tshow a list of LEDs"
+);
Viele Grüße, Stefan

On Fri, Mar 31, 2017 at 11:55:05AM -0600, Simon Glass wrote:
When driver model is used for LEDs, provide a command to allow LED access.
Signed-off-by: Simon Glass sjg@chromium.org
[snip]
+static const char *const state_label[] = {
- [LEDST_OFF] = "off",
- [LEDST_ON] = "on",
- [LEDST_TOGGLE] = "toggle",
- [LEDST_BLINK] = "blink",
If I recall some tinkering right, "most" LEDs we have available in U-Boot don't support blinking directly but rather get blinked in a real OS with timers to toggle them. So can we make blink optional so that only when we'd be using it do we get the size increase? Thanks!
participants (3)
-
Simon Glass
-
Stefan Roese
-
Tom Rini