Blame view

drivers/leds/leds-gpio.c 8.32 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

a314c5c00   Grant Likely   leds/leds-gpio: m...
120
  static inline int sizeof_gpio_leds_priv(int num_leds)
22e03f3b5   Raphael Assenat   leds: Add generic...
121
  {
a314c5c00   Grant Likely   leds/leds-gpio: m...
122
123
  	return sizeof(struct gpio_leds_priv) +
  		(sizeof(struct gpio_led_data) * num_leds);
22e03f3b5   Raphael Assenat   leds: Add generic...
124
  }
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
125
  static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
a7d878af9   Trent Piepho   leds: Add openfir...
126
  {
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
127
128
  	struct device *dev = &pdev->dev;
  	struct fwnode_handle *child;
a314c5c00   Grant Likely   leds/leds-gpio: m...
129
  	struct gpio_leds_priv *priv;
127aedc8e   Tobias Klauser   leds: leds-gpio: ...
130
  	int count, ret;
a7d878af9   Trent Piepho   leds: Add openfir...
131

a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
132
  	count = device_get_child_node_count(dev);
a7d878af9   Trent Piepho   leds: Add openfir...
133
  	if (!count)
04553e925   Roland Stigge   leds: leds-gpio: ...
134
  		return ERR_PTR(-ENODEV);
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
135
  	priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);
a314c5c00   Grant Likely   leds/leds-gpio: m...
136
  	if (!priv)
04553e925   Roland Stigge   leds: leds-gpio: ...
137
  		return ERR_PTR(-ENOMEM);
a7d878af9   Trent Piepho   leds: Add openfir...
138

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

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

a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
169
  		if (fwnode_property_present(child, "retain-state-suspended"))
4270a78d2   Robin Gong   leds: leds-gpio: ...
170
  			led.retain_state_suspended = 1;
f5808ac15   Andrew Jeffery   leds: gpio: Allow...
171
172
  		if (fwnode_property_present(child, "retain-state-shutdown"))
  			led.retain_state_shutdown = 1;
80d6737b2   Ezequiel Garcia   leds: gpio: Suppo...
173
174
  		if (fwnode_property_present(child, "panic-indicator"))
  			led.panic_indicator = 1;
4270a78d2   Robin Gong   leds: leds-gpio: ...
175

d7235f5fe   Jacek Anaszewski   leds: gpio: Use g...
176
  		ret = create_gpio_led(&led, led_dat, dev, child, NULL);
a7d878af9   Trent Piepho   leds: Add openfir...
177
  		if (ret < 0) {
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
178
  			fwnode_handle_put(child);
74b69e524   Heiner Kallweit   leds: gpio: fix a...
179
  			return ERR_PTR(ret);
a7d878af9   Trent Piepho   leds: Add openfir...
180
  		}
5440678b5   Jacek Anaszewski   leds: gpio: Fix u...
181
182
183
  		/* 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...
184
  		priv->num_leds++;
a7d878af9   Trent Piepho   leds: Add openfir...
185
  	}
a314c5c00   Grant Likely   leds/leds-gpio: m...
186
  	return priv;
a314c5c00   Grant Likely   leds/leds-gpio: m...
187
  }
a7d878af9   Trent Piepho   leds: Add openfir...
188

a314c5c00   Grant Likely   leds/leds-gpio: m...
189
190
191
192
  static const struct of_device_id of_gpio_leds_match[] = {
  	{ .compatible = "gpio-leds", },
  	{},
  };
472b854bb   Paolo Pisati   leds-gpio: of: in...
193
194
  
  MODULE_DEVICE_TABLE(of, of_gpio_leds_match);
a7d878af9   Trent Piepho   leds: Add openfir...
195

45d4c6de4   Linus Walleij   leds: gpio: Try t...
196
197
198
199
200
201
202
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
234
235
236
237
238
239
240
  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.
  	 */
  	gpiod = devm_gpiod_get_index(dev, NULL, idx, flags);
  	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 ...
241
  static int gpio_led_probe(struct platform_device *pdev)
a314c5c00   Grant Likely   leds/leds-gpio: m...
242
  {
87aae1ea8   Jingoo Han   leds: use dev_get...
243
  	struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
a314c5c00   Grant Likely   leds/leds-gpio: m...
244
245
246
247
  	struct gpio_leds_priv *priv;
  	int i, ret = 0;
  
  	if (pdata && pdata->num_leds) {
198b86113   Sachin Kamat   leds: Use devm_kz...
248
249
250
  		priv = devm_kzalloc(&pdev->dev,
  				sizeof_gpio_leds_priv(pdata->num_leds),
  					GFP_KERNEL);
a314c5c00   Grant Likely   leds/leds-gpio: m...
251
252
253
254
255
  		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...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  			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...
273
274
  					      &pdev->dev, NULL,
  					      pdata->gpio_blink_set);
bc2c0dd85   Heiner Kallweit   leds: gpio: switc...
275
  			if (ret < 0)
a314c5c00   Grant Likely   leds/leds-gpio: m...
276
  				return ret;
a314c5c00   Grant Likely   leds/leds-gpio: m...
277
278
  		}
  	} else {
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
279
  		priv = gpio_leds_create(pdev);
04553e925   Roland Stigge   leds: leds-gpio: ...
280
281
  		if (IS_ERR(priv))
  			return PTR_ERR(priv);
a314c5c00   Grant Likely   leds/leds-gpio: m...
282
283
284
285
286
  	}
  
  	platform_set_drvdata(pdev, priv);
  
  	return 0;
a7d878af9   Trent Piepho   leds: Add openfir...
287
  }
707f33ed8   Heiko Schocher   leds: leds-gpio: ...
288
289
290
291
292
293
294
  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...
295
296
  		if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN))
  			gpio_led_set(&led->cdev, LED_OFF);
707f33ed8   Heiko Schocher   leds: leds-gpio: ...
297
298
  	}
  }
a314c5c00   Grant Likely   leds/leds-gpio: m...
299
300
  static struct platform_driver gpio_led_driver = {
  	.probe		= gpio_led_probe,
707f33ed8   Heiko Schocher   leds: leds-gpio: ...
301
  	.shutdown	= gpio_led_shutdown,
a314c5c00   Grant Likely   leds/leds-gpio: m...
302
303
  	.driver		= {
  		.name	= "leds-gpio",
a43f2cbbb   Rafael J. Wysocki   leds: leds-gpio: ...
304
  		.of_match_table = of_gpio_leds_match,
a7d878af9   Trent Piepho   leds: Add openfir...
305
  	},
a7d878af9   Trent Piepho   leds: Add openfir...
306
  };
a314c5c00   Grant Likely   leds/leds-gpio: m...
307

892a8843f   Axel Lin   leds: convert led...
308
  module_platform_driver(gpio_led_driver);
a7d878af9   Trent Piepho   leds: Add openfir...
309
310
  
  MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
22e03f3b5   Raphael Assenat   leds: Add generic...
311
312
  MODULE_DESCRIPTION("GPIO LED driver");
  MODULE_LICENSE("GPL");
892a8843f   Axel Lin   leds: convert led...
313
  MODULE_ALIAS("platform:leds-gpio");