提交 d1fd345e 编写于 作者: A Andrew Bresticker 提交者: Lee Jones

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: NAndrew Bresticker <abrestic@chromium.org>
Signed-off-by: NDoug Anderson <dianders@chromium.org>
Acked-by: NDmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: NLee Jones <lee.jones@linaro.org>
上级 12ebc8a5
......@@ -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);
......
......@@ -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;
}
......
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册