On Fri, May 27, 2016 at 11:14:27AM +0200, Manfred Schlaegl wrote: > Pwm config may sleep so defer it using a worker. > > On a Freescale i.MX53 based board we ran into "BUG: scheduling while > atomic" because input_inject_event locks interrupts, but > imx_pwm_config_v2 sleeps. > > Tested on Freescale i.MX53 SoC with 4.6.0. > > Signed-off-by: Manfred Schlaegl <manfred.schlaegl@xxxxxx> Applied, thank you. > --- > drivers/input/misc/pwm-beeper.c | 69 ++++++++++++++++++++++++++++------------- > 1 file changed, 48 insertions(+), 21 deletions(-) > > diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c > index 8d71332..5f9655d 100644 > --- a/drivers/input/misc/pwm-beeper.c > +++ b/drivers/input/misc/pwm-beeper.c > @@ -20,21 +20,40 @@ > #include <linux/platform_device.h> > #include <linux/pwm.h> > #include <linux/slab.h> > +#include <linux/workqueue.h> > > struct pwm_beeper { > struct input_dev *input; > struct pwm_device *pwm; > + struct work_struct work; > unsigned long period; > }; > > #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) > > +static void __pwm_beeper_set(struct pwm_beeper *beeper) > +{ > + unsigned long period = beeper->period; > + > + if (period) { > + pwm_config(beeper->pwm, period / 2, period); > + pwm_enable(beeper->pwm); > + } else > + pwm_disable(beeper->pwm); > +} > + > +static void pwm_beeper_work(struct work_struct *work) > +{ > + struct pwm_beeper *beeper = > + container_of(work, struct pwm_beeper, work); > + > + __pwm_beeper_set(beeper); > +} > + > static int pwm_beeper_event(struct input_dev *input, > unsigned int type, unsigned int code, int value) > { > - int ret = 0; > struct pwm_beeper *beeper = input_get_drvdata(input); > - unsigned long period; > > if (type != EV_SND || value < 0) > return -EINVAL; > @@ -49,22 +68,31 @@ static int pwm_beeper_event(struct input_dev *input, > return -EINVAL; > } > > - if (value == 0) { > - pwm_disable(beeper->pwm); > - } else { > - period = HZ_TO_NANOSECONDS(value); > - ret = pwm_config(beeper->pwm, period / 2, period); > - if (ret) > - return ret; > - ret = pwm_enable(beeper->pwm); > - if (ret) > - return ret; > - beeper->period = period; > - } > + if (value == 0) > + beeper->period = 0; > + else > + beeper->period = HZ_TO_NANOSECONDS(value); > + > + schedule_work(&beeper->work); > > return 0; > } > > +static void pwm_beeper_stop(struct pwm_beeper *beeper) > +{ > + cancel_work_sync(&beeper->work); > + > + if (beeper->period) > + pwm_disable(beeper->pwm); > +} > + > +static void pwm_beeper_close(struct input_dev *input) > +{ > + struct pwm_beeper *beeper = input_get_drvdata(input); > + > + pwm_beeper_stop(beeper); > +} > + > static int pwm_beeper_probe(struct platform_device *pdev) > { > unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); > @@ -93,6 +121,8 @@ static int pwm_beeper_probe(struct platform_device *pdev) > */ > pwm_apply_args(beeper->pwm); > > + INIT_WORK(&beeper->work, pwm_beeper_work); > + > beeper->input = input_allocate_device(); > if (!beeper->input) { > dev_err(&pdev->dev, "Failed to allocate input device\n"); > @@ -112,6 +142,7 @@ static int pwm_beeper_probe(struct platform_device *pdev) > beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); > > beeper->input->event = pwm_beeper_event; > + beeper->input->close = pwm_beeper_close; > > input_set_drvdata(beeper->input, beeper); > > @@ -141,7 +172,6 @@ static int pwm_beeper_remove(struct platform_device *pdev) > > input_unregister_device(beeper->input); > > - pwm_disable(beeper->pwm); > pwm_free(beeper->pwm); > > kfree(beeper); > @@ -153,8 +183,7 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev) > { > struct pwm_beeper *beeper = dev_get_drvdata(dev); > > - if (beeper->period) > - pwm_disable(beeper->pwm); > + pwm_beeper_stop(beeper); > > return 0; > } > @@ -163,10 +192,8 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) > { > struct pwm_beeper *beeper = dev_get_drvdata(dev); > > - if (beeper->period) { > - pwm_config(beeper->pwm, beeper->period / 2, beeper->period); > - pwm_enable(beeper->pwm); > - } > + if (beeper->period) > + __pwm_beeper_set(beeper); > > return 0; > } > -- > 2.1.4 > -- Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html