1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2015 Google, Inc 4 * Written by Simon Glass <sjg@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