aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2014-10-27 18:29:32 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-11-04 15:58:22 -0500
commit5c51277a9ababfa44a7f944100bdc9fbda139905 (patch)
tree79055a561eced669a00b8fa0a3e299b92ddf7f76 /drivers/leds
parentc479ff093328c63ae9e496951a34f7fef550503a (diff)
leds: leds-gpio: Add support for GPIO descriptors
GPIO descriptors are the preferred way over legacy GPIO numbers nowadays. Convert the driver to use GPIO descriptors internally but still allow passing legacy GPIO numbers from platform data to support existing platforms. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Acked-by: Alexandre Courbot <acourbot@nvidia.com> Acked-by: Bryan Wu <cooloney@gmail.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Grant Likely <grant.likely@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-gpio.c80
1 files changed, 45 insertions, 35 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index b4518c8751c8..1ff95ce9487a 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -12,6 +12,7 @@
12 */ 12 */
13#include <linux/err.h> 13#include <linux/err.h>
14#include <linux/gpio.h> 14#include <linux/gpio.h>
15#include <linux/gpio/consumer.h>
15#include <linux/kernel.h> 16#include <linux/kernel.h>
16#include <linux/leds.h> 17#include <linux/leds.h>
17#include <linux/module.h> 18#include <linux/module.h>
@@ -24,11 +25,10 @@
24 25
25struct gpio_led_data { 26struct gpio_led_data {
26 struct led_classdev cdev; 27 struct led_classdev cdev;
27 unsigned gpio; 28 struct gpio_desc *gpiod;
28 struct work_struct work; 29 struct work_struct work;
29 u8 new_level; 30 u8 new_level;
30 u8 can_sleep; 31 u8 can_sleep;
31 u8 active_low;
32 u8 blinking; 32 u8 blinking;
33 int (*platform_gpio_blink_set)(unsigned gpio, int state, 33 int (*platform_gpio_blink_set)(unsigned gpio, int state,
34 unsigned long *delay_on, unsigned long *delay_off); 34 unsigned long *delay_on, unsigned long *delay_off);
@@ -40,12 +40,16 @@ static void gpio_led_work(struct work_struct *work)
40 container_of(work, struct gpio_led_data, work); 40 container_of(work, struct gpio_led_data, work);
41 41
42 if (led_dat->blinking) { 42 if (led_dat->blinking) {
43 led_dat->platform_gpio_blink_set(led_dat->gpio, 43 int gpio = desc_to_gpio(led_dat->gpiod);
44 led_dat->new_level, 44 int level = led_dat->new_level;
45 NULL, NULL); 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);
46 led_dat->blinking = 0; 50 led_dat->blinking = 0;
47 } else 51 } else
48 gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); 52 gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
49} 53}
50 54
51static void gpio_led_set(struct led_classdev *led_cdev, 55static void gpio_led_set(struct led_classdev *led_cdev,
@@ -60,9 +64,6 @@ static void gpio_led_set(struct led_classdev *led_cdev,
60 else 64 else
61 level = 1; 65 level = 1;
62 66
63 if (led_dat->active_low)
64 level = !level;
65
66 /* Setting GPIOs with I2C/etc requires a task context, and we don't 67 /* Setting GPIOs with I2C/etc requires a task context, and we don't
67 * seem to have a reliable way to know if we're already in one; so 68 * seem to have a reliable way to know if we're already in one; so
68 * let's just assume the worst. 69 * let's just assume the worst.
@@ -72,11 +73,16 @@ static void gpio_led_set(struct led_classdev *led_cdev,
72 schedule_work(&led_dat->work); 73 schedule_work(&led_dat->work);
73 } else { 74 } else {
74 if (led_dat->blinking) { 75 if (led_dat->blinking) {
75 led_dat->platform_gpio_blink_set(led_dat->gpio, level, 76 int gpio = desc_to_gpio(led_dat->gpiod);
76 NULL, NULL); 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);
77 led_dat->blinking = 0; 83 led_dat->blinking = 0;
78 } else 84 } else
79 gpio_set_value(led_dat->gpio, level); 85 gpiod_set_value(led_dat->gpiod, level);
80 } 86 }
81} 87}
82 88
@@ -85,9 +91,10 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
85{ 91{
86 struct gpio_led_data *led_dat = 92 struct gpio_led_data *led_dat =
87 container_of(led_cdev, struct gpio_led_data, cdev); 93 container_of(led_cdev, struct gpio_led_data, cdev);
94 int gpio = desc_to_gpio(led_dat->gpiod);
88 95
89 led_dat->blinking = 1; 96 led_dat->blinking = 1;
90 return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, 97 return led_dat->platform_gpio_blink_set(gpio, GPIO_LED_BLINK,
91 delay_on, delay_off); 98 delay_on, delay_off);
92} 99}
93 100
@@ -97,24 +104,33 @@ static int create_gpio_led(const struct gpio_led *template,
97{ 104{
98 int ret, state; 105 int ret, state;
99 106
100 led_dat->gpio = -1; 107 if (!template->gpiod) {
108 unsigned long flags = 0;
101 109
102 /* skip leds that aren't available */ 110 /* skip leds that aren't available */
103 if (!gpio_is_valid(template->gpio)) { 111 if (!gpio_is_valid(template->gpio)) {
104 dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", 112 dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
105 template->gpio, template->name); 113 template->gpio, template->name);
106 return 0; 114 return 0;
107 } 115 }
108 116
109 ret = devm_gpio_request(parent, template->gpio, template->name); 117 if (template->active_low)
110 if (ret < 0) 118 flags |= GPIOF_ACTIVE_LOW;
111 return ret; 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);
128 }
112 129
113 led_dat->cdev.name = template->name; 130 led_dat->cdev.name = template->name;
114 led_dat->cdev.default_trigger = template->default_trigger; 131 led_dat->cdev.default_trigger = template->default_trigger;
115 led_dat->gpio = template->gpio; 132 led_dat->gpiod = template->gpiod;
116 led_dat->can_sleep = gpio_cansleep(template->gpio); 133 led_dat->can_sleep = gpiod_cansleep(template->gpiod);
117 led_dat->active_low = template->active_low;
118 led_dat->blinking = 0; 134 led_dat->blinking = 0;
119 if (blink_set) { 135 if (blink_set) {
120 led_dat->platform_gpio_blink_set = blink_set; 136 led_dat->platform_gpio_blink_set = blink_set;
@@ -122,30 +138,24 @@ static int create_gpio_led(const struct gpio_led *template,
122 } 138 }
123 led_dat->cdev.brightness_set = gpio_led_set; 139 led_dat->cdev.brightness_set = gpio_led_set;
124 if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) 140 if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
125 state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low; 141 state = !!gpiod_get_value_cansleep(led_dat->gpiod);
126 else 142 else
127 state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); 143 state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
128 led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; 144 led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
129 if (!template->retain_state_suspended) 145 if (!template->retain_state_suspended)
130 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 146 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
131 147
132 ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); 148 ret = gpiod_direction_output(led_dat->gpiod, state);
133 if (ret < 0) 149 if (ret < 0)
134 return ret; 150 return ret;
135 151
136 INIT_WORK(&led_dat->work, gpio_led_work); 152 INIT_WORK(&led_dat->work, gpio_led_work);
137 153
138 ret = led_classdev_register(parent, &led_dat->cdev); 154 return led_classdev_register(parent, &led_dat->cdev);
139 if (ret < 0)
140 return ret;
141
142 return 0;
143} 155}
144 156
145static void delete_gpio_led(struct gpio_led_data *led) 157static void delete_gpio_led(struct gpio_led_data *led)
146{ 158{
147 if (!gpio_is_valid(led->gpio))
148 return;
149 led_classdev_unregister(&led->cdev); 159 led_classdev_unregister(&led->cdev);
150 cancel_work_sync(&led->work); 160 cancel_work_sync(&led->work);
151} 161}