Blame view
drivers/leds/leds-gpio.c
8.1 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
22e03f3b5 leds: Add generic... |
2 3 4 5 6 |
/* * LEDs driver for GPIOs * * Copyright (C) 2007 8D Technologies inc. * Raphael Assenat <raph@8d.com> |
a7d878af9 leds: Add openfir... |
7 |
* Copyright (C) 2008 Freescale Semiconductor, Inc. |
22e03f3b5 leds: Add generic... |
8 |
*/ |
4cc72346f led: gpio: Sort i... |
9 |
#include <linux/err.h> |
16db7f909 drivers/leds/leds... |
10 |
#include <linux/gpio.h> |
5c51277a9 leds: leds-gpio: ... |
11 |
#include <linux/gpio/consumer.h> |
4cc72346f led: gpio: Sort i... |
12 |
#include <linux/kernel.h> |
22e03f3b5 leds: Add generic... |
13 |
#include <linux/leds.h> |
4cc72346f led: gpio: Sort i... |
14 |
#include <linux/module.h> |
403097f72 leds: leds-gpio: ... |
15 |
#include <linux/of.h> |
4cc72346f led: gpio: Sort i... |
16 |
#include <linux/platform_device.h> |
a43f2cbbb leds: leds-gpio: ... |
17 |
#include <linux/property.h> |
5a0e3ad6a include cleanup: ... |
18 |
#include <linux/slab.h> |
00852279a leds: Teach leds-... |
19 |
|
22e03f3b5 leds: Add generic... |
20 21 |
struct gpio_led_data { struct led_classdev cdev; |
5c51277a9 leds: leds-gpio: ... |
22 |
struct gpio_desc *gpiod; |
00852279a leds: Teach leds-... |
23 |
u8 can_sleep; |
2146325df leds: leds-gpio: ... |
24 |
u8 blinking; |
68620e594 leds: gpio: intro... |
25 |
gpio_blink_set_t platform_gpio_blink_set; |
22e03f3b5 leds: Add generic... |
26 |
}; |
458080943 leds: gpio: add h... |
27 28 29 30 31 |
static inline struct gpio_led_data * cdev_to_gpio_led_data(struct led_classdev *led_cdev) { return container_of(led_cdev, struct gpio_led_data, cdev); } |
22e03f3b5 leds: Add generic... |
32 33 34 |
static void gpio_led_set(struct led_classdev *led_cdev, enum led_brightness value) { |
458080943 leds: gpio: add h... |
35 |
struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev); |
22e03f3b5 leds: Add generic... |
36 37 38 39 40 41 |
int level; if (value == LED_OFF) level = 0; else level = 1; |
d5b8a0900 leds: gpio: Remov... |
42 43 44 45 |
if (led_dat->blinking) { led_dat->platform_gpio_blink_set(led_dat->gpiod, level, NULL, NULL); led_dat->blinking = 0; |
2146325df leds: leds-gpio: ... |
46 |
} else { |
d5b8a0900 leds: gpio: Remov... |
47 48 49 |
if (led_dat->can_sleep) gpiod_set_value_cansleep(led_dat->gpiod, level); else |
5c51277a9 leds: leds-gpio: ... |
50 |
gpiod_set_value(led_dat->gpiod, level); |
2146325df leds: leds-gpio: ... |
51 |
} |
22e03f3b5 leds: Add generic... |
52 |
} |
d5b8a0900 leds: gpio: Remov... |
53 54 55 56 57 58 |
static int gpio_led_set_blocking(struct led_classdev *led_cdev, enum led_brightness value) { gpio_led_set(led_cdev, value); return 0; } |
ca3259b36 leds: enable supp... |
59 60 61 |
static int gpio_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { |
458080943 leds: gpio: add h... |
62 |
struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev); |
ca3259b36 leds: enable supp... |
63 |
|
2146325df leds: leds-gpio: ... |
64 |
led_dat->blinking = 1; |
c673a2b40 leds: leds-gpio: ... |
65 |
return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK, |
2146325df leds: leds-gpio: ... |
66 |
delay_on, delay_off); |
ca3259b36 leds: enable supp... |
67 |
} |
98ea1ea20 leds: remove use ... |
68 |
static int create_gpio_led(const struct gpio_led *template, |
a7d878af9 leds: Add openfir... |
69 |
struct gpio_led_data *led_dat, struct device *parent, |
d7235f5fe leds: gpio: Use g... |
70 |
struct fwnode_handle *fwnode, gpio_blink_set_t blink_set) |
a7d878af9 leds: Add openfir... |
71 |
{ |
d7235f5fe leds: gpio: Use g... |
72 |
struct led_init_data init_data = {}; |
ed88bae69 leds: Add options... |
73 |
int ret, state; |
a7d878af9 leds: Add openfir... |
74 |
|
a7d878af9 leds: Add openfir... |
75 |
led_dat->cdev.default_trigger = template->default_trigger; |
ec98a4975 leds: leds-gpio: ... |
76 |
led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod); |
d5b8a0900 leds: gpio: Remov... |
77 78 79 80 |
if (!led_dat->can_sleep) led_dat->cdev.brightness_set = gpio_led_set; else led_dat->cdev.brightness_set_blocking = gpio_led_set_blocking; |
2146325df leds: leds-gpio: ... |
81 |
led_dat->blinking = 0; |
a7d878af9 leds: Add openfir... |
82 83 84 85 |
if (blink_set) { led_dat->platform_gpio_blink_set = blink_set; led_dat->cdev.blink_set = gpio_blink_set; } |
061b5c1d0 leds: gpio: fix a... |
86 87 88 89 90 |
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) { state = gpiod_get_value_cansleep(led_dat->gpiod); if (state < 0) return state; } else { |
ed88bae69 leds: Add options... |
91 |
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); |
061b5c1d0 leds: gpio: fix a... |
92 |
} |
ed88bae69 leds: Add options... |
93 |
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; |
defb512d2 leds: Add suspend... |
94 95 |
if (!template->retain_state_suspended) led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
80d6737b2 leds: gpio: Suppo... |
96 97 |
if (template->panic_indicator) led_dat->cdev.flags |= LED_PANIC_INDICATOR; |
f5808ac15 leds: gpio: Allow... |
98 99 |
if (template->retain_state_shutdown) led_dat->cdev.flags |= LED_RETAIN_AT_SHUTDOWN; |
a7d878af9 leds: Add openfir... |
100 |
|
5c51277a9 leds: leds-gpio: ... |
101 |
ret = gpiod_direction_output(led_dat->gpiod, state); |
a7d878af9 leds: Add openfir... |
102 |
if (ret < 0) |
a99d76f9e leds: leds-gpio: ... |
103 |
return ret; |
d7235f5fe leds: gpio: Use g... |
104 105 106 107 108 109 110 111 112 113 |
if (template->name) { led_dat->cdev.name = template->name; ret = devm_led_classdev_register(parent, &led_dat->cdev); } else { init_data.fwnode = fwnode; ret = devm_led_classdev_register_ext(parent, &led_dat->cdev, &init_data); } return ret; |
a7d878af9 leds: Add openfir... |
114 |
} |
a314c5c00 leds/leds-gpio: m... |
115 116 117 118 |
struct gpio_leds_priv { int num_leds; struct gpio_led_data leds[]; }; |
22e03f3b5 leds: Add generic... |
119 |
|
a43f2cbbb leds: leds-gpio: ... |
120 |
static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) |
a7d878af9 leds: Add openfir... |
121 |
{ |
a43f2cbbb leds: leds-gpio: ... |
122 123 |
struct device *dev = &pdev->dev; struct fwnode_handle *child; |
a314c5c00 leds/leds-gpio: m... |
124 |
struct gpio_leds_priv *priv; |
127aedc8e leds: leds-gpio: ... |
125 |
int count, ret; |
a7d878af9 leds: Add openfir... |
126 |
|
a43f2cbbb leds: leds-gpio: ... |
127 |
count = device_get_child_node_count(dev); |
a7d878af9 leds: Add openfir... |
128 |
if (!count) |
04553e925 leds: leds-gpio: ... |
129 |
return ERR_PTR(-ENODEV); |
cf1a1a6a7 leds: gpio: Use s... |
130 |
priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); |
a314c5c00 leds/leds-gpio: m... |
131 |
if (!priv) |
04553e925 leds: leds-gpio: ... |
132 |
return ERR_PTR(-ENOMEM); |
a7d878af9 leds: Add openfir... |
133 |
|
a43f2cbbb leds: leds-gpio: ... |
134 |
device_for_each_child_node(dev, child) { |
bff23714b leds: leds-gpio: ... |
135 |
struct gpio_led_data *led_dat = &priv->leds[priv->num_leds]; |
0493a4ff1 leds-gpio: fix de... |
136 |
struct gpio_led led = {}; |
a43f2cbbb leds: leds-gpio: ... |
137 |
const char *state = NULL; |
bec69de89 leds: gpio: fix a... |
138 |
|
90a8e82d3 leds: gpio: Fix u... |
139 140 141 142 143 |
/* * Acquire gpiod from DT with uninitialized label, which * will be updated after LED class device is registered, * Only then the final LED name is known. */ |
4b0947974 gpio: Rename devm... |
144 145 |
led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child, GPIOD_ASIS, |
90a8e82d3 leds: gpio: Fix u... |
146 |
NULL); |
b2987d743 gpio: Pass GPIO l... |
147 148 149 150 |
if (IS_ERR(led.gpiod)) { fwnode_handle_put(child); return ERR_CAST(led.gpiod); } |
72b8ad40e leds: gpio: set l... |
151 |
led_dat->gpiod = led.gpiod; |
d735d25e6 leds: leds-gpio: ... |
152 |
if (!fwnode_property_read_string(child, "default-state", |
a43f2cbbb leds: leds-gpio: ... |
153 |
&state)) { |
ed88bae69 leds: Add options... |
154 155 |
if (!strcmp(state, "keep")) led.default_state = LEDS_GPIO_DEFSTATE_KEEP; |
a314c5c00 leds/leds-gpio: m... |
156 |
else if (!strcmp(state, "on")) |
ed88bae69 leds: Add options... |
157 158 159 160 |
led.default_state = LEDS_GPIO_DEFSTATE_ON; else led.default_state = LEDS_GPIO_DEFSTATE_OFF; } |
a7d878af9 leds: Add openfir... |
161 |
|
a43f2cbbb leds: leds-gpio: ... |
162 |
if (fwnode_property_present(child, "retain-state-suspended")) |
4270a78d2 leds: leds-gpio: ... |
163 |
led.retain_state_suspended = 1; |
f5808ac15 leds: gpio: Allow... |
164 165 |
if (fwnode_property_present(child, "retain-state-shutdown")) led.retain_state_shutdown = 1; |
80d6737b2 leds: gpio: Suppo... |
166 167 |
if (fwnode_property_present(child, "panic-indicator")) led.panic_indicator = 1; |
4270a78d2 leds: leds-gpio: ... |
168 |
|
d7235f5fe leds: gpio: Use g... |
169 |
ret = create_gpio_led(&led, led_dat, dev, child, NULL); |
a7d878af9 leds: Add openfir... |
170 |
if (ret < 0) { |
a43f2cbbb leds: leds-gpio: ... |
171 |
fwnode_handle_put(child); |
74b69e524 leds: gpio: fix a... |
172 |
return ERR_PTR(ret); |
a7d878af9 leds: Add openfir... |
173 |
} |
90a8e82d3 leds: gpio: Fix u... |
174 175 176 |
/* Set gpiod label to match the corresponding LED name. */ gpiod_set_consumer_name(led_dat->gpiod, led_dat->cdev.dev->kobj.name); |
65c6b7e3a leds: gpio: Fix d... |
177 |
priv->num_leds++; |
a7d878af9 leds: Add openfir... |
178 |
} |
a314c5c00 leds/leds-gpio: m... |
179 |
return priv; |
a314c5c00 leds/leds-gpio: m... |
180 |
} |
a7d878af9 leds: Add openfir... |
181 |
|
a314c5c00 leds/leds-gpio: m... |
182 183 184 185 |
static const struct of_device_id of_gpio_leds_match[] = { { .compatible = "gpio-leds", }, {}, }; |
472b854bb leds-gpio: of: in... |
186 187 |
MODULE_DEVICE_TABLE(of, of_gpio_leds_match); |
a7d878af9 leds: Add openfir... |
188 |
|
45d4c6de4 leds: gpio: Try t... |
189 190 191 192 193 194 195 196 197 198 199 200 201 |
static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx, const struct gpio_led *template) { struct gpio_desc *gpiod; unsigned long flags = GPIOF_OUT_INIT_LOW; int ret; /* * This means the LED does not come from the device tree * or ACPI, so let's try just getting it by index from the * device, this will hit the board file, if any and get * the GPIO from there. */ |
c4e944139 leds: gpio: Fix s... |
202 |
gpiod = devm_gpiod_get_index(dev, NULL, idx, GPIOD_OUT_LOW); |
45d4c6de4 leds: gpio: Try t... |
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
if (!IS_ERR(gpiod)) { gpiod_set_consumer_name(gpiod, template->name); return gpiod; } if (PTR_ERR(gpiod) != -ENOENT) return gpiod; /* * This is the legacy code path for platform code that * still uses GPIO numbers. Ultimately we would like to get * rid of this block completely. */ /* skip leds that aren't available */ if (!gpio_is_valid(template->gpio)) return ERR_PTR(-ENOENT); if (template->active_low) flags |= GPIOF_ACTIVE_LOW; ret = devm_gpio_request_one(dev, template->gpio, flags, template->name); if (ret < 0) return ERR_PTR(ret); gpiod = gpio_to_desc(template->gpio); if (!gpiod) return ERR_PTR(-EINVAL); return gpiod; } |
98ea1ea20 leds: remove use ... |
234 |
static int gpio_led_probe(struct platform_device *pdev) |
a314c5c00 leds/leds-gpio: m... |
235 |
{ |
87aae1ea8 leds: use dev_get... |
236 |
struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
a314c5c00 leds/leds-gpio: m... |
237 238 239 240 |
struct gpio_leds_priv *priv; int i, ret = 0; if (pdata && pdata->num_leds) { |
cf1a1a6a7 leds: gpio: Use s... |
241 242 |
priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, pdata->num_leds), GFP_KERNEL); |
a314c5c00 leds/leds-gpio: m... |
243 244 245 246 247 |
if (!priv) return -ENOMEM; priv->num_leds = pdata->num_leds; for (i = 0; i < priv->num_leds; i++) { |
45d4c6de4 leds: gpio: Try t... |
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
const struct gpio_led *template = &pdata->leds[i]; struct gpio_led_data *led_dat = &priv->leds[i]; if (template->gpiod) led_dat->gpiod = template->gpiod; else led_dat->gpiod = gpio_led_get_gpiod(&pdev->dev, i, template); if (IS_ERR(led_dat->gpiod)) { dev_info(&pdev->dev, "Skipping unavailable LED gpio %d (%s) ", template->gpio, template->name); continue; } ret = create_gpio_led(template, led_dat, |
7ea79ae86 leds: gpio: use O... |
265 266 |
&pdev->dev, NULL, pdata->gpio_blink_set); |
bc2c0dd85 leds: gpio: switc... |
267 |
if (ret < 0) |
a314c5c00 leds/leds-gpio: m... |
268 |
return ret; |
a314c5c00 leds/leds-gpio: m... |
269 270 |
} } else { |
a43f2cbbb leds: leds-gpio: ... |
271 |
priv = gpio_leds_create(pdev); |
04553e925 leds: leds-gpio: ... |
272 273 |
if (IS_ERR(priv)) return PTR_ERR(priv); |
a314c5c00 leds/leds-gpio: m... |
274 275 276 277 278 |
} platform_set_drvdata(pdev, priv); return 0; |
a7d878af9 leds: Add openfir... |
279 |
} |
707f33ed8 leds: leds-gpio: ... |
280 281 282 283 284 285 286 |
static void gpio_led_shutdown(struct platform_device *pdev) { struct gpio_leds_priv *priv = platform_get_drvdata(pdev); int i; for (i = 0; i < priv->num_leds; i++) { struct gpio_led_data *led = &priv->leds[i]; |
f5808ac15 leds: gpio: Allow... |
287 288 |
if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN)) gpio_led_set(&led->cdev, LED_OFF); |
707f33ed8 leds: leds-gpio: ... |
289 290 |
} } |
a314c5c00 leds/leds-gpio: m... |
291 292 |
static struct platform_driver gpio_led_driver = { .probe = gpio_led_probe, |
707f33ed8 leds: leds-gpio: ... |
293 |
.shutdown = gpio_led_shutdown, |
a314c5c00 leds/leds-gpio: m... |
294 295 |
.driver = { .name = "leds-gpio", |
a43f2cbbb leds: leds-gpio: ... |
296 |
.of_match_table = of_gpio_leds_match, |
a7d878af9 leds: Add openfir... |
297 |
}, |
a7d878af9 leds: Add openfir... |
298 |
}; |
a314c5c00 leds/leds-gpio: m... |
299 |
|
892a8843f leds: convert led... |
300 |
module_platform_driver(gpio_led_driver); |
a7d878af9 leds: Add openfir... |
301 302 |
MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>"); |
22e03f3b5 leds: Add generic... |
303 304 |
MODULE_DESCRIPTION("GPIO LED driver"); MODULE_LICENSE("GPL"); |
892a8843f leds: convert led... |
305 |
MODULE_ALIAS("platform:leds-gpio"); |