dm: led: Add support for getting the state of an LED

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>
Reviewed-by: Ziping Chen <techping.chan@gmail.com>
diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c
index b303469..ea5fbab 100644
--- a/drivers/led/led-uclass.c
+++ b/drivers/led/led-uclass.c
@@ -42,6 +42,16 @@
 	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 af8133d..789d156 100644
--- a/drivers/led/led_gpio.c
+++ b/drivers/led/led_gpio.c
@@ -24,10 +24,31 @@
 
 	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, 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);
@@ -88,6 +109,7 @@
 
 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 8af87ea..bbab4d1 100644
--- a/include/led.h
+++ b/include/led.h
@@ -33,6 +33,14 @@
 	 * @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_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 ebb9b46..68aa39b 100644
--- a/test/dm/led.c
+++ b/test/dm/led.c
@@ -43,9 +43,11 @@
 	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;
 }