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

Input: nspire-keypad - enable interrupts only when opened

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>

authored by

Fabian Vogt and committed by
Dmitry Torokhov
69d5ff3e daa58c8e

+31 -25
+31 -25
drivers/input/keyboard/nspire-keypad.c
··· 93 93 return IRQ_HANDLED; 94 94 } 95 95 96 - static int nspire_keypad_chip_init(struct nspire_keypad *keypad) 96 + static int nspire_keypad_open(struct input_dev *input) 97 97 { 98 + struct nspire_keypad *keypad = input_get_drvdata(input); 98 99 unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles; 100 + int error; 101 + 102 + error = clk_prepare_enable(keypad->clk); 103 + if (error) 104 + return error; 99 105 100 106 cycles_per_us = (clk_get_rate(keypad->clk) / 1000000); 101 107 if (cycles_per_us == 0) ··· 127 121 keypad->int_mask = 1 << 1; 128 122 writel(keypad->int_mask, keypad->reg_base + KEYPAD_INTMSK); 129 123 130 - /* Disable GPIO interrupts to prevent hanging on touchpad */ 131 - /* Possibly used to detect touchpad events */ 132 - writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT); 133 - /* Acknowledge existing interrupts */ 134 - writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS); 135 - 136 - return 0; 137 - } 138 - 139 - static int nspire_keypad_open(struct input_dev *input) 140 - { 141 - struct nspire_keypad *keypad = input_get_drvdata(input); 142 - int error; 143 - 144 - error = clk_prepare_enable(keypad->clk); 145 - if (error) 146 - return error; 147 - 148 - error = nspire_keypad_chip_init(keypad); 149 - if (error) { 150 - clk_disable_unprepare(keypad->clk); 151 - return error; 152 - } 153 - 154 124 return 0; 155 125 } 156 126 157 127 static void nspire_keypad_close(struct input_dev *input) 158 128 { 159 129 struct nspire_keypad *keypad = input_get_drvdata(input); 130 + 131 + /* Disable interrupts */ 132 + writel(0, keypad->reg_base + KEYPAD_INTMSK); 133 + /* Acknowledge existing interrupts */ 134 + writel(~0, keypad->reg_base + KEYPAD_INT); 160 135 161 136 clk_disable_unprepare(keypad->clk); 162 137 } ··· 196 209 dev_err(&pdev->dev, "failed to allocate input device\n"); 197 210 return -ENOMEM; 198 211 } 212 + 213 + error = clk_prepare_enable(keypad->clk); 214 + if (error) { 215 + dev_err(&pdev->dev, "failed to enable clock\n"); 216 + return error; 217 + } 218 + 219 + /* Disable interrupts */ 220 + writel(0, keypad->reg_base + KEYPAD_INTMSK); 221 + /* Acknowledge existing interrupts */ 222 + writel(~0, keypad->reg_base + KEYPAD_INT); 223 + 224 + /* Disable GPIO interrupts to prevent hanging on touchpad */ 225 + /* Possibly used to detect touchpad events */ 226 + writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT); 227 + /* Acknowledge existing GPIO interrupts */ 228 + writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS); 229 + 230 + clk_disable_unprepare(keypad->clk); 199 231 200 232 input_set_drvdata(input, keypad); 201 233