Skip to content

Commit

Permalink
leds: leds-gpio: Add support for GPIO descriptors
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mika Westerberg authored and Rafael J. Wysocki committed Nov 4, 2014
1 parent c479ff0 commit 5c51277
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 35 deletions.
80 changes: 45 additions & 35 deletions drivers/leds/leds-gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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);
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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);
}
}

Expand All @@ -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);
}

Expand All @@ -97,55 +104,58 @@ 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;
led_dat->cdev.blink_set = gpio_blink_set;
}
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);
}
Expand Down
1 change: 1 addition & 0 deletions include/linux/leds.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5c51277

Please sign in to comment.