summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/leds/leds-gpio.c80
-rw-r--r--include/linux/leds.h1
2 files changed, 46 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 @@
*/
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
@@ -24,11 +25,10 @@
struct gpio_led_data {
struct led_classdev cdev;
- unsigned gpio;
+ struct gpio_desc *gpiod;
struct work_struct work;
u8 new_level;
u8 can_sleep;
- u8 active_low;
u8 blinking;
int (*platform_gpio_blink_set)(unsigned gpio, int state,
unsigned long *delay_on, unsigned long *delay_off);
@@ -40,12 +40,16 @@ static void gpio_led_work(struct work_struct *work)
container_of(work, struct gpio_led_data, work);
if (led_dat->blinking) {
- led_dat->platform_gpio_blink_set(led_dat->gpio,
- led_dat->new_level,
- NULL, NULL);
+ int gpio = desc_to_gpio(led_dat->gpiod);
+ int level = led_dat->new_level;
+
+ if (gpiod_is_active_low(led_dat->gpiod))
+ level = !level;
+
+ led_dat->platform_gpio_blink_set(gpio, level, NULL, NULL);
led_dat->blinking = 0;
} else
- gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
+ gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
}
static void gpio_led_set(struct led_classdev *led_cdev,
@@ -60,9 +64,6 @@ static void gpio_led_set(struct led_classdev *led_cdev,
else
level = 1;
- if (led_dat->active_low)
- level = !level;
-
/* Setting GPIOs with I2C/etc requires a task context, and we don't
* seem to have a reliable way to know if we're already in one; so
* let's just assume the worst.
@@ -72,11 +73,16 @@ static void gpio_led_set(struct led_classdev *led_cdev,
schedule_work(&led_dat->work);
} else {
if (led_dat->blinking) {
- led_dat->platform_gpio_blink_set(led_dat->gpio, level,
- NULL, NULL);
+ int gpio = desc_to_gpio(led_dat->gpiod);
+
+ if (gpiod_is_active_low(led_dat->gpiod))
+ level = !level;
+
+ led_dat->platform_gpio_blink_set(gpio, level, NULL,
+ NULL);
led_dat->blinking = 0;
} else
- gpio_set_value(led_dat->gpio, level);
+ gpiod_set_value(led_dat->gpiod, level);
}
}
@@ -85,9 +91,10 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
{
struct gpio_led_data *led_dat =
container_of(led_cdev, struct gpio_led_data, cdev);
+ int gpio = desc_to_gpio(led_dat->gpiod);
led_dat->blinking = 1;
- return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK,
+ return led_dat->platform_gpio_blink_set(gpio, GPIO_LED_BLINK,
delay_on, delay_off);
}
@@ -97,24 +104,33 @@ static int create_gpio_led(const struct gpio_led *template,
{
int ret, state;
- led_dat->gpio = -1;
+ if (!template->gpiod) {
+ unsigned long flags = 0;
- /* skip leds that aren't available */
- if (!gpio_is_valid(template->gpio)) {
- dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
- template->gpio, template->name);
- return 0;
- }
+ /* skip leds that aren't available */
+ if (!gpio_is_valid(template->gpio)) {
+ dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
+ template->gpio, template->name);
+ return 0;
+ }
- ret = devm_gpio_request(parent, template->gpio, template->name);
- if (ret < 0)
- return ret;
+ if (template->active_low)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ ret = devm_gpio_request_one(parent, template->gpio, flags,
+ template->name);
+ if (ret < 0)
+ return ret;
+
+ led_dat->gpiod = gpio_to_desc(template->gpio);
+ if (IS_ERR(led_dat->gpiod))
+ return PTR_ERR(led_dat->gpiod);
+ }
led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger;
- led_dat->gpio = template->gpio;
- led_dat->can_sleep = gpio_cansleep(template->gpio);
- led_dat->active_low = template->active_low;
+ led_dat->gpiod = template->gpiod;
+ led_dat->can_sleep = gpiod_cansleep(template->gpiod);
led_dat->blinking = 0;
if (blink_set) {
led_dat->platform_gpio_blink_set = blink_set;
@@ -122,30 +138,24 @@ static int create_gpio_led(const struct gpio_led *template,
}
led_dat->cdev.brightness_set = gpio_led_set;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
- state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low;
+ state = !!gpiod_get_value_cansleep(led_dat->gpiod);
else
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
if (!template->retain_state_suspended)
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
- ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
+ ret = gpiod_direction_output(led_dat->gpiod, state);
if (ret < 0)
return ret;
INIT_WORK(&led_dat->work, gpio_led_work);
- ret = led_classdev_register(parent, &led_dat->cdev);
- if (ret < 0)
- return ret;
-
- return 0;
+ return led_classdev_register(parent, &led_dat->cdev);
}
static void delete_gpio_led(struct gpio_led_data *led)
{
- if (!gpio_is_valid(led->gpio))
- return;
led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
}
diff --git a/include/linux/leds.h b/include/linux/leds.h
index a57611d0c94e..f3af5c4d9084 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -261,6 +261,7 @@ struct gpio_led {
unsigned retain_state_suspended : 1;
unsigned default_state : 2;
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
+ struct gpio_desc *gpiod;
};
#define LEDS_GPIO_DEFSTATE_OFF 0
#define LEDS_GPIO_DEFSTATE_ON 1