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