Blame view

drivers/leds/leds-gpio.c 8.1 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
22e03f3b5   Raphael Assenat   leds: Add generic...
2
3
4
5
6
  /*
   * LEDs driver for GPIOs
   *
   * Copyright (C) 2007 8D Technologies inc.
   * Raphael Assenat <raph@8d.com>
a7d878af9   Trent Piepho   leds: Add openfir...
7
   * Copyright (C) 2008 Freescale Semiconductor, Inc.
22e03f3b5   Raphael Assenat   leds: Add generic...
8
   */
4cc72346f   Xiubo Li   led: gpio: Sort i...
9
  #include <linux/err.h>
16db7f909   Mark Brown   drivers/leds/leds...
10
  #include <linux/gpio.h>
5c51277a9   Mika Westerberg   leds: leds-gpio: ...
11
  #include <linux/gpio/consumer.h>
4cc72346f   Xiubo Li   led: gpio: Sort i...
12
  #include <linux/kernel.h>
22e03f3b5   Raphael Assenat   leds: Add generic...
13
  #include <linux/leds.h>
4cc72346f   Xiubo Li   led: gpio: Sort i...
14
  #include <linux/module.h>
403097f72   Geert Uytterhoeven   leds: leds-gpio: ...
15
  #include <linux/of.h>
4cc72346f   Xiubo Li   led: gpio: Sort i...
16
  #include <linux/platform_device.h>
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
17
  #include <linux/property.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
00852279a   David Brownell   leds: Teach leds-...
19

22e03f3b5   Raphael Assenat   leds: Add generic...
20
21
  struct gpio_led_data {
  	struct led_classdev cdev;
5c51277a9   Mika Westerberg   leds: leds-gpio: ...
22
  	struct gpio_desc *gpiod;
00852279a   David Brownell   leds: Teach leds-...
23
  	u8 can_sleep;
2146325df   Benjamin Herrenschmidt   leds: leds-gpio: ...
24
  	u8 blinking;
68620e594   Heiner Kallweit   leds: gpio: intro...
25
  	gpio_blink_set_t platform_gpio_blink_set;
22e03f3b5   Raphael Assenat   leds: Add generic...
26
  };
458080943   Heiner Kallweit   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   Raphael Assenat   leds: Add generic...
32
33
34
  static void gpio_led_set(struct led_classdev *led_cdev,
  	enum led_brightness value)
  {
458080943   Heiner Kallweit   leds: gpio: add h...
35
  	struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev);
22e03f3b5   Raphael Assenat   leds: Add generic...
36
37
38
39
40
41
  	int level;
  
  	if (value == LED_OFF)
  		level = 0;
  	else
  		level = 1;
d5b8a0900   Jacek Anaszewski   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   Benjamin Herrenschmidt   leds: leds-gpio: ...
46
  	} else {
d5b8a0900   Jacek Anaszewski   leds: gpio: Remov...
47
48
49
  		if (led_dat->can_sleep)
  			gpiod_set_value_cansleep(led_dat->gpiod, level);
  		else
5c51277a9   Mika Westerberg   leds: leds-gpio: ...
50
  			gpiod_set_value(led_dat->gpiod, level);
2146325df   Benjamin Herrenschmidt   leds: leds-gpio: ...
51
  	}
22e03f3b5   Raphael Assenat   leds: Add generic...
52
  }
d5b8a0900   Jacek Anaszewski   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   Herbert Valerio Riedel   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   Heiner Kallweit   leds: gpio: add h...
62
  	struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev);
ca3259b36   Herbert Valerio Riedel   leds: enable supp...
63

2146325df   Benjamin Herrenschmidt   leds: leds-gpio: ...
64
  	led_dat->blinking = 1;
c673a2b40   Mika Westerberg   leds: leds-gpio: ...
65
  	return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK,
2146325df   Benjamin Herrenschmidt   leds: leds-gpio: ...
66
  						delay_on, delay_off);
ca3259b36   Herbert Valerio Riedel   leds: enable supp...
67
  }
98ea1ea20   Bill Pemberton   leds: remove use ...
68
  static int create_gpio_led(const struct gpio_led *template,
a7d878af9   Trent Piepho   leds: Add openfir...
69
  	struct gpio_led_data *led_dat, struct device *parent,
d7235f5fe   Jacek Anaszewski   leds: gpio: Use g...
70
  	struct fwnode_handle *fwnode, gpio_blink_set_t blink_set)
a7d878af9   Trent Piepho   leds: Add openfir...
71
  {
d7235f5fe   Jacek Anaszewski   leds: gpio: Use g...
72
  	struct led_init_data init_data = {};
ed88bae69   Trent Piepho   leds: Add options...
73
  	int ret, state;
a7d878af9   Trent Piepho   leds: Add openfir...
74

a7d878af9   Trent Piepho   leds: Add openfir...
75
  	led_dat->cdev.default_trigger = template->default_trigger;
ec98a4975   Geert Uytterhoeven   leds: leds-gpio: ...
76
  	led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod);
d5b8a0900   Jacek Anaszewski   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   Benjamin Herrenschmidt   leds: leds-gpio: ...
81
  	led_dat->blinking = 0;
a7d878af9   Trent Piepho   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   Heiner Kallweit   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   Trent Piepho   leds: Add options...
91
  		state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
061b5c1d0   Heiner Kallweit   leds: gpio: fix a...
92
  	}
ed88bae69   Trent Piepho   leds: Add options...
93
  	led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
defb512d2   Richard Purdie   leds: Add suspend...
94
95
  	if (!template->retain_state_suspended)
  		led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
80d6737b2   Ezequiel Garcia   leds: gpio: Suppo...
96
97
  	if (template->panic_indicator)
  		led_dat->cdev.flags |= LED_PANIC_INDICATOR;
f5808ac15   Andrew Jeffery   leds: gpio: Allow...
98
99
  	if (template->retain_state_shutdown)
  		led_dat->cdev.flags |= LED_RETAIN_AT_SHUTDOWN;
a7d878af9   Trent Piepho   leds: Add openfir...
100

5c51277a9   Mika Westerberg   leds: leds-gpio: ...
101
  	ret = gpiod_direction_output(led_dat->gpiod, state);
a7d878af9   Trent Piepho   leds: Add openfir...
102
  	if (ret < 0)
a99d76f9e   Jingoo Han   leds: leds-gpio: ...
103
  		return ret;
d7235f5fe   Jacek Anaszewski   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   Trent Piepho   leds: Add openfir...
114
  }
a314c5c00   Grant Likely   leds/leds-gpio: m...
115
116
117
118
  struct gpio_leds_priv {
  	int num_leds;
  	struct gpio_led_data leds[];
  };
22e03f3b5   Raphael Assenat   leds: Add generic...
119

a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
120
  static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
a7d878af9   Trent Piepho   leds: Add openfir...
121
  {
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
122
123
  	struct device *dev = &pdev->dev;
  	struct fwnode_handle *child;
a314c5c00   Grant Likely   leds/leds-gpio: m...
124
  	struct gpio_leds_priv *priv;
127aedc8e   Tobias Klauser   leds: leds-gpio: ...
125
  	int count, ret;
a7d878af9   Trent Piepho   leds: Add openfir...
126

a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
127
  	count = device_get_child_node_count(dev);
a7d878af9   Trent Piepho   leds: Add openfir...
128
  	if (!count)
04553e925   Roland Stigge   leds: leds-gpio: ...
129
  		return ERR_PTR(-ENODEV);
cf1a1a6a7   Gustavo A. R. Silva   leds: gpio: Use s...
130
  	priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL);
a314c5c00   Grant Likely   leds/leds-gpio: m...
131
  	if (!priv)
04553e925   Roland Stigge   leds: leds-gpio: ...
132
  		return ERR_PTR(-ENOMEM);
a7d878af9   Trent Piepho   leds: Add openfir...
133

a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
134
  	device_for_each_child_node(dev, child) {
bff23714b   Rafał Miłecki   leds: leds-gpio: ...
135
  		struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
0493a4ff1   Anton Vorontsov   leds-gpio: fix de...
136
  		struct gpio_led led = {};
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
137
  		const char *state = NULL;
bec69de89   Heiner Kallweit   leds: gpio: fix a...
138

90a8e82d3   Jacek Anaszewski   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   Boris Brezillon   gpio: Rename devm...
144
145
  		led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
  							     GPIOD_ASIS,
90a8e82d3   Jacek Anaszewski   leds: gpio: Fix u...
146
  							     NULL);
b2987d743   Alexander Stein   gpio: Pass GPIO l...
147
148
149
150
  		if (IS_ERR(led.gpiod)) {
  			fwnode_handle_put(child);
  			return ERR_CAST(led.gpiod);
  		}
72b8ad40e   Liviu Dudau   leds: gpio: set l...
151
  		led_dat->gpiod = led.gpiod;
d735d25e6   Fabio Estevam   leds: leds-gpio: ...
152
  		if (!fwnode_property_read_string(child, "default-state",
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
153
  						 &state)) {
ed88bae69   Trent Piepho   leds: Add options...
154
155
  			if (!strcmp(state, "keep"))
  				led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
a314c5c00   Grant Likely   leds/leds-gpio: m...
156
  			else if (!strcmp(state, "on"))
ed88bae69   Trent Piepho   leds: Add options...
157
158
159
160
  				led.default_state = LEDS_GPIO_DEFSTATE_ON;
  			else
  				led.default_state = LEDS_GPIO_DEFSTATE_OFF;
  		}
a7d878af9   Trent Piepho   leds: Add openfir...
161

a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
162
  		if (fwnode_property_present(child, "retain-state-suspended"))
4270a78d2   Robin Gong   leds: leds-gpio: ...
163
  			led.retain_state_suspended = 1;
f5808ac15   Andrew Jeffery   leds: gpio: Allow...
164
165
  		if (fwnode_property_present(child, "retain-state-shutdown"))
  			led.retain_state_shutdown = 1;
80d6737b2   Ezequiel Garcia   leds: gpio: Suppo...
166
167
  		if (fwnode_property_present(child, "panic-indicator"))
  			led.panic_indicator = 1;
4270a78d2   Robin Gong   leds: leds-gpio: ...
168

d7235f5fe   Jacek Anaszewski   leds: gpio: Use g...
169
  		ret = create_gpio_led(&led, led_dat, dev, child, NULL);
a7d878af9   Trent Piepho   leds: Add openfir...
170
  		if (ret < 0) {
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
171
  			fwnode_handle_put(child);
74b69e524   Heiner Kallweit   leds: gpio: fix a...
172
  			return ERR_PTR(ret);
a7d878af9   Trent Piepho   leds: Add openfir...
173
  		}
90a8e82d3   Jacek Anaszewski   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   Sebastian Hesselbarth   leds: gpio: Fix d...
177
  		priv->num_leds++;
a7d878af9   Trent Piepho   leds: Add openfir...
178
  	}
a314c5c00   Grant Likely   leds/leds-gpio: m...
179
  	return priv;
a314c5c00   Grant Likely   leds/leds-gpio: m...
180
  }
a7d878af9   Trent Piepho   leds: Add openfir...
181

a314c5c00   Grant Likely   leds/leds-gpio: m...
182
183
184
185
  static const struct of_device_id of_gpio_leds_match[] = {
  	{ .compatible = "gpio-leds", },
  	{},
  };
472b854bb   Paolo Pisati   leds-gpio: of: in...
186
187
  
  MODULE_DEVICE_TABLE(of, of_gpio_leds_match);
a7d878af9   Trent Piepho   leds: Add openfir...
188

45d4c6de4   Linus Walleij   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   Linus Walleij   leds: gpio: Fix s...
202
  	gpiod = devm_gpiod_get_index(dev, NULL, idx, GPIOD_OUT_LOW);
45d4c6de4   Linus Walleij   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   Bill Pemberton   leds: remove use ...
234
  static int gpio_led_probe(struct platform_device *pdev)
a314c5c00   Grant Likely   leds/leds-gpio: m...
235
  {
87aae1ea8   Jingoo Han   leds: use dev_get...
236
  	struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
a314c5c00   Grant Likely   leds/leds-gpio: m...
237
238
239
240
  	struct gpio_leds_priv *priv;
  	int i, ret = 0;
  
  	if (pdata && pdata->num_leds) {
cf1a1a6a7   Gustavo A. R. Silva   leds: gpio: Use s...
241
242
  		priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, pdata->num_leds),
  				    GFP_KERNEL);
a314c5c00   Grant Likely   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   Linus Walleij   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   Rafał Miłecki   leds: gpio: use O...
265
266
  					      &pdev->dev, NULL,
  					      pdata->gpio_blink_set);
bc2c0dd85   Heiner Kallweit   leds: gpio: switc...
267
  			if (ret < 0)
a314c5c00   Grant Likely   leds/leds-gpio: m...
268
  				return ret;
a314c5c00   Grant Likely   leds/leds-gpio: m...
269
270
  		}
  	} else {
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
271
  		priv = gpio_leds_create(pdev);
04553e925   Roland Stigge   leds: leds-gpio: ...
272
273
  		if (IS_ERR(priv))
  			return PTR_ERR(priv);
a314c5c00   Grant Likely   leds/leds-gpio: m...
274
275
276
277
278
  	}
  
  	platform_set_drvdata(pdev, priv);
  
  	return 0;
a7d878af9   Trent Piepho   leds: Add openfir...
279
  }
707f33ed8   Heiko Schocher   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   Andrew Jeffery   leds: gpio: Allow...
287
288
  		if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN))
  			gpio_led_set(&led->cdev, LED_OFF);
707f33ed8   Heiko Schocher   leds: leds-gpio: ...
289
290
  	}
  }
a314c5c00   Grant Likely   leds/leds-gpio: m...
291
292
  static struct platform_driver gpio_led_driver = {
  	.probe		= gpio_led_probe,
707f33ed8   Heiko Schocher   leds: leds-gpio: ...
293
  	.shutdown	= gpio_led_shutdown,
a314c5c00   Grant Likely   leds/leds-gpio: m...
294
295
  	.driver		= {
  		.name	= "leds-gpio",
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
296
  		.of_match_table = of_gpio_leds_match,
a7d878af9   Trent Piepho   leds: Add openfir...
297
  	},
a7d878af9   Trent Piepho   leds: Add openfir...
298
  };
a314c5c00   Grant Likely   leds/leds-gpio: m...
299

892a8843f   Axel Lin   leds: convert led...
300
  module_platform_driver(gpio_led_driver);
a7d878af9   Trent Piepho   leds: Add openfir...
301
302
  
  MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
22e03f3b5   Raphael Assenat   leds: Add generic...
303
304
  MODULE_DESCRIPTION("GPIO LED driver");
  MODULE_LICENSE("GPL");
892a8843f   Axel Lin   leds: convert led...
305
  MODULE_ALIAS("platform:leds-gpio");