diff options
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r-- | drivers/input/keyboard/gpio_keys.c | 105 | ||||
-rw-r--r-- | drivers/input/keyboard/imx_keypad.c | 13 | ||||
-rw-r--r-- | drivers/input/keyboard/nspire-keypad.c | 56 | ||||
-rw-r--r-- | drivers/input/keyboard/tca6416-keypad.c | 3 | ||||
-rw-r--r-- | drivers/input/keyboard/tegra-kbc.c | 5 |
5 files changed, 108 insertions, 74 deletions
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 77bac4ddf324..8dbf1e69c90a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -8,6 +8,7 @@ #include <linux/module.h> +#include <linux/hrtimer.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> @@ -36,10 +37,11 @@ struct gpio_button_data { unsigned short *code; - struct timer_list release_timer; + struct hrtimer release_timer; unsigned int release_delay; /* in msecs, for IRQ-only buttons */ struct delayed_work work; + struct hrtimer debounce_timer; unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ unsigned int irq; @@ -48,6 +50,7 @@ struct gpio_button_data { bool disabled; bool key_pressed; bool suspended; + bool debounce_use_hrtimer; }; struct gpio_keys_drvdata { @@ -122,6 +125,18 @@ static const unsigned long *get_bm_events_by_type(struct input_dev *dev, return (type == EV_KEY) ? dev->keybit : dev->swbit; } +static void gpio_keys_quiesce_key(void *data) +{ + struct gpio_button_data *bdata = data; + + if (!bdata->gpiod) + hrtimer_cancel(&bdata->release_timer); + if (bdata->debounce_use_hrtimer) + hrtimer_cancel(&bdata->debounce_timer); + else + cancel_delayed_work_sync(&bdata->work); +} + /** * gpio_keys_disable_button() - disables given GPIO button * @bdata: button data for button to be disabled @@ -142,12 +157,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) * Disable IRQ and associated timer/work structure. */ disable_irq(bdata->irq); - - if (bdata->gpiod) - cancel_delayed_work_sync(&bdata->work); - else - del_timer_sync(&bdata->release_timer); - + gpio_keys_quiesce_key(bdata); bdata->disabled = true; } } @@ -360,7 +370,9 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) unsigned int type = button->type ?: EV_KEY; int state; - state = gpiod_get_value_cansleep(bdata->gpiod); + state = bdata->debounce_use_hrtimer ? + gpiod_get_value(bdata->gpiod) : + gpiod_get_value_cansleep(bdata->gpiod); if (state < 0) { dev_err(input->dev.parent, "failed to get gpio state: %d\n", state); @@ -373,7 +385,15 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) } else { input_event(input, type, *bdata->code, state); } - input_sync(input); +} + +static void gpio_keys_debounce_event(struct gpio_button_data *bdata) +{ + gpio_keys_gpio_report_event(bdata); + input_sync(bdata->input); + + if (bdata->button->wakeup) + pm_relax(bdata->input->dev.parent); } static void gpio_keys_gpio_work_func(struct work_struct *work) @@ -381,10 +401,17 @@ static void gpio_keys_gpio_work_func(struct work_struct *work) struct gpio_button_data *bdata = container_of(work, struct gpio_button_data, work.work); - gpio_keys_gpio_report_event(bdata); + gpio_keys_debounce_event(bdata); +} - if (bdata->button->wakeup) - pm_relax(bdata->input->dev.parent); +static enum hrtimer_restart gpio_keys_debounce_timer(struct hrtimer *t) +{ + struct gpio_button_data *bdata = + container_of(t, struct gpio_button_data, debounce_timer); + + gpio_keys_debounce_event(bdata); + + return HRTIMER_NORESTART; } static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) @@ -408,26 +435,33 @@ static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) } } - mod_delayed_work(system_wq, - &bdata->work, - msecs_to_jiffies(bdata->software_debounce)); + if (bdata->debounce_use_hrtimer) { + hrtimer_start(&bdata->debounce_timer, + ms_to_ktime(bdata->software_debounce), + HRTIMER_MODE_REL); + } else { + mod_delayed_work(system_wq, + &bdata->work, + msecs_to_jiffies(bdata->software_debounce)); + } return IRQ_HANDLED; } -static void gpio_keys_irq_timer(struct timer_list *t) +static enum hrtimer_restart gpio_keys_irq_timer(struct hrtimer *t) { - struct gpio_button_data *bdata = from_timer(bdata, t, release_timer); + struct gpio_button_data *bdata = container_of(t, + struct gpio_button_data, + release_timer); struct input_dev *input = bdata->input; - unsigned long flags; - spin_lock_irqsave(&bdata->lock, flags); if (bdata->key_pressed) { input_event(input, EV_KEY, *bdata->code, 0); input_sync(input); bdata->key_pressed = false; } - spin_unlock_irqrestore(&bdata->lock, flags); + + return HRTIMER_NORESTART; } static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) @@ -457,23 +491,14 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) } if (bdata->release_delay) - mod_timer(&bdata->release_timer, - jiffies + msecs_to_jiffies(bdata->release_delay)); + hrtimer_start(&bdata->release_timer, + ms_to_ktime(bdata->release_delay), + HRTIMER_MODE_REL_HARD); out: spin_unlock_irqrestore(&bdata->lock, flags); return IRQ_HANDLED; } -static void gpio_keys_quiesce_key(void *data) -{ - struct gpio_button_data *bdata = data; - - if (bdata->gpiod) - cancel_delayed_work_sync(&bdata->work); - else - del_timer_sync(&bdata->release_timer); -} - static int gpio_keys_setup_key(struct platform_device *pdev, struct input_dev *input, struct gpio_keys_drvdata *ddata, @@ -543,6 +568,14 @@ static int gpio_keys_setup_key(struct platform_device *pdev, if (error < 0) bdata->software_debounce = button->debounce_interval; + + /* + * If reading the GPIO won't sleep, we can use a + * hrtimer instead of a standard timer for the software + * debounce, to reduce the latency as much as possible. + */ + bdata->debounce_use_hrtimer = + !gpiod_cansleep(bdata->gpiod); } if (button->irq) { @@ -561,6 +594,10 @@ static int gpio_keys_setup_key(struct platform_device *pdev, INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func); + hrtimer_init(&bdata->debounce_timer, + CLOCK_REALTIME, HRTIMER_MODE_REL); + bdata->debounce_timer.function = gpio_keys_debounce_timer; + isr = gpio_keys_gpio_isr; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; @@ -595,7 +632,9 @@ static int gpio_keys_setup_key(struct platform_device *pdev, } bdata->release_delay = button->debounce_interval; - timer_setup(&bdata->release_timer, gpio_keys_irq_timer, 0); + hrtimer_init(&bdata->release_timer, + CLOCK_REALTIME, HRTIMER_MODE_REL_HARD); + bdata->release_timer.function = gpio_keys_irq_timer; isr = gpio_keys_irq_isr; irqflags = 0; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 1f5c9ea5e9e5..ae9303848571 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -408,27 +408,18 @@ open_err: return -EIO; } -#ifdef CONFIG_OF static const struct of_device_id imx_keypad_of_match[] = { { .compatible = "fsl,imx21-kpp", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_keypad_of_match); -#endif static int imx_keypad_probe(struct platform_device *pdev) { - const struct matrix_keymap_data *keymap_data = - dev_get_platdata(&pdev->dev); struct imx_keypad *keypad; struct input_dev *input_dev; int irq, error, i, row, col; - if (!keymap_data && !pdev->dev.of_node) { - dev_err(&pdev->dev, "no keymap defined\n"); - return -EINVAL; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -469,7 +460,7 @@ static int imx_keypad_probe(struct platform_device *pdev) input_dev->open = imx_keypad_open; input_dev->close = imx_keypad_close; - error = matrix_keypad_build_keymap(keymap_data, NULL, + error = matrix_keypad_build_keymap(NULL, NULL, MAX_MATRIX_KEY_ROWS, MAX_MATRIX_KEY_COLS, keypad->keycodes, input_dev); @@ -582,7 +573,7 @@ static struct platform_driver imx_keypad_driver = { .driver = { .name = "imx-keypad", .pm = &imx_kbd_pm_ops, - .of_match_table = of_match_ptr(imx_keypad_of_match), + .of_match_table = imx_keypad_of_match, }, .probe = imx_keypad_probe, }; 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; diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 9b0f9665dcb0..2a9755910065 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -274,7 +274,7 @@ static int tca6416_keypad_probe(struct i2c_client *client, error = request_threaded_irq(chip->irqnum, NULL, tca6416_keys_isr, IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, + IRQF_ONESHOT | IRQF_NO_AUTOEN, "tca6416-keypad", chip); if (error) { dev_dbg(&client->dev, @@ -282,7 +282,6 @@ static int tca6416_keypad_probe(struct i2c_client *client, chip->irqnum, error); goto fail1; } - disable_irq(chip->irqnum); } error = input_register_device(input); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 9671842a082a..570fe18c0ce9 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -694,14 +694,13 @@ static int tegra_kbc_probe(struct platform_device *pdev) input_set_drvdata(kbc->idev, kbc); err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr, - IRQF_TRIGGER_HIGH, pdev->name, kbc); + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + pdev->name, kbc); if (err) { dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); return err; } - disable_irq(kbc->irq); - err = input_register_device(kbc->idev); if (err) { dev_err(&pdev->dev, "failed to register input device\n"); |