mfd: cros_ec: Move EC interrupt to cros_ec_keyb
authorAndrew Bresticker <abrestic@chromium.org>
Wed, 18 Jun 2014 18:14:07 +0000 (11:14 -0700)
committerLee Jones <lee.jones@linaro.org>
Wed, 9 Jul 2014 13:58:20 +0000 (14:58 +0100)
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>
drivers/input/keyboard/cros_ec_keyb.c
drivers/mfd/cros_ec.c
include/linux/mfd/cros_ec.h

index b8341ab99f55004942b8e406dc3d071efd8caf57..791781ade4e71535027dd9ba707e295fa9df57da 100644 (file)
@@ -24,8 +24,8 @@
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
-#include <linux/notifier.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/input/matrix_keypad.h>
@@ -42,7 +42,6 @@
  * @dev: Device pointer
  * @idev: Input device
  * @ec: Top level ChromeOS device to use to talk to EC
- * @event_notifier: interrupt event notifier for transport devices
  */
 struct cros_ec_keyb {
        unsigned int rows;
@@ -55,7 +54,6 @@ struct cros_ec_keyb {
        struct device *dev;
        struct input_dev *idev;
        struct cros_ec_device *ec;
-       struct notifier_block notifier;
 };
 
 
@@ -173,22 +171,6 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
        input_sync(ckdev->idev);
 }
 
-static int cros_ec_keyb_open(struct input_dev *dev)
-{
-       struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
-
-       return blocking_notifier_chain_register(&ckdev->ec->event_notifier,
-                                               &ckdev->notifier);
-}
-
-static void cros_ec_keyb_close(struct input_dev *dev)
-{
-       struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
-
-       blocking_notifier_chain_unregister(&ckdev->ec->event_notifier,
-                                          &ckdev->notifier);
-}
-
 static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
 {
        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)
        return ckdev->ec->cmd_xfer(ckdev->ec, &msg);
 }
 
-static int cros_ec_keyb_work(struct notifier_block *nb,
-                    unsigned long state, void *_notify)
+static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
 {
+       struct cros_ec_keyb *ckdev = data;
+       struct cros_ec_device *ec = ckdev->ec;
        int ret;
-       struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb,
-                                                   notifier);
        uint8_t kb_state[ckdev->cols];
 
+       if (device_may_wakeup(ec->dev))
+               pm_wakeup_event(ec->dev, 0);
+
        ret = cros_ec_keyb_get_state(ckdev, kb_state);
        if (ret >= 0)
                cros_ec_keyb_process(ckdev, kb_state, ret);
+       else
+               dev_err(ec->dev, "failed to get keyboard state: %d\n", ret);
 
-       return NOTIFY_DONE;
+       return IRQ_HANDLED;
+}
+
+static int cros_ec_keyb_open(struct input_dev *dev)
+{
+       struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
+       struct cros_ec_device *ec = ckdev->ec;
+
+       return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       "cros_ec_keyb", ckdev);
+}
+
+static void cros_ec_keyb_close(struct input_dev *dev)
+{
+       struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
+       struct cros_ec_device *ec = ckdev->ec;
+
+       free_irq(ec->irq, ckdev);
 }
 
 static int cros_ec_keyb_probe(struct platform_device *pdev)
@@ -246,8 +250,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
        if (!idev)
                return -ENOMEM;
 
+       if (!ec->irq) {
+               dev_err(dev, "no EC IRQ specified\n");
+               return -EINVAL;
+       }
+
        ckdev->ec = ec;
-       ckdev->notifier.notifier_call = cros_ec_keyb_work;
        ckdev->dev = dev;
        dev_set_drvdata(&pdev->dev, ckdev);
 
index 83e30c66357850dc132e5cf005c23597583b19e5..4873f9c504526503eaef81ba97dfd640e65e3386 100644 (file)
@@ -62,18 +62,6 @@ int cros_ec_check_result(struct cros_ec_device *ec_dev,
 }
 EXPORT_SYMBOL(cros_ec_check_result);
 
-static irqreturn_t ec_irq_thread(int irq, void *data)
-{
-       struct cros_ec_device *ec_dev = data;
-
-       if (device_may_wakeup(ec_dev->dev))
-               pm_wakeup_event(ec_dev->dev, 0);
-
-       blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev);
-
-       return IRQ_HANDLED;
-}
-
 static const struct mfd_cell cros_devs[] = {
        {
                .name = "cros-ec-keyb",
@@ -92,8 +80,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
        struct device *dev = ec_dev->dev;
        int err = 0;
 
-       BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
-
        if (ec_dev->din_size) {
                ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
                if (!ec_dev->din)
@@ -105,42 +91,23 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
                        return -ENOMEM;
        }
 
-       if (!ec_dev->irq) {
-               dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq);
-               return err;
-       }
-
-       err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
-                                  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-                                  "chromeos-ec", ec_dev);
-       if (err) {
-               dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err);
-               return err;
-       }
-
        err = mfd_add_devices(dev, 0, cros_devs,
                              ARRAY_SIZE(cros_devs),
                              NULL, ec_dev->irq, NULL);
        if (err) {
                dev_err(dev, "failed to add mfd devices\n");
-               goto fail_mfd;
+               return err;
        }
 
        dev_info(dev, "Chrome EC device registered\n");
 
        return 0;
-
-fail_mfd:
-       free_irq(ec_dev->irq, ec_dev);
-
-       return err;
 }
 EXPORT_SYMBOL(cros_ec_register);
 
 int cros_ec_remove(struct cros_ec_device *ec_dev)
 {
        mfd_remove_devices(ec_dev->dev);
-       free_irq(ec_dev->irq, ec_dev);
 
        return 0;
 }
index 0ebf26fddbbbd1f60a16d5fe0b9d42a780e94985..fcbe9d129a9da60e1c47e3682ce18961b460a223 100644 (file)
@@ -62,7 +62,6 @@ struct cros_ec_command {
  * @dev: Device pointer
  * @was_wake_device: true if this device was set to wake the system from
  * sleep at the last suspend
- * @event_notifier: interrupt event notifier for transport devices
  * @cmd_xfer: send command to EC and get response
  *     Returns the number of bytes received if the communication succeeded, but
  *     that doesn't mean the EC was happy with the command. The caller
@@ -93,7 +92,6 @@ struct cros_ec_device {
        struct device *dev;
        bool was_wake_device;
        struct class *cros_class;
-       struct blocking_notifier_head event_notifier;
        int (*cmd_xfer)(struct cros_ec_device *ec,
                        struct cros_ec_command *msg);