Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andrew Bresticker and committed by
Lee Jones
d1fd345e 12ebc8a5

+34 -61
+33 -25
drivers/input/keyboard/cros_ec_keyb.c
··· 24 24 #include <linux/module.h> 25 25 #include <linux/i2c.h> 26 26 #include <linux/input.h> 27 + #include <linux/interrupt.h> 27 28 #include <linux/kernel.h> 28 - #include <linux/notifier.h> 29 29 #include <linux/platform_device.h> 30 30 #include <linux/slab.h> 31 31 #include <linux/input/matrix_keypad.h> ··· 42 42 * @dev: Device pointer 43 43 * @idev: Input device 44 44 * @ec: Top level ChromeOS device to use to talk to EC 45 - * @event_notifier: interrupt event notifier for transport devices 46 45 */ 47 46 struct cros_ec_keyb { 48 47 unsigned int rows; ··· 54 55 struct device *dev; 55 56 struct input_dev *idev; 56 57 struct cros_ec_device *ec; 57 - struct notifier_block notifier; 58 58 }; 59 59 60 60 ··· 171 173 input_sync(ckdev->idev); 172 174 } 173 175 174 - static int cros_ec_keyb_open(struct input_dev *dev) 175 - { 176 - struct cros_ec_keyb *ckdev = input_get_drvdata(dev); 177 - 178 - return blocking_notifier_chain_register(&ckdev->ec->event_notifier, 179 - &ckdev->notifier); 180 - } 181 - 182 - static void cros_ec_keyb_close(struct input_dev *dev) 183 - { 184 - struct cros_ec_keyb *ckdev = input_get_drvdata(dev); 185 - 186 - blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, 187 - &ckdev->notifier); 188 - } 189 - 190 176 static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) 191 177 { 192 178 struct cros_ec_command msg = { ··· 185 203 return ckdev->ec->cmd_xfer(ckdev->ec, &msg); 186 204 } 187 205 188 - static int cros_ec_keyb_work(struct notifier_block *nb, 189 - unsigned long state, void *_notify) 206 + static irqreturn_t cros_ec_keyb_irq(int irq, void *data) 190 207 { 208 + struct cros_ec_keyb *ckdev = data; 209 + struct cros_ec_device *ec = ckdev->ec; 191 210 int ret; 192 - struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, 193 - notifier); 194 211 uint8_t kb_state[ckdev->cols]; 212 + 213 + if (device_may_wakeup(ec->dev)) 214 + pm_wakeup_event(ec->dev, 0); 195 215 196 216 ret = cros_ec_keyb_get_state(ckdev, kb_state); 197 217 if (ret >= 0) 198 218 cros_ec_keyb_process(ckdev, kb_state, ret); 219 + else 220 + dev_err(ec->dev, "failed to get keyboard state: %d\n", ret); 199 221 200 - return NOTIFY_DONE; 222 + return IRQ_HANDLED; 223 + } 224 + 225 + static int cros_ec_keyb_open(struct input_dev *dev) 226 + { 227 + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); 228 + struct cros_ec_device *ec = ckdev->ec; 229 + 230 + return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq, 231 + IRQF_TRIGGER_LOW | IRQF_ONESHOT, 232 + "cros_ec_keyb", ckdev); 233 + } 234 + 235 + static void cros_ec_keyb_close(struct input_dev *dev) 236 + { 237 + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); 238 + struct cros_ec_device *ec = ckdev->ec; 239 + 240 + free_irq(ec->irq, ckdev); 201 241 } 202 242 203 243 static int cros_ec_keyb_probe(struct platform_device *pdev) ··· 250 246 if (!idev) 251 247 return -ENOMEM; 252 248 249 + if (!ec->irq) { 250 + dev_err(dev, "no EC IRQ specified\n"); 251 + return -EINVAL; 252 + } 253 + 253 254 ckdev->ec = ec; 254 - ckdev->notifier.notifier_call = cros_ec_keyb_work; 255 255 ckdev->dev = dev; 256 256 dev_set_drvdata(&pdev->dev, ckdev); 257 257
+1 -34
drivers/mfd/cros_ec.c
··· 62 62 } 63 63 EXPORT_SYMBOL(cros_ec_check_result); 64 64 65 - static 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 - 77 65 static const struct mfd_cell cros_devs[] = { 78 66 { 79 67 .name = "cros-ec-keyb", ··· 80 92 struct device *dev = ec_dev->dev; 81 93 int err = 0; 82 94 83 - BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); 84 - 85 95 if (ec_dev->din_size) { 86 96 ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 87 97 if (!ec_dev->din) ··· 91 105 return -ENOMEM; 92 106 } 93 107 94 - if (!ec_dev->irq) { 95 - dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq); 96 - return err; 97 - } 98 - 99 - err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, 100 - IRQF_TRIGGER_LOW | IRQF_ONESHOT, 101 - "chromeos-ec", ec_dev); 102 - if (err) { 103 - dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err); 104 - return err; 105 - } 106 - 107 108 err = mfd_add_devices(dev, 0, cros_devs, 108 109 ARRAY_SIZE(cros_devs), 109 110 NULL, ec_dev->irq, NULL); 110 111 if (err) { 111 112 dev_err(dev, "failed to add mfd devices\n"); 112 - goto fail_mfd; 113 + return err; 113 114 } 114 115 115 116 dev_info(dev, "Chrome EC device registered\n"); 116 117 117 118 return 0; 118 - 119 - fail_mfd: 120 - free_irq(ec_dev->irq, ec_dev); 121 - 122 - return err; 123 119 } 124 120 EXPORT_SYMBOL(cros_ec_register); 125 121 126 122 int cros_ec_remove(struct cros_ec_device *ec_dev) 127 123 { 128 124 mfd_remove_devices(ec_dev->dev); 129 - free_irq(ec_dev->irq, ec_dev); 130 125 131 126 return 0; 132 127 }
-2
include/linux/mfd/cros_ec.h
··· 62 62 * @dev: Device pointer 63 63 * @was_wake_device: true if this device was set to wake the system from 64 64 * sleep at the last suspend 65 - * @event_notifier: interrupt event notifier for transport devices 66 65 * @cmd_xfer: send command to EC and get response 67 66 * Returns the number of bytes received if the communication succeeded, but 68 67 * that doesn't mean the EC was happy with the command. The caller ··· 92 93 struct device *dev; 93 94 bool was_wake_device; 94 95 struct class *cros_class; 95 - struct blocking_notifier_head event_notifier; 96 96 int (*cmd_xfer)(struct cros_ec_device *ec, 97 97 struct cros_ec_command *msg); 98 98