diff options
-rw-r--r-- | drivers/leds/leds-gpio.c | 80 | ||||
-rw-r--r-- | include/linux/leds.h | 1 |
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 |