| From 8546f002f51a8858cb78e196743bfbcf325d9ef8 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 23 Mar 2021 10:45:55 -0700 |
| Subject: Input: nspire-keypad - enable interrupts only when opened |
| |
| From: Fabian Vogt <fabian@ritter-vogt.de> |
| |
| [ Upstream commit 69d5ff3e9e51e23d5d81bf48480aa5671be67a71 ] |
| |
| The driver registers an interrupt handler in _probe, but didn't configure |
| them until later when the _open function is called. In between, the keypad |
| can fire an IRQ due to touchpad activity, which the handler ignores. This |
| causes the kernel to disable the interrupt, blocking the keypad from |
| working. |
| |
| Fix this by disabling interrupts before registering the handler. |
| Additionally, disable them in _close, so that they're only enabled while |
| open. |
| |
| Fixes: fc4f31461892 ("Input: add TI-Nspire keypad support") |
| Signed-off-by: Fabian Vogt <fabian@ritter-vogt.de> |
| Link: https://lore.kernel.org/r/3383725.iizBOSrK1V@linux-e202.suse.de |
| Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/input/keyboard/nspire-keypad.c | 56 ++++++++++++++------------ |
| 1 file changed, 31 insertions(+), 25 deletions(-) |
| |
| diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c |
| index 63d5e488137d..e9fa1423f136 100644 |
| --- a/drivers/input/keyboard/nspire-keypad.c |
| +++ b/drivers/input/keyboard/nspire-keypad.c |
| @@ -93,9 +93,15 @@ static irqreturn_t nspire_keypad_irq(int irq, void *dev_id) |
| return IRQ_HANDLED; |
| } |
| |
| -static int nspire_keypad_chip_init(struct nspire_keypad *keypad) |
| +static int nspire_keypad_open(struct input_dev *input) |
| { |
| + struct nspire_keypad *keypad = input_get_drvdata(input); |
| unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles; |
| + int error; |
| + |
| + error = clk_prepare_enable(keypad->clk); |
| + if (error) |
| + return error; |
| |
| cycles_per_us = (clk_get_rate(keypad->clk) / 1000000); |
| if (cycles_per_us == 0) |
| @@ -121,30 +127,6 @@ static int nspire_keypad_chip_init(struct nspire_keypad *keypad) |
| keypad->int_mask = 1 << 1; |
| writel(keypad->int_mask, keypad->reg_base + KEYPAD_INTMSK); |
| |
| - /* Disable GPIO interrupts to prevent hanging on touchpad */ |
| - /* Possibly used to detect touchpad events */ |
| - writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT); |
| - /* Acknowledge existing interrupts */ |
| - writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS); |
| - |
| - return 0; |
| -} |
| - |
| -static int nspire_keypad_open(struct input_dev *input) |
| -{ |
| - struct nspire_keypad *keypad = input_get_drvdata(input); |
| - int error; |
| - |
| - error = clk_prepare_enable(keypad->clk); |
| - if (error) |
| - return error; |
| - |
| - error = nspire_keypad_chip_init(keypad); |
| - if (error) { |
| - clk_disable_unprepare(keypad->clk); |
| - return error; |
| - } |
| - |
| return 0; |
| } |
| |
| @@ -152,6 +134,11 @@ static void nspire_keypad_close(struct input_dev *input) |
| { |
| struct nspire_keypad *keypad = input_get_drvdata(input); |
| |
| + /* Disable interrupts */ |
| + writel(0, keypad->reg_base + KEYPAD_INTMSK); |
| + /* Acknowledge existing interrupts */ |
| + writel(~0, keypad->reg_base + KEYPAD_INT); |
| + |
| clk_disable_unprepare(keypad->clk); |
| } |
| |
| @@ -210,6 +197,25 @@ static int nspire_keypad_probe(struct platform_device *pdev) |
| return -ENOMEM; |
| } |
| |
| + error = clk_prepare_enable(keypad->clk); |
| + if (error) { |
| + dev_err(&pdev->dev, "failed to enable clock\n"); |
| + return error; |
| + } |
| + |
| + /* Disable interrupts */ |
| + writel(0, keypad->reg_base + KEYPAD_INTMSK); |
| + /* Acknowledge existing interrupts */ |
| + writel(~0, keypad->reg_base + KEYPAD_INT); |
| + |
| + /* Disable GPIO interrupts to prevent hanging on touchpad */ |
| + /* Possibly used to detect touchpad events */ |
| + writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT); |
| + /* Acknowledge existing GPIO interrupts */ |
| + writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS); |
| + |
| + clk_disable_unprepare(keypad->clk); |
| + |
| input_set_drvdata(input, keypad); |
| |
| input->id.bustype = BUS_HOST; |
| -- |
| 2.30.2 |
| |