Message ID | 1359121471-21457-4-git-send-email-florian.vaussard@epfl.ch (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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@epfl.ch> Reviewed-by: Peter Ujfalusi <peter.ujfalusi@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; > } >
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; }
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@epfl.ch> --- drivers/leds/leds-pwm.c | 50 +++++++++++++++++++++++++++++++++++++++------- 1 files changed, 42 insertions(+), 8 deletions(-)