Commit 5c51277a authored by Mika Westerberg's avatar Mika Westerberg Committed by Rafael J. Wysocki

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: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Acked-by: default avatarBryan Wu <cooloney@gmail.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Acked-by: default avatarGrant Likely <grant.likely@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent c479ff09
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
*/ */
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -24,11 +25,10 @@ ...@@ -24,11 +25,10 @@
struct gpio_led_data { struct gpio_led_data {
struct led_classdev cdev; struct led_classdev cdev;
unsigned gpio; struct gpio_desc *gpiod;
struct work_struct work; struct work_struct work;
u8 new_level; u8 new_level;
u8 can_sleep; u8 can_sleep;
u8 active_low;
u8 blinking; u8 blinking;
int (*platform_gpio_blink_set)(unsigned gpio, int state, int (*platform_gpio_blink_set)(unsigned gpio, int state,
unsigned long *delay_on, unsigned long *delay_off); unsigned long *delay_on, unsigned long *delay_off);
...@@ -40,12 +40,16 @@ static void gpio_led_work(struct work_struct *work) ...@@ -40,12 +40,16 @@ static void gpio_led_work(struct work_struct *work)
container_of(work, struct gpio_led_data, work); container_of(work, struct gpio_led_data, work);
if (led_dat->blinking) { if (led_dat->blinking) {
led_dat->platform_gpio_blink_set(led_dat->gpio, int gpio = desc_to_gpio(led_dat->gpiod);
led_dat->new_level, int level = led_dat->new_level;
NULL, NULL);
if (gpiod_is_active_low(led_dat->gpiod))
level = !level;
led_dat->platform_gpio_blink_set(gpio, level, NULL, NULL);
led_dat->blinking = 0; led_dat->blinking = 0;
} else } 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, static void gpio_led_set(struct led_classdev *led_cdev,
...@@ -60,9 +64,6 @@ 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 else
level = 1; level = 1;
if (led_dat->active_low)
level = !level;
/* Setting GPIOs with I2C/etc requires a task context, and we don't /* 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 * seem to have a reliable way to know if we're already in one; so
* let's just assume the worst. * let's just assume the worst.
...@@ -72,11 +73,16 @@ static void gpio_led_set(struct led_classdev *led_cdev, ...@@ -72,11 +73,16 @@ static void gpio_led_set(struct led_classdev *led_cdev,
schedule_work(&led_dat->work); schedule_work(&led_dat->work);
} else { } else {
if (led_dat->blinking) { if (led_dat->blinking) {
led_dat->platform_gpio_blink_set(led_dat->gpio, level, int gpio = desc_to_gpio(led_dat->gpiod);
NULL, NULL);
if (gpiod_is_active_low(led_dat->gpiod))
level = !level;
led_dat->platform_gpio_blink_set(gpio, level, NULL,
NULL);
led_dat->blinking = 0; led_dat->blinking = 0;
} else } 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, ...@@ -85,9 +91,10 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
{ {
struct gpio_led_data *led_dat = struct gpio_led_data *led_dat =
container_of(led_cdev, struct gpio_led_data, cdev); container_of(led_cdev, struct gpio_led_data, cdev);
int gpio = desc_to_gpio(led_dat->gpiod);
led_dat->blinking = 1; 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); delay_on, delay_off);
} }
...@@ -97,7 +104,8 @@ static int create_gpio_led(const struct gpio_led *template, ...@@ -97,7 +104,8 @@ static int create_gpio_led(const struct gpio_led *template,
{ {
int ret, state; int ret, state;
led_dat->gpio = -1; if (!template->gpiod) {
unsigned long flags = 0;
/* skip leds that aren't available */ /* skip leds that aren't available */
if (!gpio_is_valid(template->gpio)) { if (!gpio_is_valid(template->gpio)) {
...@@ -106,15 +114,23 @@ static int create_gpio_led(const struct gpio_led *template, ...@@ -106,15 +114,23 @@ static int create_gpio_led(const struct gpio_led *template,
return 0; return 0;
} }
ret = devm_gpio_request(parent, template->gpio, template->name); if (template->active_low)
flags |= GPIOF_ACTIVE_LOW;
ret = devm_gpio_request_one(parent, template->gpio, flags,
template->name);
if (ret < 0) if (ret < 0)
return ret; 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.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger; led_dat->cdev.default_trigger = template->default_trigger;
led_dat->gpio = template->gpio; led_dat->gpiod = template->gpiod;
led_dat->can_sleep = gpio_cansleep(template->gpio); led_dat->can_sleep = gpiod_cansleep(template->gpiod);
led_dat->active_low = template->active_low;
led_dat->blinking = 0; led_dat->blinking = 0;
if (blink_set) { if (blink_set) {
led_dat->platform_gpio_blink_set = blink_set; led_dat->platform_gpio_blink_set = blink_set;
...@@ -122,30 +138,24 @@ static int create_gpio_led(const struct gpio_led *template, ...@@ -122,30 +138,24 @@ static int create_gpio_led(const struct gpio_led *template,
} }
led_dat->cdev.brightness_set = gpio_led_set; led_dat->cdev.brightness_set = gpio_led_set;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) 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 else
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
if (!template->retain_state_suspended) if (!template->retain_state_suspended)
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 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) if (ret < 0)
return ret; return ret;
INIT_WORK(&led_dat->work, gpio_led_work); INIT_WORK(&led_dat->work, gpio_led_work);
ret = led_classdev_register(parent, &led_dat->cdev); return led_classdev_register(parent, &led_dat->cdev);
if (ret < 0)
return ret;
return 0;
} }
static void delete_gpio_led(struct gpio_led_data *led) static void delete_gpio_led(struct gpio_led_data *led)
{ {
if (!gpio_is_valid(led->gpio))
return;
led_classdev_unregister(&led->cdev); led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work); cancel_work_sync(&led->work);
} }
......
...@@ -261,6 +261,7 @@ struct gpio_led { ...@@ -261,6 +261,7 @@ struct gpio_led {
unsigned retain_state_suspended : 1; unsigned retain_state_suspended : 1;
unsigned default_state : 2; unsigned default_state : 2;
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ /* 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_OFF 0
#define LEDS_GPIO_DEFSTATE_ON 1 #define LEDS_GPIO_DEFSTATE_ON 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment