Home | History | Annotate | Download | only in led
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (c) 2015 Google, Inc
      4  * Written by Simon Glass <sjg (at) chromium.org>
      5  */
      6 
      7 #include <common.h>
      8 #include <dm.h>
      9 #include <errno.h>
     10 #include <led.h>
     11 #include <asm/gpio.h>
     12 #include <dm/lists.h>
     13 #include <dm/uclass-internal.h>
     14 
     15 struct led_gpio_priv {
     16 	struct gpio_desc gpio;
     17 };
     18 
     19 static int gpio_led_set_state(struct udevice *dev, enum led_state_t state)
     20 {
     21 	struct led_gpio_priv *priv = dev_get_priv(dev);
     22 	int ret;
     23 
     24 	if (!dm_gpio_is_valid(&priv->gpio))
     25 		return -EREMOTEIO;
     26 	switch (state) {
     27 	case LEDST_OFF:
     28 	case LEDST_ON:
     29 		break;
     30 	case LEDST_TOGGLE:
     31 		ret = dm_gpio_get_value(&priv->gpio);
     32 		if (ret < 0)
     33 			return ret;
     34 		state = !ret;
     35 		break;
     36 	default:
     37 		return -ENOSYS;
     38 	}
     39 
     40 	return dm_gpio_set_value(&priv->gpio, state);
     41 }
     42 
     43 static enum led_state_t gpio_led_get_state(struct udevice *dev)
     44 {
     45 	struct led_gpio_priv *priv = dev_get_priv(dev);
     46 	int ret;
     47 
     48 	if (!dm_gpio_is_valid(&priv->gpio))
     49 		return -EREMOTEIO;
     50 	ret = dm_gpio_get_value(&priv->gpio);
     51 	if (ret < 0)
     52 		return ret;
     53 
     54 	return ret ? LEDST_ON : LEDST_OFF;
     55 }
     56 
     57 static int led_gpio_probe(struct udevice *dev)
     58 {
     59 	struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
     60 	struct led_gpio_priv *priv = dev_get_priv(dev);
     61 	const char *default_state;
     62 	int ret;
     63 
     64 	/* Ignore the top-level LED node */
     65 	if (!uc_plat->label)
     66 		return 0;
     67 
     68 	ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT);
     69 	if (ret)
     70 		return ret;
     71 
     72 	default_state = dev_read_string(dev, "default-state");
     73 	if (default_state) {
     74 		if (!strncmp(default_state, "on", 2))
     75 			gpio_led_set_state(dev, LEDST_ON);
     76 		else if (!strncmp(default_state, "off", 3))
     77 			gpio_led_set_state(dev, LEDST_OFF);
     78 	}
     79 	return 0;
     80 }
     81 
     82 static int led_gpio_remove(struct udevice *dev)
     83 {
     84 	/*
     85 	 * The GPIO driver may have already been removed. We will need to
     86 	 * address this more generally.
     87 	 */
     88 #ifndef CONFIG_SANDBOX
     89 	struct led_gpio_priv *priv = dev_get_priv(dev);
     90 
     91 	if (dm_gpio_is_valid(&priv->gpio))
     92 		dm_gpio_free(dev, &priv->gpio);
     93 #endif
     94 
     95 	return 0;
     96 }
     97 
     98 static int led_gpio_bind(struct udevice *parent)
     99 {
    100 	struct udevice *dev;
    101 	ofnode node;
    102 	int ret;
    103 
    104 	dev_for_each_subnode(node, parent) {
    105 		struct led_uc_plat *uc_plat;
    106 		const char *label;
    107 
    108 		label = ofnode_read_string(node, "label");
    109 		if (!label) {
    110 			debug("%s: node %s has no label\n", __func__,
    111 			      ofnode_get_name(node));
    112 			return -EINVAL;
    113 		}
    114 		ret = device_bind_driver_to_node(parent, "gpio_led",
    115 						 ofnode_get_name(node),
    116 						 node, &dev);
    117 		if (ret)
    118 			return ret;
    119 		uc_plat = dev_get_uclass_platdata(dev);
    120 		uc_plat->label = label;
    121 
    122 		if (ofnode_read_bool(node, "default-state")) {
    123 			struct udevice *devp;
    124 
    125 			ret = uclass_get_device_tail(dev, 0, &devp);
    126 			if (ret)
    127 				return ret;
    128 		}
    129 	}
    130 
    131 	return 0;
    132 }
    133 
    134 static const struct led_ops gpio_led_ops = {
    135 	.set_state	= gpio_led_set_state,
    136 	.get_state	= gpio_led_get_state,
    137 };
    138 
    139 static const struct udevice_id led_gpio_ids[] = {
    140 	{ .compatible = "gpio-leds" },
    141 	{ }
    142 };
    143 
    144 U_BOOT_DRIVER(led_gpio) = {
    145 	.name	= "gpio_led",
    146 	.id	= UCLASS_LED,
    147 	.of_match = led_gpio_ids,
    148 	.ops	= &gpio_led_ops,
    149 	.priv_auto_alloc_size = sizeof(struct led_gpio_priv),
    150 	.bind	= led_gpio_bind,
    151 	.probe	= led_gpio_probe,
    152 	.remove	= led_gpio_remove,
    153 };
    154