1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * LEDs driver for GPIOs |
4 | * |
5 | * Copyright (C) 2007 8D Technologies inc. |
6 | * Raphael Assenat <raph@8d.com> |
7 | * Copyright (C) 2008 Freescale Semiconductor, Inc. |
8 | */ |
9 | #include <linux/container_of.h> |
10 | #include <linux/device.h> |
11 | #include <linux/err.h> |
12 | #include <linux/gpio.h> |
13 | #include <linux/gpio/consumer.h> |
14 | #include <linux/leds.h> |
15 | #include <linux/mod_devicetable.h> |
16 | #include <linux/module.h> |
17 | #include <linux/overflow.h> |
18 | #include <linux/pinctrl/consumer.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/property.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/types.h> |
23 | |
24 | #include "leds.h" |
25 | |
26 | struct gpio_led_data { |
27 | struct led_classdev cdev; |
28 | struct gpio_desc *gpiod; |
29 | u8 can_sleep; |
30 | u8 blinking; |
31 | gpio_blink_set_t platform_gpio_blink_set; |
32 | }; |
33 | |
34 | static inline struct gpio_led_data * |
35 | cdev_to_gpio_led_data(struct led_classdev *led_cdev) |
36 | { |
37 | return container_of(led_cdev, struct gpio_led_data, cdev); |
38 | } |
39 | |
40 | static void gpio_led_set(struct led_classdev *led_cdev, |
41 | enum led_brightness value) |
42 | { |
43 | struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev); |
44 | int level; |
45 | |
46 | if (value == LED_OFF) |
47 | level = 0; |
48 | else |
49 | level = 1; |
50 | |
51 | if (led_dat->blinking) { |
52 | led_dat->platform_gpio_blink_set(led_dat->gpiod, level, |
53 | NULL, NULL); |
54 | led_dat->blinking = 0; |
55 | } else { |
56 | if (led_dat->can_sleep) |
57 | gpiod_set_value_cansleep(desc: led_dat->gpiod, value: level); |
58 | else |
59 | gpiod_set_value(desc: led_dat->gpiod, value: level); |
60 | } |
61 | } |
62 | |
63 | static int gpio_led_set_blocking(struct led_classdev *led_cdev, |
64 | enum led_brightness value) |
65 | { |
66 | gpio_led_set(led_cdev, value); |
67 | return 0; |
68 | } |
69 | |
70 | static int gpio_blink_set(struct led_classdev *led_cdev, |
71 | unsigned long *delay_on, unsigned long *delay_off) |
72 | { |
73 | struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev); |
74 | |
75 | led_dat->blinking = 1; |
76 | return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK, |
77 | delay_on, delay_off); |
78 | } |
79 | |
80 | static int create_gpio_led(const struct gpio_led *template, |
81 | struct gpio_led_data *led_dat, struct device *parent, |
82 | struct fwnode_handle *fwnode, gpio_blink_set_t blink_set) |
83 | { |
84 | struct led_init_data init_data = {}; |
85 | struct pinctrl *pinctrl; |
86 | int ret, state; |
87 | |
88 | led_dat->cdev.default_trigger = template->default_trigger; |
89 | led_dat->can_sleep = gpiod_cansleep(desc: led_dat->gpiod); |
90 | if (!led_dat->can_sleep) |
91 | led_dat->cdev.brightness_set = gpio_led_set; |
92 | else |
93 | led_dat->cdev.brightness_set_blocking = gpio_led_set_blocking; |
94 | led_dat->blinking = 0; |
95 | if (blink_set) { |
96 | led_dat->platform_gpio_blink_set = blink_set; |
97 | led_dat->cdev.blink_set = gpio_blink_set; |
98 | } |
99 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) { |
100 | state = gpiod_get_value_cansleep(desc: led_dat->gpiod); |
101 | if (state < 0) |
102 | return state; |
103 | } else { |
104 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); |
105 | } |
106 | led_dat->cdev.brightness = state; |
107 | led_dat->cdev.max_brightness = 1; |
108 | if (!template->retain_state_suspended) |
109 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
110 | if (template->panic_indicator) |
111 | led_dat->cdev.flags |= LED_PANIC_INDICATOR; |
112 | if (template->retain_state_shutdown) |
113 | led_dat->cdev.flags |= LED_RETAIN_AT_SHUTDOWN; |
114 | |
115 | ret = gpiod_direction_output(desc: led_dat->gpiod, value: state); |
116 | if (ret < 0) |
117 | return ret; |
118 | |
119 | if (template->name) { |
120 | led_dat->cdev.name = template->name; |
121 | ret = devm_led_classdev_register(parent, led_cdev: &led_dat->cdev); |
122 | } else { |
123 | init_data.fwnode = fwnode; |
124 | ret = devm_led_classdev_register_ext(parent, led_cdev: &led_dat->cdev, |
125 | init_data: &init_data); |
126 | } |
127 | |
128 | if (ret) |
129 | return ret; |
130 | |
131 | pinctrl = devm_pinctrl_get_select_default(dev: led_dat->cdev.dev); |
132 | ret = PTR_ERR_OR_ZERO(ptr: pinctrl); |
133 | /* pinctrl-%d not present, not an error */ |
134 | if (ret == -ENODEV) |
135 | ret = 0; |
136 | if (ret) { |
137 | dev_warn(led_dat->cdev.dev, "Failed to select %pfw pinctrl: %d\n" , |
138 | fwnode, ret); |
139 | } |
140 | |
141 | return ret; |
142 | } |
143 | |
144 | struct gpio_leds_priv { |
145 | int num_leds; |
146 | struct gpio_led_data leds[] __counted_by(num_leds); |
147 | }; |
148 | |
149 | static struct gpio_leds_priv *gpio_leds_create(struct device *dev) |
150 | { |
151 | struct fwnode_handle *child; |
152 | struct gpio_leds_priv *priv; |
153 | int count, ret; |
154 | |
155 | count = device_get_child_node_count(dev); |
156 | if (!count) |
157 | return ERR_PTR(error: -ENODEV); |
158 | |
159 | priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); |
160 | if (!priv) |
161 | return ERR_PTR(error: -ENOMEM); |
162 | |
163 | device_for_each_child_node(dev, child) { |
164 | struct gpio_led_data *led_dat = &priv->leds[priv->num_leds]; |
165 | struct gpio_led led = {}; |
166 | |
167 | /* |
168 | * Acquire gpiod from DT with uninitialized label, which |
169 | * will be updated after LED class device is registered, |
170 | * Only then the final LED name is known. |
171 | */ |
172 | led.gpiod = devm_fwnode_gpiod_get(dev, fwnode: child, NULL, flags: GPIOD_ASIS, |
173 | NULL); |
174 | if (IS_ERR(ptr: led.gpiod)) { |
175 | dev_err_probe(dev, err: PTR_ERR(ptr: led.gpiod), fmt: "Failed to get GPIO '%pfw'\n" , |
176 | child); |
177 | fwnode_handle_put(fwnode: child); |
178 | return ERR_CAST(ptr: led.gpiod); |
179 | } |
180 | |
181 | led_dat->gpiod = led.gpiod; |
182 | |
183 | led.default_state = led_init_default_state_get(fwnode: child); |
184 | |
185 | if (fwnode_property_present(fwnode: child, propname: "retain-state-suspended" )) |
186 | led.retain_state_suspended = 1; |
187 | if (fwnode_property_present(fwnode: child, propname: "retain-state-shutdown" )) |
188 | led.retain_state_shutdown = 1; |
189 | if (fwnode_property_present(fwnode: child, propname: "panic-indicator" )) |
190 | led.panic_indicator = 1; |
191 | |
192 | ret = create_gpio_led(template: &led, led_dat, parent: dev, fwnode: child, NULL); |
193 | if (ret < 0) { |
194 | fwnode_handle_put(fwnode: child); |
195 | return ERR_PTR(error: ret); |
196 | } |
197 | /* Set gpiod label to match the corresponding LED name. */ |
198 | gpiod_set_consumer_name(desc: led_dat->gpiod, |
199 | name: led_dat->cdev.dev->kobj.name); |
200 | priv->num_leds++; |
201 | } |
202 | |
203 | return priv; |
204 | } |
205 | |
206 | static const struct of_device_id of_gpio_leds_match[] = { |
207 | { .compatible = "gpio-leds" , }, |
208 | {}, |
209 | }; |
210 | |
211 | MODULE_DEVICE_TABLE(of, of_gpio_leds_match); |
212 | |
213 | static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx, |
214 | const struct gpio_led *template) |
215 | { |
216 | struct gpio_desc *gpiod; |
217 | unsigned long flags = GPIOF_OUT_INIT_LOW; |
218 | int ret; |
219 | |
220 | /* |
221 | * This means the LED does not come from the device tree |
222 | * or ACPI, so let's try just getting it by index from the |
223 | * device, this will hit the board file, if any and get |
224 | * the GPIO from there. |
225 | */ |
226 | gpiod = devm_gpiod_get_index_optional(dev, NULL, index: idx, flags: GPIOD_OUT_LOW); |
227 | if (IS_ERR(ptr: gpiod)) |
228 | return gpiod; |
229 | if (gpiod) { |
230 | gpiod_set_consumer_name(desc: gpiod, name: template->name); |
231 | return gpiod; |
232 | } |
233 | |
234 | /* |
235 | * This is the legacy code path for platform code that |
236 | * still uses GPIO numbers. Ultimately we would like to get |
237 | * rid of this block completely. |
238 | */ |
239 | |
240 | /* skip leds that aren't available */ |
241 | if (!gpio_is_valid(number: template->gpio)) |
242 | return ERR_PTR(error: -ENOENT); |
243 | |
244 | if (template->active_low) |
245 | flags |= GPIOF_ACTIVE_LOW; |
246 | |
247 | ret = devm_gpio_request_one(dev, gpio: template->gpio, flags, |
248 | label: template->name); |
249 | if (ret < 0) |
250 | return ERR_PTR(error: ret); |
251 | |
252 | gpiod = gpio_to_desc(gpio: template->gpio); |
253 | if (!gpiod) |
254 | return ERR_PTR(error: -EINVAL); |
255 | |
256 | return gpiod; |
257 | } |
258 | |
259 | static int gpio_led_probe(struct platform_device *pdev) |
260 | { |
261 | struct device *dev = &pdev->dev; |
262 | struct gpio_led_platform_data *pdata = dev_get_platdata(dev); |
263 | struct gpio_leds_priv *priv; |
264 | int i, ret; |
265 | |
266 | if (pdata && pdata->num_leds) { |
267 | priv = devm_kzalloc(dev, struct_size(priv, leds, pdata->num_leds), GFP_KERNEL); |
268 | if (!priv) |
269 | return -ENOMEM; |
270 | |
271 | priv->num_leds = pdata->num_leds; |
272 | for (i = 0; i < priv->num_leds; i++) { |
273 | const struct gpio_led *template = &pdata->leds[i]; |
274 | struct gpio_led_data *led_dat = &priv->leds[i]; |
275 | |
276 | if (template->gpiod) |
277 | led_dat->gpiod = template->gpiod; |
278 | else |
279 | led_dat->gpiod = |
280 | gpio_led_get_gpiod(dev, idx: i, template); |
281 | if (IS_ERR(ptr: led_dat->gpiod)) { |
282 | dev_info(dev, "Skipping unavailable LED gpio %d (%s)\n" , |
283 | template->gpio, template->name); |
284 | continue; |
285 | } |
286 | |
287 | ret = create_gpio_led(template, led_dat, parent: dev, NULL, |
288 | blink_set: pdata->gpio_blink_set); |
289 | if (ret < 0) |
290 | return ret; |
291 | } |
292 | } else { |
293 | priv = gpio_leds_create(dev); |
294 | if (IS_ERR(ptr: priv)) |
295 | return PTR_ERR(ptr: priv); |
296 | } |
297 | |
298 | platform_set_drvdata(pdev, data: priv); |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | static void gpio_led_shutdown(struct platform_device *pdev) |
304 | { |
305 | struct gpio_leds_priv *priv = platform_get_drvdata(pdev); |
306 | int i; |
307 | |
308 | for (i = 0; i < priv->num_leds; i++) { |
309 | struct gpio_led_data *led = &priv->leds[i]; |
310 | |
311 | if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN)) |
312 | gpio_led_set(led_cdev: &led->cdev, value: LED_OFF); |
313 | } |
314 | } |
315 | |
316 | static struct platform_driver gpio_led_driver = { |
317 | .probe = gpio_led_probe, |
318 | .shutdown = gpio_led_shutdown, |
319 | .driver = { |
320 | .name = "leds-gpio" , |
321 | .of_match_table = of_gpio_leds_match, |
322 | }, |
323 | }; |
324 | |
325 | module_platform_driver(gpio_led_driver); |
326 | |
327 | MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>" ); |
328 | MODULE_DESCRIPTION("GPIO LED driver" ); |
329 | MODULE_LICENSE("GPL" ); |
330 | MODULE_ALIAS("platform:leds-gpio" ); |
331 | |