aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c58
-rw-r--r--drivers/mfd/cros_ec.c35
-rw-r--r--include/linux/mfd/cros_ec.h2
3 files changed, 34 insertions, 61 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
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index 83e30c663578..4873f9c50452 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -62,18 +62,6 @@ int cros_ec_check_result(struct cros_ec_device *ec_dev,
62} 62}
63EXPORT_SYMBOL(cros_ec_check_result); 63EXPORT_SYMBOL(cros_ec_check_result);
64 64
65static irqreturn_t ec_irq_thread(int irq, void *data)
66{
67 struct cros_ec_device *ec_dev = data;
68
69 if (device_may_wakeup(ec_dev->dev))
70 pm_wakeup_event(ec_dev->dev, 0);
71
72 blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev);
73
74 return IRQ_HANDLED;
75}
76
77static const struct mfd_cell cros_devs[] = { 65static const struct mfd_cell cros_devs[] = {
78 { 66 {
79 .name = "cros-ec-keyb", 67 .name = "cros-ec-keyb",
@@ -92,8 +80,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
92 struct device *dev = ec_dev->dev; 80 struct device *dev = ec_dev->dev;
93 int err = 0; 81 int err = 0;
94 82
95 BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
96
97 if (ec_dev->din_size) { 83 if (ec_dev->din_size) {
98 ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 84 ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
99 if (!ec_dev->din) 85 if (!ec_dev->din)
@@ -105,42 +91,23 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
105 return -ENOMEM; 91 return -ENOMEM;
106 } 92 }
107 93
108 if (!ec_dev->irq) {
109 dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq);
110 return err;
111 }
112
113 err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
114 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
115 "chromeos-ec", ec_dev);
116 if (err) {
117 dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err);
118 return err;
119 }
120
121 err = mfd_add_devices(dev, 0, cros_devs, 94 err = mfd_add_devices(dev, 0, cros_devs,
122 ARRAY_SIZE(cros_devs), 95 ARRAY_SIZE(cros_devs),
123 NULL, ec_dev->irq, NULL); 96 NULL, ec_dev->irq, NULL);
124 if (err) { 97 if (err) {
125 dev_err(dev, "failed to add mfd devices\n"); 98 dev_err(dev, "failed to add mfd devices\n");
126 goto fail_mfd; 99 return err;
127 } 100 }
128 101
129 dev_info(dev, "Chrome EC device registered\n"); 102 dev_info(dev, "Chrome EC device registered\n");
130 103
131 return 0; 104 return 0;
132
133fail_mfd:
134 free_irq(ec_dev->irq, ec_dev);
135
136 return err;
137} 105}
138EXPORT_SYMBOL(cros_ec_register); 106EXPORT_SYMBOL(cros_ec_register);
139 107
140int cros_ec_remove(struct cros_ec_device *ec_dev) 108int cros_ec_remove(struct cros_ec_device *ec_dev)
141{ 109{
142 mfd_remove_devices(ec_dev->dev); 110 mfd_remove_devices(ec_dev->dev);
143 free_irq(ec_dev->irq, ec_dev);
144 111
145 return 0; 112 return 0;
146} 113}
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index 0ebf26fddbbb..fcbe9d129a9d 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -62,7 +62,6 @@ struct cros_ec_command {
62 * @dev: Device pointer 62 * @dev: Device pointer
63 * @was_wake_device: true if this device was set to wake the system from 63 * @was_wake_device: true if this device was set to wake the system from
64 * sleep at the last suspend 64 * sleep at the last suspend
65 * @event_notifier: interrupt event notifier for transport devices
66 * @cmd_xfer: send command to EC and get response 65 * @cmd_xfer: send command to EC and get response
67 * Returns the number of bytes received if the communication succeeded, but 66 * Returns the number of bytes received if the communication succeeded, but
68 * that doesn't mean the EC was happy with the command. The caller 67 * that doesn't mean the EC was happy with the command. The caller
@@ -93,7 +92,6 @@ struct cros_ec_device {
93 struct device *dev; 92 struct device *dev;
94 bool was_wake_device; 93 bool was_wake_device;
95 struct class *cros_class; 94 struct class *cros_class;
96 struct blocking_notifier_head event_notifier;
97 int (*cmd_xfer)(struct cros_ec_device *ec, 95 int (*cmd_xfer)(struct cros_ec_device *ec,
98 struct cros_ec_command *msg); 96 struct cros_ec_command *msg);
99 97