dm: Add support for LEDs

Add a simple uclass for LEDs, so that these can be controlled by the device
tree and activated when needed. LEDs are referred to by their label.

This implementation requires a driver for each type of LED (e.g GPIO, I2C).

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/doc/device-tree-bindings/leds/common.txt b/doc/device-tree-bindings/leds/common.txt
new file mode 100644
index 0000000..2d88816
--- /dev/null
+++ b/doc/device-tree-bindings/leds/common.txt
@@ -0,0 +1,23 @@
+Common leds properties.
+
+Optional properties for child nodes:
+- label : The label for this LED.  If omitted, the label is
+  taken from the node name (excluding the unit address).
+
+- linux,default-trigger :  This parameter, if present, is a
+    string defining the trigger assigned to the LED.  Current triggers are:
+     "backlight" - LED will act as a back-light, controlled by the framebuffer
+		   system
+     "default-on" - LED will turn on (but for leds-gpio see "default-state"
+		    property in Documentation/devicetree/bindings/gpio/led.txt)
+     "heartbeat" - LED "double" flashes at a load average based rate
+     "ide-disk" - LED indicates disk activity
+     "timer" - LED flashes at a fixed, configurable rate
+
+Examples:
+
+system-status {
+	label = "Status";
+	linux,default-trigger = "heartbeat";
+	...
+};
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c7e526c..5ebc89d 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -20,6 +20,8 @@
 
 source "drivers/input/Kconfig"
 
+source "drivers/led/Kconfig"
+
 source "drivers/serial/Kconfig"
 
 source "drivers/tpm/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 405b64b..c090aba 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -7,6 +7,7 @@
 obj-y += crypto/
 obj-$(CONFIG_FPGA) += fpga/
 obj-y += hwmon/
+obj-$(CONFIG_LED) += led/
 obj-y += misc/
 obj-y += pcmcia/
 obj-y += dfu/
diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig
new file mode 100644
index 0000000..e4d9a84
--- /dev/null
+++ b/drivers/led/Kconfig
@@ -0,0 +1,17 @@
+config LED
+	bool "Enable LED support"
+	depends on DM
+	help
+	  Many boards have LEDs which can be used to signal status or alerts.
+	  U-Boot provides a uclass API to implement this feature. LED drivers
+	  can provide access to board-specific LEDs. Use of the device tree
+	  for configuration is encouraged.
+
+config SPL_LED_SUPPORT
+	bool "Enable LED support in SPL"
+	depends on LED
+	help
+	  The LED subsystem adds a small amount of overhead to the image.
+	  If this is acceptable and you have a need to use LEDs in SPL,
+	  enable this option. You will need to enable device tree in SPL
+	  for this to work.
diff --git a/drivers/led/Makefile b/drivers/led/Makefile
new file mode 100644
index 0000000..b39b205
--- /dev/null
+++ b/drivers/led/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (c) 2015 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_LED) += led-uclass.o
diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c
new file mode 100644
index 0000000..a80ae93
--- /dev/null
+++ b/drivers/led/led-uclass.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <led.h>
+#include <dm/root.h>
+#include <dm/uclass-internal.h>
+
+int led_get_by_label(const char *label, struct udevice **devp)
+{
+	struct udevice *dev;
+	struct uclass *uc;
+	int ret;
+
+	ret = uclass_get(UCLASS_LED, &uc);
+	if (ret)
+		return ret;
+	uclass_foreach_dev(dev, uc) {
+		struct led_uclass_plat *uc_plat = dev_get_uclass_platdata(dev);
+
+		if (!strcmp(label, uc_plat->label))
+			return uclass_get_device_tail(dev, 0, devp);
+	}
+
+	return -ENOENT;
+}
+
+int led_set_on(struct udevice *dev, int on)
+{
+	struct led_ops *ops = led_get_ops(dev);
+
+	if (!ops->set_on)
+		return -ENOSYS;
+
+	return ops->set_on(dev, on);
+}
+
+UCLASS_DRIVER(led) = {
+	.id		= UCLASS_LED,
+	.name		= "led",
+	.per_device_platdata_auto_alloc_size = sizeof(struct led_uclass_plat),
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 24baa5c..a961648 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -33,6 +33,7 @@
 	UCLASS_I2C,		/* I2C bus */
 	UCLASS_I2C_EEPROM,	/* I2C EEPROM device */
 	UCLASS_I2C_GENERIC,	/* Generic I2C device */
+	UCLASS_LED,		/* Light-emitting diode (LED) */
 	UCLASS_LPC,		/* x86 'low pin count' interface */
 	UCLASS_MASS_STORAGE,	/* Mass storage device */
 	UCLASS_MOD_EXP,		/* RSA Mod Exp device */
diff --git a/include/led.h b/include/led.h
new file mode 100644
index 0000000..8925d75
--- /dev/null
+++ b/include/led.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LED_H
+#define __LED_H
+
+/**
+ * struct led_uclass_plat - Platform data the uclass stores about each device
+ *
+ * @label:	LED label
+ */
+struct led_uclass_plat {
+	const char *label;
+};
+
+struct led_ops {
+	/**
+	 * set_on() - set the state of an LED
+	 *
+	 * @dev:	LED device to change
+	 * @on:		1 to turn the LED on, 0 to turn it off
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*set_on)(struct udevice *dev, int on);
+};
+
+#define led_get_ops(dev)	((struct led_ops *)(dev)->driver->ops)
+
+/**
+ * led_get_by_label() - Find an LED device by label
+ *
+ * @label:	LED label to look up
+ * @devp:	Returns the associated device, if found
+ * @return 0 if found, -ve on error
+ */
+int led_get_by_label(const char *label, struct udevice **devp);
+
+/**
+ * led_set_on() - set the state of an LED
+ *
+ * @dev:	LED device to change
+ * @on:		1 to turn the LED on, 0 to turn it off
+ * @return 0 if OK, -ve on error
+ */
+int led_set_on(struct udevice *dev, int on);
+
+#endif
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl
index bcc7ca2..6b32618 100644
--- a/scripts/Makefile.spl
+++ b/scripts/Makefile.spl
@@ -64,6 +64,7 @@
 libs-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += drivers/mtd/spi/
 libs-$(CONFIG_SPL_SPI_SUPPORT) += drivers/spi/
 libs-y += fs/
+libs-$(CONFIG_SPL_LED_SUPPORT) += drivers/led/
 libs-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/
 libs-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/ drivers/power/pmic/
 libs-$(CONFIG_SPL_MTD_SUPPORT) += drivers/mtd/