aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc/pwm-beeper.c
diff options
context:
space:
mode:
authorManfred Schlaegl <manfred.schlaegl@gmx.at>2016-05-27 19:36:36 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2016-05-27 19:40:30 -0400
commitf49cf3b8b4c841457244c461c66186a719e13bcc (patch)
tree11485c8b7383180ea9a68990dd58e79c5ffd88ab /drivers/input/misc/pwm-beeper.c
parent6f49a398b266d4895bd7e041db77a2b2ee1482a6 (diff)
Input: pwm-beeper - fix - scheduling while atomic
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@gmx.at> Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/misc/pwm-beeper.c')
-rw-r--r--drivers/input/misc/pwm-beeper.c69
1 files changed, 48 insertions, 21 deletions
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index f2261ab54701..18663d4edae5 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -20,21 +20,40 @@
20#include <linux/platform_device.h> 20#include <linux/platform_device.h>
21#include <linux/pwm.h> 21#include <linux/pwm.h>
22#include <linux/slab.h> 22#include <linux/slab.h>
23#include <linux/workqueue.h>
23 24
24struct pwm_beeper { 25struct pwm_beeper {
25 struct input_dev *input; 26 struct input_dev *input;
26 struct pwm_device *pwm; 27 struct pwm_device *pwm;
28 struct work_struct work;
27 unsigned long period; 29 unsigned long period;
28}; 30};
29 31
30#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) 32#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
31 33
34static void __pwm_beeper_set(struct pwm_beeper *beeper)
35{
36 unsigned long period = beeper->period;
37
38 if (period) {
39 pwm_config(beeper->pwm, period / 2, period);
40 pwm_enable(beeper->pwm);
41 } else
42 pwm_disable(beeper->pwm);
43}
44
45static void pwm_beeper_work(struct work_struct *work)
46{
47 struct pwm_beeper *beeper =
48 container_of(work, struct pwm_beeper, work);
49
50 __pwm_beeper_set(beeper);
51}
52
32static int pwm_beeper_event(struct input_dev *input, 53static int pwm_beeper_event(struct input_dev *input,
33 unsigned int type, unsigned int code, int value) 54 unsigned int type, unsigned int code, int value)
34{ 55{
35 int ret = 0;
36 struct pwm_beeper *beeper = input_get_drvdata(input); 56 struct pwm_beeper *beeper = input_get_drvdata(input);
37 unsigned long period;
38 57
39 if (type != EV_SND || value < 0) 58 if (type != EV_SND || value < 0)
40 return -EINVAL; 59 return -EINVAL;
@@ -49,22 +68,31 @@ static int pwm_beeper_event(struct input_dev *input,
49 return -EINVAL; 68 return -EINVAL;
50 } 69 }
51 70
52 if (value == 0) { 71 if (value == 0)
53 pwm_disable(beeper->pwm); 72 beeper->period = 0;
54 } else { 73 else
55 period = HZ_TO_NANOSECONDS(value); 74 beeper->period = HZ_TO_NANOSECONDS(value);
56 ret = pwm_config(beeper->pwm, period / 2, period); 75
57 if (ret) 76 schedule_work(&beeper->work);
58 return ret;
59 ret = pwm_enable(beeper->pwm);
60 if (ret)
61 return ret;
62 beeper->period = period;
63 }
64 77
65 return 0; 78 return 0;
66} 79}
67 80
81static void pwm_beeper_stop(struct pwm_beeper *beeper)
82{
83 cancel_work_sync(&beeper->work);
84
85 if (beeper->period)
86 pwm_disable(beeper->pwm);
87}
88
89static void pwm_beeper_close(struct input_dev *input)
90{
91 struct pwm_beeper *beeper = input_get_drvdata(input);
92
93 pwm_beeper_stop(beeper);
94}
95
68static int pwm_beeper_probe(struct platform_device *pdev) 96static int pwm_beeper_probe(struct platform_device *pdev)
69{ 97{
70 unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); 98 unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev);
@@ -87,6 +115,8 @@ static int pwm_beeper_probe(struct platform_device *pdev)
87 goto err_free; 115 goto err_free;
88 } 116 }
89 117
118 INIT_WORK(&beeper->work, pwm_beeper_work);
119
90 beeper->input = input_allocate_device(); 120 beeper->input = input_allocate_device();
91 if (!beeper->input) { 121 if (!beeper->input) {
92 dev_err(&pdev->dev, "Failed to allocate input device\n"); 122 dev_err(&pdev->dev, "Failed to allocate input device\n");
@@ -106,6 +136,7 @@ static int pwm_beeper_probe(struct platform_device *pdev)
106 beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); 136 beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL);
107 137
108 beeper->input->event = pwm_beeper_event; 138 beeper->input->event = pwm_beeper_event;
139 beeper->input->close = pwm_beeper_close;
109 140
110 input_set_drvdata(beeper->input, beeper); 141 input_set_drvdata(beeper->input, beeper);
111 142
@@ -135,7 +166,6 @@ static int pwm_beeper_remove(struct platform_device *pdev)
135 166
136 input_unregister_device(beeper->input); 167 input_unregister_device(beeper->input);
137 168
138 pwm_disable(beeper->pwm);
139 pwm_free(beeper->pwm); 169 pwm_free(beeper->pwm);
140 170
141 kfree(beeper); 171 kfree(beeper);
@@ -147,8 +177,7 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev)
147{ 177{
148 struct pwm_beeper *beeper = dev_get_drvdata(dev); 178 struct pwm_beeper *beeper = dev_get_drvdata(dev);
149 179
150 if (beeper->period) 180 pwm_beeper_stop(beeper);
151 pwm_disable(beeper->pwm);
152 181
153 return 0; 182 return 0;
154} 183}
@@ -157,10 +186,8 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
157{ 186{
158 struct pwm_beeper *beeper = dev_get_drvdata(dev); 187 struct pwm_beeper *beeper = dev_get_drvdata(dev);
159 188
160 if (beeper->period) { 189 if (beeper->period)
161 pwm_config(beeper->pwm, beeper->period / 2, beeper->period); 190 __pwm_beeper_set(beeper);
162 pwm_enable(beeper->pwm);
163 }
164 191
165 return 0; 192 return 0;
166} 193}