[PATCH v2 3/3] leds: leds-pwm: Defer led_pwm_set() if PWM can sleep
Peter Ujfalusi
peter.ujfalusi at ti.com
Fri Jan 25 08:52:10 EST 2013
On 01/25/2013 02:44 PM, Florian Vaussard wrote:
> Call to led_pwm_set() can happen inside atomic context, like triggers.
> If the PWM call can sleep, defer using a worker.
>
> Signed-off-by: Florian Vaussard <florian.vaussard at epfl.ch>
Reviewed-by: Peter Ujfalusi <peter.ujfalusi at ti.com>
> ---
> drivers/leds/leds-pwm.c | 50 +++++++++++++++++++++++++++++++++++++++-------
> 1 files changed, 42 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
> index a1ea5f6..70effa7 100644
> --- a/drivers/leds/leds-pwm.c
> +++ b/drivers/leds/leds-pwm.c
> @@ -23,12 +23,16 @@
> #include <linux/pwm.h>
> #include <linux/leds_pwm.h>
> #include <linux/slab.h>
> +#include <linux/workqueue.h>
>
> struct led_pwm_data {
> struct led_classdev cdev;
> struct pwm_device *pwm;
> + struct work_struct work;
> unsigned int active_low;
> unsigned int period;
> + int duty;
> + unsigned can_sleep:1;
> };
>
> struct led_pwm_priv {
> @@ -36,6 +40,26 @@ struct led_pwm_priv {
> struct led_pwm_data leds[0];
> };
>
> +static void __led_pwm_set(struct led_pwm_data *led_dat)
> +{
> + int new_duty = led_dat->duty;
> +
> + pwm_config(led_dat->pwm, new_duty, led_dat->period);
> +
> + if (new_duty == 0)
> + pwm_disable(led_dat->pwm);
> + else
> + pwm_enable(led_dat->pwm);
> +}
> +
> +static void led_pwm_work(struct work_struct *work)
> +{
> + struct led_pwm_data *led_dat =
> + container_of(work, struct led_pwm_data, work);
> +
> + __led_pwm_set(led_dat);
> +}
> +
> static void led_pwm_set(struct led_classdev *led_cdev,
> enum led_brightness brightness)
> {
> @@ -44,13 +68,12 @@ static void led_pwm_set(struct led_classdev *led_cdev,
> unsigned int max = led_dat->cdev.max_brightness;
> unsigned int period = led_dat->period;
>
> - if (brightness == 0) {
> - pwm_config(led_dat->pwm, 0, period);
> - pwm_disable(led_dat->pwm);
> - } else {
> - pwm_config(led_dat->pwm, brightness * period / max, period);
> - pwm_enable(led_dat->pwm);
> - }
> + led_dat->duty = brightness * period / max;
> +
> + if (led_dat->can_sleep)
> + schedule_work(&led_dat->work);
> + else
> + __led_pwm_set(led_dat);
> }
>
> static inline size_t sizeof_pwm_leds_priv(int num_leds)
> @@ -100,6 +123,10 @@ static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev)
> led_dat->cdev.brightness = LED_OFF;
> led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
>
> + led_dat->can_sleep = pwm_cansleep(led_dat->pwm);
> + if (led_dat->can_sleep)
> + INIT_WORK(&led_dat->work, led_pwm_work);
> +
> ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
> if (ret < 0) {
> dev_err(&pdev->dev, "failed to register for %s\n",
> @@ -153,6 +180,10 @@ static int led_pwm_probe(struct platform_device *pdev)
> led_dat->cdev.max_brightness = cur_led->max_brightness;
> led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
>
> + led_dat->can_sleep = pwm_cansleep(led_dat->pwm);
> + if (led_dat->can_sleep)
> + INIT_WORK(&led_dat->work, led_pwm_work);
> +
> ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
> if (ret < 0)
> goto err;
> @@ -180,8 +211,11 @@ static int led_pwm_remove(struct platform_device *pdev)
> struct led_pwm_priv *priv = platform_get_drvdata(pdev);
> int i;
>
> - for (i = 0; i < priv->num_leds; i++)
> + for (i = 0; i < priv->num_leds; i++) {
> led_classdev_unregister(&priv->leds[i].cdev);
> + if (priv->leds[i].can_sleep)
> + cancel_work_sync(&priv->leds[i].work);
> + }
>
> return 0;
> }
>
--
Péter
More information about the linux-arm-kernel
mailing list