1 /*
2 * Copyright (c) 2006-2022, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-3-08 GuEe-GUI the first version
9 */
10
11 #include <rtthread.h>
12 #include <rtdevice.h>
13
14 #define DBG_TAG "led.gpio"
15 #define DBG_LVL DBG_INFO
16 #include <rtdbg.h>
17
18 struct gpio_led
19 {
20 struct rt_led_device parent;
21
22 rt_base_t pin;
23 rt_uint8_t active_val;
24 };
25
26 #define raw_to_gpio_led(raw) rt_container_of(raw, struct gpio_led, parent)
27
gpio_led_set_state(struct rt_led_device * led,enum rt_led_state state)28 static rt_err_t gpio_led_set_state(struct rt_led_device *led, enum rt_led_state state)
29 {
30 rt_err_t err = RT_EOK;
31 struct gpio_led *gled = raw_to_gpio_led(led);
32
33 rt_pin_mode(gled->pin, PIN_MODE_OUTPUT);
34
35 switch (state)
36 {
37 case RT_LED_S_OFF:
38 rt_pin_write(gled->pin, !gled->active_val);
39 break;
40
41 case RT_LED_S_ON:
42 rt_pin_write(gled->pin, gled->active_val);
43 break;
44
45 case RT_LED_S_TOGGLE:
46 err = led->ops->get_state(led, &state);
47
48 if (!err)
49 {
50 err = led->ops->set_state(led, state == RT_LED_S_OFF ? RT_LED_S_ON : RT_LED_S_OFF);
51 }
52 break;
53
54 default:
55 return -RT_ENOSYS;
56 }
57
58 return err;
59 }
60
gpio_led_get_state(struct rt_led_device * led,enum rt_led_state * out_state)61 static rt_err_t gpio_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state)
62 {
63 struct gpio_led *gled = raw_to_gpio_led(led);
64
65 switch (rt_pin_read(gled->pin))
66 {
67 case PIN_LOW:
68 *out_state = RT_LED_S_OFF;
69 break;
70
71 case PIN_HIGH:
72 *out_state = RT_LED_S_ON;
73 break;
74
75 default:
76 return -RT_ERROR;
77 }
78
79 return RT_EOK;
80 }
81
82 const static struct rt_led_ops gpio_led_ops =
83 {
84 .set_state = gpio_led_set_state,
85 .get_state = gpio_led_get_state,
86 };
87
ofw_append_gpio_led(struct rt_ofw_node * np)88 static rt_err_t ofw_append_gpio_led(struct rt_ofw_node *np)
89 {
90 rt_err_t err;
91 enum rt_led_state led_state = RT_LED_S_OFF;
92 const char *propname, *state, *trigger;
93 struct gpio_led *gled = rt_malloc(sizeof(*gled));
94
95 if (!gled)
96 {
97 return -RT_ENOMEM;
98 }
99
100 gled->pin = rt_ofw_get_named_pin(np, RT_NULL, 0, RT_NULL, &gled->active_val);
101
102 if (gled->pin < 0)
103 {
104 err = gled->pin;
105
106 goto _fail;
107 }
108
109 gled->parent.ops = &gpio_led_ops;
110
111 if ((err = rt_led_register(&gled->parent)))
112 {
113 goto _fail;
114 }
115
116 if (!rt_ofw_prop_read_string(np, "default-state", &state))
117 {
118 if (!rt_strcmp(state, "on"))
119 {
120 led_state = RT_LED_S_ON;
121 }
122 }
123
124 if ((propname = rt_ofw_get_prop_fuzzy_name(np, "default-trigger$")))
125 {
126 if (!rt_ofw_prop_read_string(np, propname, &trigger))
127 {
128 if (!rt_strcmp(trigger, "heartbeat") ||
129 !rt_strcmp(trigger, "timer"))
130 {
131 led_state = RT_LED_S_BLINK;
132 }
133 }
134 }
135
136 rt_led_set_state(&gled->parent, led_state);
137
138 rt_ofw_data(np) = &gled->parent;
139
140 return RT_EOK;
141
142 _fail:
143 rt_free(gled);
144
145 return err;
146 }
147
gpio_led_probe(struct rt_platform_device * pdev)148 static rt_err_t gpio_led_probe(struct rt_platform_device *pdev)
149 {
150 rt_bool_t pinctrl_apply = RT_FALSE;
151 struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node;
152
153 if (rt_ofw_prop_read_bool(np, "pinctrl-0"))
154 {
155 pinctrl_apply = RT_TRUE;
156 rt_pin_ctrl_confs_apply_by_name(&pdev->parent, RT_NULL);
157 }
158
159 rt_ofw_foreach_available_child_node(np, led_np)
160 {
161 rt_err_t err = ofw_append_gpio_led(led_np);
162
163 if (err == -RT_ENOMEM)
164 {
165 rt_ofw_node_put(led_np);
166
167 return err;
168 }
169 else if (err)
170 {
171 LOG_E("%s: create LED fail", rt_ofw_node_full_name(led_np));
172 continue;
173 }
174
175 if (!pinctrl_apply)
176 {
177 struct rt_device dev_tmp;
178
179 dev_tmp.ofw_node = led_np;
180 rt_pin_ctrl_confs_apply_by_name(&dev_tmp, RT_NULL);
181 }
182 }
183
184 return RT_EOK;
185 }
186
gpio_led_remove(struct rt_platform_device * pdev)187 static rt_err_t gpio_led_remove(struct rt_platform_device *pdev)
188 {
189 struct gpio_led *gled;
190 struct rt_led_device *led_dev;
191 struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node;
192
193 rt_ofw_foreach_available_child_node(np, led_np)
194 {
195 led_dev = rt_ofw_data(led_np);
196
197 if (!led_dev)
198 {
199 continue;
200 }
201
202 gled = rt_container_of(led_dev, struct gpio_led, parent);
203
204 rt_ofw_data(led_np) = RT_NULL;
205
206 rt_led_unregister(&gled->parent);
207
208 rt_free(gled);
209 }
210
211 return RT_EOK;
212 }
213
214 static const struct rt_ofw_node_id gpio_led_ofw_ids[] =
215 {
216 { .compatible = "gpio-leds" },
217 { /* sentinel */ }
218 };
219
220 static struct rt_platform_driver gpio_led_driver =
221 {
222 .name = "led-gpio",
223 .ids = gpio_led_ofw_ids,
224
225 .probe = gpio_led_probe,
226 .remove = gpio_led_remove,
227 };
228 RT_PLATFORM_DRIVER_EXPORT(gpio_led_driver);
229