diff options
Diffstat (limited to 'drivers/leds/leds-gpio.c')
| -rw-r--r-- | drivers/leds/leds-gpio.c | 148 | 
1 files changed, 76 insertions, 72 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b4518c8751c8..5fb4440127d9 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -12,25 +12,23 @@   */  #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> -#include <linux/of.h> -#include <linux/of_gpio.h> -#include <linux/of_platform.h>  #include <linux/platform_device.h> +#include <linux/property.h>  #include <linux/slab.h>  #include <linux/workqueue.h>  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, +	int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state,  			unsigned long *delay_on, unsigned long *delay_off);  }; @@ -40,12 +38,11 @@ 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); +		led_dat->platform_gpio_blink_set(led_dat->gpiod, +					led_dat->new_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 +57,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 +66,11 @@ 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, +			led_dat->platform_gpio_blink_set(led_dat->gpiod, level,  							 NULL, NULL);  			led_dat->blinking = 0;  		} else -			gpio_set_value(led_dat->gpio, level); +			gpiod_set_value(led_dat->gpiod, level);  	}  } @@ -87,34 +81,49 @@ static int gpio_blink_set(struct led_classdev *led_cdev,  		container_of(led_cdev, struct gpio_led_data, cdev);  	led_dat->blinking = 1; -	return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, +	return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK,  						delay_on, delay_off);  }  static int create_gpio_led(const struct gpio_led *template,  	struct gpio_led_data *led_dat, struct device *parent, -	int (*blink_set)(unsigned, int, unsigned long *, unsigned long *)) +	int (*blink_set)(struct gpio_desc *, int, unsigned long *, +			 unsigned long *))  {  	int ret, state; -	led_dat->gpio = -1; +	led_dat->gpiod = template->gpiod; +	if (!led_dat->gpiod) { +		/* +		 * This is the legacy code path for platform code that +		 * still uses GPIO numbers. Ultimately we would like to get +		 * rid of this block completely. +		 */ +		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->can_sleep = gpiod_cansleep(led_dat->gpiod);  	led_dat->blinking = 0;  	if (blink_set) {  		led_dat->platform_gpio_blink_set = blink_set; @@ -122,30 +131,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);  } @@ -161,40 +164,47 @@ static inline int sizeof_gpio_leds_priv(int num_leds)  		(sizeof(struct gpio_led_data) * num_leds);  } -/* Code to create from OpenFirmware platform devices */ -#ifdef CONFIG_OF_GPIO -static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) +static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)  { -	struct device_node *np = pdev->dev.of_node, *child; +	struct device *dev = &pdev->dev; +	struct fwnode_handle *child;  	struct gpio_leds_priv *priv;  	int count, ret; +	struct device_node *np; -	/* count LEDs in this device, so we know how much to allocate */ -	count = of_get_available_child_count(np); +	count = device_get_child_node_count(dev);  	if (!count)  		return ERR_PTR(-ENODEV); -	for_each_available_child_of_node(np, child) -		if (of_get_gpio(child, 0) == -EPROBE_DEFER) -			return ERR_PTR(-EPROBE_DEFER); - -	priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count), -			GFP_KERNEL); +	priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);  	if (!priv)  		return ERR_PTR(-ENOMEM); -	for_each_available_child_of_node(np, child) { +	device_for_each_child_node(dev, child) {  		struct gpio_led led = {}; -		enum of_gpio_flags flags; -		const char *state; +		const char *state = NULL; + +		led.gpiod = devm_get_gpiod_from_child(dev, child); +		if (IS_ERR(led.gpiod)) { +			fwnode_handle_put(child); +			goto err; +		} + +		np = of_node(child); -		led.gpio = of_get_gpio_flags(child, 0, &flags); -		led.active_low = flags & OF_GPIO_ACTIVE_LOW; -		led.name = of_get_property(child, "label", NULL) ? : child->name; -		led.default_trigger = -			of_get_property(child, "linux,default-trigger", NULL); -		state = of_get_property(child, "default-state", NULL); -		if (state) { +		if (fwnode_property_present(child, "label")) { +			fwnode_property_read_string(child, "label", &led.name); +		} else { +			if (IS_ENABLED(CONFIG_OF) && !led.name && np) +				led.name = np->name; +			if (!led.name) +				return ERR_PTR(-EINVAL); +		} +		fwnode_property_read_string(child, "linux,default-trigger", +					    &led.default_trigger); + +		if (!fwnode_property_read_string(child, "default-state", +						 &state)) {  			if (!strcmp(state, "keep"))  				led.default_state = LEDS_GPIO_DEFSTATE_KEEP;  			else if (!strcmp(state, "on")) @@ -203,13 +213,13 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)  				led.default_state = LEDS_GPIO_DEFSTATE_OFF;  		} -		if (of_get_property(child, "retain-state-suspended", NULL)) +		if (fwnode_property_present(child, "retain-state-suspended"))  			led.retain_state_suspended = 1;  		ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], -				      &pdev->dev, NULL); +				      dev, NULL);  		if (ret < 0) { -			of_node_put(child); +			fwnode_handle_put(child);  			goto err;  		}  	} @@ -228,12 +238,6 @@ static const struct of_device_id of_gpio_leds_match[] = {  };  MODULE_DEVICE_TABLE(of, of_gpio_leds_match); -#else /* CONFIG_OF_GPIO */ -static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) -{ -	return ERR_PTR(-ENODEV); -} -#endif /* CONFIG_OF_GPIO */  static int gpio_led_probe(struct platform_device *pdev)  { @@ -261,7 +265,7 @@ static int gpio_led_probe(struct platform_device *pdev)  			}  		}  	} else { -		priv = gpio_leds_create_of(pdev); +		priv = gpio_leds_create(pdev);  		if (IS_ERR(priv))  			return PTR_ERR(priv);  	} @@ -288,7 +292,7 @@ static struct platform_driver gpio_led_driver = {  	.driver		= {  		.name	= "leds-gpio",  		.owner	= THIS_MODULE, -		.of_match_table = of_match_ptr(of_gpio_leds_match), +		.of_match_table = of_gpio_leds_match,  	},  };  | 
