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