|
@@ -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 @@
|
|
|
|
|
|
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)
|
|
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,
|
|
|
|
- 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;
|
|
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,
|
|
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,
|
|
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,
|
|
|
|
- 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;
|
|
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,
|
|
{
|
|
{
|
|
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,24 +104,33 @@ 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 */
|
|
|
|
- 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.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->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;
|
|
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,
|
|
}
|
|
}
|
|
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);
|
|
|
|
- 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)
|
|
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);
|
|
}
|
|
}
|