aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard
diff options
context:
space:
mode:
authorAndrew Bresticker <abrestic@chromium.org>2014-06-18 14:14:07 -0400
committerLee Jones <lee.jones@linaro.org>2014-07-09 09:58:20 -0400
commitd1fd345e2087f0362c92bd3b0a1cea7fe636ac3a (patch)
tree38c93059a128db984ff4d95ee2c2f35f02275fa6 /drivers/input/keyboard
parent12ebc8a50bc54e3a6fe207861fc6793181f9c2dc (diff)
mfd: cros_ec: Move EC interrupt to cros_ec_keyb
If we receive EC interrupts after the cros_ec driver has probed, but before the cros_ec_keyb driver has probed, the cros_ec IRQ handler will not run the cros_ec_keyb notifier and the EC will leave the IRQ line asserted. The cros_ec IRQ handler then returns IRQ_HANDLED and the resulting flood of interrupts causes the machine to hang. Since the EC interrupt is currently only used for the keyboard, move the setup and handling of the EC interrupt to the cros_ec_keyb driver. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Signed-off-by: Doug Anderson <dianders@chromium.org> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c58
1 files changed, 33 insertions, 25 deletions
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index b8341ab99f55..791781ade4e7 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -24,8 +24,8 @@
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/i2c.h> 25#include <linux/i2c.h>
26#include <linux/input.h> 26#include <linux/input.h>
27#include <linux/interrupt.h>
27#include <linux/kernel.h> 28#include <linux/kernel.h>
28#include <linux/notifier.h>
29#include <linux/platform_device.h> 29#include <linux/platform_device.h>
30#include <linux/slab.h> 30#include <linux/slab.h>
31#include <linux/input/matrix_keypad.h> 31#include <linux/input/matrix_keypad.h>
@@ -42,7 +42,6 @@
42 * @dev: Device pointer 42 * @dev: Device pointer
43 * @idev: Input device 43 * @idev: Input device
44 * @ec: Top level ChromeOS device to use to talk to EC 44 * @ec: Top level ChromeOS device to use to talk to EC
45 * @event_notifier: interrupt event notifier for transport devices
46 */ 45 */
47struct cros_ec_keyb { 46struct cros_ec_keyb {
48 unsigned int rows; 47 unsigned int rows;
@@ -55,7 +54,6 @@ struct cros_ec_keyb {
55 struct device *dev; 54 struct device *dev;
56 struct input_dev *idev; 55 struct input_dev *idev;
57 struct cros_ec_device *ec; 56 struct cros_ec_device *ec;
58 struct notifier_block notifier;
59}; 57};
60 58
61 59
@@ -173,22 +171,6 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
173 input_sync(ckdev->idev); 171 input_sync(ckdev->idev);
174} 172}
175 173
176static int cros_ec_keyb_open(struct input_dev *dev)
177{
178 struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
179
180 return blocking_notifier_chain_register(&ckdev->ec->event_notifier,
181 &ckdev->notifier);
182}
183
184static void cros_ec_keyb_close(struct input_dev *dev)
185{
186 struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
187
188 blocking_notifier_chain_unregister(&ckdev->ec->event_notifier,
189 &ckdev->notifier);
190}
191
192static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) 174static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
193{ 175{
194 struct cros_ec_command msg = { 176 struct cros_ec_command msg = {
@@ -203,19 +185,41 @@ static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
203 return ckdev->ec->cmd_xfer(ckdev->ec, &msg); 185 return ckdev->ec->cmd_xfer(ckdev->ec, &msg);
204} 186}
205 187
206static int cros_ec_keyb_work(struct notifier_block *nb, 188static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
207 unsigned long state, void *_notify)
208{ 189{
190 struct cros_ec_keyb *ckdev = data;
191 struct cros_ec_device *ec = ckdev->ec;
209 int ret; 192 int ret;
210 struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb,
211 notifier);
212 uint8_t kb_state[ckdev->cols]; 193 uint8_t kb_state[ckdev->cols];
213 194
195 if (device_may_wakeup(ec->dev))
196 pm_wakeup_event(ec->dev, 0);
197
214 ret = cros_ec_keyb_get_state(ckdev, kb_state); 198 ret = cros_ec_keyb_get_state(ckdev, kb_state);
215 if (ret >= 0) 199 if (ret >= 0)
216 cros_ec_keyb_process(ckdev, kb_state, ret); 200 cros_ec_keyb_process(ckdev, kb_state, ret);
201 else
202 dev_err(ec->dev, "failed to get keyboard state: %d\n", ret);
217 203
218 return NOTIFY_DONE; 204 return IRQ_HANDLED;
205}
206
207static int cros_ec_keyb_open(struct input_dev *dev)
208{
209 struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
210 struct cros_ec_device *ec = ckdev->ec;
211
212 return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq,
213 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
214 "cros_ec_keyb", ckdev);
215}
216
217static void cros_ec_keyb_close(struct input_dev *dev)
218{
219 struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
220 struct cros_ec_device *ec = ckdev->ec;
221
222 free_irq(ec->irq, ckdev);
219} 223}
220 224
221static int cros_ec_keyb_probe(struct platform_device *pdev) 225static int cros_ec_keyb_probe(struct platform_device *pdev)
@@ -246,8 +250,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
246 if (!idev) 250 if (!idev)
247 return -ENOMEM; 251 return -ENOMEM;
248 252
253 if (!ec->irq) {
254 dev_err(dev, "no EC IRQ specified\n");
255 return -EINVAL;
256 }
257
249 ckdev->ec = ec; 258 ckdev->ec = ec;
250 ckdev->notifier.notifier_call = cros_ec_keyb_work;
251 ckdev->dev = dev; 259 ckdev->dev = dev;
252 dev_set_drvdata(&pdev->dev, ckdev); 260 dev_set_drvdata(&pdev->dev, ckdev);
253 261