blob: 1ff95ce9487a9c5ab4a61d78f027789ed684d9cc [file] [log] [blame]
Raphael Assenat22e03f32007-02-27 19:49:53 +00001/*
2 * LEDs driver for GPIOs
3 *
4 * Copyright (C) 2007 8D Technologies inc.
5 * Raphael Assenat <raph@8d.com>
Trent Piephoa7d878a2009-01-10 17:26:01 +00006 * Copyright (C) 2008 Freescale Semiconductor, Inc.
Raphael Assenat22e03f32007-02-27 19:49:53 +00007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
Xiubo Li4cc72342014-09-28 01:57:14 -070013#include <linux/err.h>
Mark Brown16db7f92012-03-23 15:02:11 -070014#include <linux/gpio.h>
Mika Westerberg5c512772014-10-27 23:29:32 +010015#include <linux/gpio/consumer.h>
Xiubo Li4cc72342014-09-28 01:57:14 -070016#include <linux/kernel.h>
Raphael Assenat22e03f32007-02-27 19:49:53 +000017#include <linux/leds.h>
Xiubo Li4cc72342014-09-28 01:57:14 -070018#include <linux/module.h>
Sachin Kamatc68f46d2013-09-28 04:38:30 -070019#include <linux/of.h>
Grant Likelya314c5c2011-02-22 20:06:04 -070020#include <linux/of_gpio.h>
Xiubo Li4cc72342014-09-28 01:57:14 -070021#include <linux/of_platform.h>
22#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090023#include <linux/slab.h>
David Brownell00852272007-05-10 10:51:41 +010024#include <linux/workqueue.h>
25
Raphael Assenat22e03f32007-02-27 19:49:53 +000026struct gpio_led_data {
27 struct led_classdev cdev;
Mika Westerberg5c512772014-10-27 23:29:32 +010028 struct gpio_desc *gpiod;
David Brownell00852272007-05-10 10:51:41 +010029 struct work_struct work;
30 u8 new_level;
31 u8 can_sleep;
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100032 u8 blinking;
33 int (*platform_gpio_blink_set)(unsigned gpio, int state,
Herbert Valerio Riedelca3259b2008-03-09 23:48:25 +000034 unsigned long *delay_on, unsigned long *delay_off);
Raphael Assenat22e03f32007-02-27 19:49:53 +000035};
36
David Brownell00852272007-05-10 10:51:41 +010037static void gpio_led_work(struct work_struct *work)
38{
Xiubo Lia4c84e62014-09-28 01:57:16 -070039 struct gpio_led_data *led_dat =
David Brownell00852272007-05-10 10:51:41 +010040 container_of(work, struct gpio_led_data, work);
41
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100042 if (led_dat->blinking) {
Mika Westerberg5c512772014-10-27 23:29:32 +010043 int gpio = desc_to_gpio(led_dat->gpiod);
44 int level = led_dat->new_level;
45
46 if (gpiod_is_active_low(led_dat->gpiod))
47 level = !level;
48
49 led_dat->platform_gpio_blink_set(gpio, level, NULL, NULL);
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100050 led_dat->blinking = 0;
51 } else
Mika Westerberg5c512772014-10-27 23:29:32 +010052 gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
David Brownell00852272007-05-10 10:51:41 +010053}
Raphael Assenat22e03f32007-02-27 19:49:53 +000054
55static void gpio_led_set(struct led_classdev *led_cdev,
56 enum led_brightness value)
57{
58 struct gpio_led_data *led_dat =
59 container_of(led_cdev, struct gpio_led_data, cdev);
60 int level;
61
62 if (value == LED_OFF)
63 level = 0;
64 else
65 level = 1;
66
David Brownell306dd852008-03-27 00:59:02 +000067 /* Setting GPIOs with I2C/etc requires a task context, and we don't
68 * seem to have a reliable way to know if we're already in one; so
69 * let's just assume the worst.
70 */
David Brownell00852272007-05-10 10:51:41 +010071 if (led_dat->can_sleep) {
David Brownell306dd852008-03-27 00:59:02 +000072 led_dat->new_level = level;
73 schedule_work(&led_dat->work);
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100074 } else {
75 if (led_dat->blinking) {
Mika Westerberg5c512772014-10-27 23:29:32 +010076 int gpio = desc_to_gpio(led_dat->gpiod);
77
78 if (gpiod_is_active_low(led_dat->gpiod))
79 level = !level;
80
81 led_dat->platform_gpio_blink_set(gpio, level, NULL,
82 NULL);
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100083 led_dat->blinking = 0;
84 } else
Mika Westerberg5c512772014-10-27 23:29:32 +010085 gpiod_set_value(led_dat->gpiod, level);
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100086 }
Raphael Assenat22e03f32007-02-27 19:49:53 +000087}
88
Herbert Valerio Riedelca3259b2008-03-09 23:48:25 +000089static int gpio_blink_set(struct led_classdev *led_cdev,
90 unsigned long *delay_on, unsigned long *delay_off)
91{
92 struct gpio_led_data *led_dat =
93 container_of(led_cdev, struct gpio_led_data, cdev);
Mika Westerberg5c512772014-10-27 23:29:32 +010094 int gpio = desc_to_gpio(led_dat->gpiod);
Herbert Valerio Riedelca3259b2008-03-09 23:48:25 +000095
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100096 led_dat->blinking = 1;
Mika Westerberg5c512772014-10-27 23:29:32 +010097 return led_dat->platform_gpio_blink_set(gpio, GPIO_LED_BLINK,
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +100098 delay_on, delay_off);
Herbert Valerio Riedelca3259b2008-03-09 23:48:25 +000099}
100
Bill Pemberton98ea1ea2012-11-19 13:23:02 -0500101static int create_gpio_led(const struct gpio_led *template,
Trent Piephoa7d878a2009-01-10 17:26:01 +0000102 struct gpio_led_data *led_dat, struct device *parent,
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +1000103 int (*blink_set)(unsigned, int, unsigned long *, unsigned long *))
Trent Piephoa7d878a2009-01-10 17:26:01 +0000104{
Trent Piephoed88bae2009-05-12 15:33:12 -0700105 int ret, state;
Trent Piephoa7d878a2009-01-10 17:26:01 +0000106
Mika Westerberg5c512772014-10-27 23:29:32 +0100107 if (!template->gpiod) {
108 unsigned long flags = 0;
Dmitry Eremin-Solenikov0b4634f2009-11-16 01:48:32 +0300109
Mika Westerberg5c512772014-10-27 23:29:32 +0100110 /* skip leds that aren't available */
111 if (!gpio_is_valid(template->gpio)) {
112 dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
113 template->gpio, template->name);
114 return 0;
115 }
116
117 if (template->active_low)
118 flags |= GPIOF_ACTIVE_LOW;
119
120 ret = devm_gpio_request_one(parent, template->gpio, flags,
121 template->name);
122 if (ret < 0)
123 return ret;
124
125 led_dat->gpiod = gpio_to_desc(template->gpio);
126 if (IS_ERR(led_dat->gpiod))
127 return PTR_ERR(led_dat->gpiod);
David Brownelld379ee82009-03-05 16:46:44 -0800128 }
129
Trent Piephoa7d878a2009-01-10 17:26:01 +0000130 led_dat->cdev.name = template->name;
131 led_dat->cdev.default_trigger = template->default_trigger;
Mika Westerberg5c512772014-10-27 23:29:32 +0100132 led_dat->gpiod = template->gpiod;
133 led_dat->can_sleep = gpiod_cansleep(template->gpiod);
Benjamin Herrenschmidt21463252010-05-22 20:54:55 +1000134 led_dat->blinking = 0;
Trent Piephoa7d878a2009-01-10 17:26:01 +0000135 if (blink_set) {
136 led_dat->platform_gpio_blink_set = blink_set;
137 led_dat->cdev.blink_set = gpio_blink_set;
138 }
139 led_dat->cdev.brightness_set = gpio_led_set;
Trent Piephoed88bae2009-05-12 15:33:12 -0700140 if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
Mika Westerberg5c512772014-10-27 23:29:32 +0100141 state = !!gpiod_get_value_cansleep(led_dat->gpiod);
Trent Piephoed88bae2009-05-12 15:33:12 -0700142 else
143 state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
144 led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
Richard Purdiedefb5122009-02-17 15:04:07 +0000145 if (!template->retain_state_suspended)
146 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
Trent Piephoa7d878a2009-01-10 17:26:01 +0000147
Mika Westerberg5c512772014-10-27 23:29:32 +0100148 ret = gpiod_direction_output(led_dat->gpiod, state);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000149 if (ret < 0)
Jingoo Hana99d76f2012-10-23 05:17:11 -0700150 return ret;
151
Trent Piephoa7d878a2009-01-10 17:26:01 +0000152 INIT_WORK(&led_dat->work, gpio_led_work);
153
Mika Westerberg5c512772014-10-27 23:29:32 +0100154 return led_classdev_register(parent, &led_dat->cdev);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000155}
156
157static void delete_gpio_led(struct gpio_led_data *led)
158{
159 led_classdev_unregister(&led->cdev);
160 cancel_work_sync(&led->work);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000161}
162
Grant Likelya314c5c2011-02-22 20:06:04 -0700163struct gpio_leds_priv {
164 int num_leds;
165 struct gpio_led_data leds[];
Raphael Assenat22e03f32007-02-27 19:49:53 +0000166};
167
Grant Likelya314c5c2011-02-22 20:06:04 -0700168static inline int sizeof_gpio_leds_priv(int num_leds)
169{
170 return sizeof(struct gpio_leds_priv) +
171 (sizeof(struct gpio_led_data) * num_leds);
172}
Trent Piephoa7d878a2009-01-10 17:26:01 +0000173
174/* Code to create from OpenFirmware platform devices */
Shawn Guo2bcc7ed2011-05-31 16:23:43 +0800175#ifdef CONFIG_OF_GPIO
Bill Pemberton98ea1ea2012-11-19 13:23:02 -0500176static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
Trent Piephoa7d878a2009-01-10 17:26:01 +0000177{
Grant Likelya314c5c2011-02-22 20:06:04 -0700178 struct device_node *np = pdev->dev.of_node, *child;
179 struct gpio_leds_priv *priv;
Tobias Klauser127aedc2012-08-21 17:21:53 +0800180 int count, ret;
Trent Piephoa7d878a2009-01-10 17:26:01 +0000181
Grant Likelya314c5c2011-02-22 20:06:04 -0700182 /* count LEDs in this device, so we know how much to allocate */
Josh Wub0bb83d2013-09-26 04:27:56 -0700183 count = of_get_available_child_count(np);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000184 if (!count)
Roland Stigge04553e92012-10-28 08:34:59 -0700185 return ERR_PTR(-ENODEV);
186
Josh Wub0bb83d2013-09-26 04:27:56 -0700187 for_each_available_child_of_node(np, child)
Roland Stigge04553e92012-10-28 08:34:59 -0700188 if (of_get_gpio(child, 0) == -EPROBE_DEFER)
189 return ERR_PTR(-EPROBE_DEFER);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000190
Sachin Kamat198b8612012-07-04 11:35:44 +0800191 priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
192 GFP_KERNEL);
Grant Likelya314c5c2011-02-22 20:06:04 -0700193 if (!priv)
Roland Stigge04553e92012-10-28 08:34:59 -0700194 return ERR_PTR(-ENOMEM);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000195
Josh Wub0bb83d2013-09-26 04:27:56 -0700196 for_each_available_child_of_node(np, child) {
Anton Vorontsov0493a4f2010-03-11 13:58:47 -0800197 struct gpio_led led = {};
Trent Piephoa7d878a2009-01-10 17:26:01 +0000198 enum of_gpio_flags flags;
Trent Piephoed88bae2009-05-12 15:33:12 -0700199 const char *state;
Trent Piephoa7d878a2009-01-10 17:26:01 +0000200
201 led.gpio = of_get_gpio_flags(child, 0, &flags);
202 led.active_low = flags & OF_GPIO_ACTIVE_LOW;
203 led.name = of_get_property(child, "label", NULL) ? : child->name;
204 led.default_trigger =
205 of_get_property(child, "linux,default-trigger", NULL);
Trent Piephoed88bae2009-05-12 15:33:12 -0700206 state = of_get_property(child, "default-state", NULL);
207 if (state) {
208 if (!strcmp(state, "keep"))
209 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
Grant Likelya314c5c2011-02-22 20:06:04 -0700210 else if (!strcmp(state, "on"))
Trent Piephoed88bae2009-05-12 15:33:12 -0700211 led.default_state = LEDS_GPIO_DEFSTATE_ON;
212 else
213 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
214 }
Trent Piephoa7d878a2009-01-10 17:26:01 +0000215
Robin Gong4270a782014-01-20 03:41:26 -0800216 if (of_get_property(child, "retain-state-suspended", NULL))
217 led.retain_state_suspended = 1;
218
Grant Likelya314c5c2011-02-22 20:06:04 -0700219 ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
220 &pdev->dev, NULL);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000221 if (ret < 0) {
222 of_node_put(child);
223 goto err;
224 }
225 }
226
Grant Likelya314c5c2011-02-22 20:06:04 -0700227 return priv;
Trent Piephoa7d878a2009-01-10 17:26:01 +0000228
229err:
Grant Likelya314c5c2011-02-22 20:06:04 -0700230 for (count = priv->num_leds - 2; count >= 0; count--)
231 delete_gpio_led(&priv->leds[count]);
Roland Stigge04553e92012-10-28 08:34:59 -0700232 return ERR_PTR(-ENODEV);
Trent Piephoa7d878a2009-01-10 17:26:01 +0000233}
234
235static const struct of_device_id of_gpio_leds_match[] = {
236 { .compatible = "gpio-leds", },
237 {},
238};
Paolo Pisati472b8542014-03-06 09:18:37 -0800239
240MODULE_DEVICE_TABLE(of, of_gpio_leds_match);
Shawn Guo2bcc7ed2011-05-31 16:23:43 +0800241#else /* CONFIG_OF_GPIO */
Bill Pemberton98ea1ea2012-11-19 13:23:02 -0500242static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
Grant Likelya314c5c2011-02-22 20:06:04 -0700243{
Roland Stigge04553e92012-10-28 08:34:59 -0700244 return ERR_PTR(-ENODEV);
Grant Likelya314c5c2011-02-22 20:06:04 -0700245}
Shawn Guo2bcc7ed2011-05-31 16:23:43 +0800246#endif /* CONFIG_OF_GPIO */
Trent Piephoa7d878a2009-01-10 17:26:01 +0000247
Bill Pemberton98ea1ea2012-11-19 13:23:02 -0500248static int gpio_led_probe(struct platform_device *pdev)
Grant Likelya314c5c2011-02-22 20:06:04 -0700249{
Jingoo Han87aae1e2013-07-30 01:07:35 -0700250 struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
Grant Likelya314c5c2011-02-22 20:06:04 -0700251 struct gpio_leds_priv *priv;
252 int i, ret = 0;
253
254 if (pdata && pdata->num_leds) {
Sachin Kamat198b8612012-07-04 11:35:44 +0800255 priv = devm_kzalloc(&pdev->dev,
256 sizeof_gpio_leds_priv(pdata->num_leds),
257 GFP_KERNEL);
Grant Likelya314c5c2011-02-22 20:06:04 -0700258 if (!priv)
259 return -ENOMEM;
260
261 priv->num_leds = pdata->num_leds;
262 for (i = 0; i < priv->num_leds; i++) {
263 ret = create_gpio_led(&pdata->leds[i],
264 &priv->leds[i],
265 &pdev->dev, pdata->gpio_blink_set);
266 if (ret < 0) {
267 /* On failure: unwind the led creations */
268 for (i = i - 1; i >= 0; i--)
269 delete_gpio_led(&priv->leds[i]);
Grant Likelya314c5c2011-02-22 20:06:04 -0700270 return ret;
271 }
272 }
273 } else {
274 priv = gpio_leds_create_of(pdev);
Roland Stigge04553e92012-10-28 08:34:59 -0700275 if (IS_ERR(priv))
276 return PTR_ERR(priv);
Grant Likelya314c5c2011-02-22 20:06:04 -0700277 }
278
279 platform_set_drvdata(pdev, priv);
280
281 return 0;
282}
283
Bill Pemberton678e8a62012-11-19 13:26:00 -0500284static int gpio_led_remove(struct platform_device *pdev)
Grant Likelya314c5c2011-02-22 20:06:04 -0700285{
Tobias Klauser59c4dce2012-08-16 18:36:13 +0800286 struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
Grant Likelya314c5c2011-02-22 20:06:04 -0700287 int i;
288
289 for (i = 0; i < priv->num_leds; i++)
290 delete_gpio_led(&priv->leds[i]);
291
Grant Likelya314c5c2011-02-22 20:06:04 -0700292 return 0;
293}
294
295static struct platform_driver gpio_led_driver = {
296 .probe = gpio_led_probe,
Bill Pembertondf07cf82012-11-19 13:20:20 -0500297 .remove = gpio_led_remove,
Grant Likelya314c5c2011-02-22 20:06:04 -0700298 .driver = {
299 .name = "leds-gpio",
300 .owner = THIS_MODULE,
Tobias Klauser6ebcebd2012-08-16 18:35:36 +0800301 .of_match_table = of_match_ptr(of_gpio_leds_match),
Trent Piephoa7d878a2009-01-10 17:26:01 +0000302 },
Trent Piephoa7d878a2009-01-10 17:26:01 +0000303};
Grant Likelya314c5c2011-02-22 20:06:04 -0700304
Axel Lin892a8842012-01-10 15:09:24 -0800305module_platform_driver(gpio_led_driver);
Richard Purdieb2bdc3e2009-02-02 23:04:42 +0000306
Trent Piephoa7d878a2009-01-10 17:26:01 +0000307MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
Raphael Assenat22e03f32007-02-27 19:49:53 +0000308MODULE_DESCRIPTION("GPIO LED driver");
309MODULE_LICENSE("GPL");
Axel Lin892a8842012-01-10 15:09:24 -0800310MODULE_ALIAS("platform:leds-gpio");