From f5cace4b93d736cef348211ae0814cabdd26d86a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 18 Dec 2020 10:03:12 -0800 Subject: Input: imx_keypad - add dependency on HAS_IOMEM devm_platform_ioremap_resource() depends on CONFIG_HAS_IOMEM, so let's add it to the dependencies when COMPILE_TEST is enabled. Reported-by: kernel test robot Fixes: c8834032ffe2 ("Input: imx_keypad - add COMPILE_TEST support") Link: https://lore.kernel.org/r/X9llpA3w1zlZCHXU@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 2b321c17054a..94eab82086b2 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -446,7 +446,7 @@ config KEYBOARD_MPR121 config KEYBOARD_SNVS_PWRKEY tristate "IMX SNVS Power Key Driver" - depends on ARCH_MXC || COMPILE_TEST + depends on ARCH_MXC || (COMPILE_TEST && HAS_IOMEM) depends on OF help This is the snvs powerkey driver for the Freescale i.MX application -- cgit v1.2.3-59-g8ed1b From 60dc45119465d086724851d2a5fd09daeb3c515e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 10 Jan 2021 18:19:59 -0800 Subject: Input: omap4-keypad - switch to use managed resources Now that input core supports devres-managed input devices we can clean up this driver a bit. Link: https://lore.kernel.org/r/X/qfJKiM21uyksYl@google.com Tested-by: Tony Lindgren Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 137 +++++++++++++--------------------- 1 file changed, 53 insertions(+), 84 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index b17ac2a295b9..55cc4855fb5c 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -252,8 +252,14 @@ static int omap4_keypad_check_revision(struct device *dev, return 0; } +static void omap4_disable_pm(void *d) +{ + pm_runtime_disable(d); +} + static int omap4_keypad_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct omap4_keypad *keypad_data; struct input_dev *input_dev; struct resource *res; @@ -271,33 +277,29 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (irq < 0) return irq; - keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL); + keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL); if (!keypad_data) { - dev_err(&pdev->dev, "keypad_data memory allocation failed\n"); + dev_err(dev, "keypad_data memory allocation failed\n"); return -ENOMEM; } keypad_data->irq = irq; - error = omap4_keypad_parse_dt(&pdev->dev, keypad_data); + error = omap4_keypad_parse_dt(dev, keypad_data); if (error) - goto err_free_keypad; + return error; - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "can't request mem region\n"); - error = -EBUSY; - goto err_free_keypad; - } + keypad_data->base = devm_ioremap_resource(dev, res); + if (IS_ERR(keypad_data->base)) + return PTR_ERR(keypad_data->base); - keypad_data->base = ioremap(res->start, resource_size(res)); - if (!keypad_data->base) { - dev_err(&pdev->dev, "can't ioremap mem resource\n"); - error = -ENOMEM; - goto err_release_mem; - } + pm_runtime_enable(dev); - pm_runtime_enable(&pdev->dev); + error = devm_add_action_or_reset(dev, omap4_disable_pm, dev); + if (error) { + dev_err(dev, "unable to register cleanup action\n"); + return error; + } /* * Enable clocks for the keypad module so that we can read @@ -307,27 +309,25 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (error) { dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); pm_runtime_put_noidle(&pdev->dev); - } else { - error = omap4_keypad_check_revision(&pdev->dev, - keypad_data); - if (!error) { - /* Ensure device does not raise interrupts */ - omap4_keypad_stop(keypad_data); - } - pm_runtime_put_sync(&pdev->dev); + return error; + } + + error = omap4_keypad_check_revision(&pdev->dev, keypad_data); + if (!error) { + /* Ensure device does not raise interrupts */ + omap4_keypad_stop(keypad_data); } + + pm_runtime_put_sync(&pdev->dev); if (error) - goto err_pm_disable; + return error; /* input device allocation */ - keypad_data->input = input_dev = input_allocate_device(); - if (!input_dev) { - error = -ENOMEM; - goto err_pm_disable; - } + keypad_data->input = input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return -ENOMEM; input_dev->name = pdev->name; - input_dev->dev.parent = &pdev->dev; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; @@ -344,84 +344,53 @@ static int omap4_keypad_probe(struct platform_device *pdev) keypad_data->row_shift = get_count_order(keypad_data->cols); max_keys = keypad_data->rows << keypad_data->row_shift; - keypad_data->keymap = kcalloc(max_keys, - sizeof(keypad_data->keymap[0]), - GFP_KERNEL); + keypad_data->keymap = devm_kcalloc(dev, + max_keys, + sizeof(keypad_data->keymap[0]), + GFP_KERNEL); if (!keypad_data->keymap) { - dev_err(&pdev->dev, "Not enough memory for keymap\n"); - error = -ENOMEM; - goto err_free_input; + dev_err(dev, "Not enough memory for keymap\n"); + return -ENOMEM; } error = matrix_keypad_build_keymap(NULL, NULL, keypad_data->rows, keypad_data->cols, keypad_data->keymap, input_dev); if (error) { - dev_err(&pdev->dev, "failed to build keymap\n"); - goto err_free_keymap; + dev_err(dev, "failed to build keymap\n"); + return error; } - error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler, - omap4_keypad_irq_thread_fn, IRQF_ONESHOT, - "omap4-keypad", keypad_data); + error = devm_request_threaded_irq(dev, keypad_data->irq, + omap4_keypad_irq_handler, + omap4_keypad_irq_thread_fn, + IRQF_ONESHOT, + "omap4-keypad", keypad_data); if (error) { - dev_err(&pdev->dev, "failed to register interrupt\n"); - goto err_free_keymap; + dev_err(dev, "failed to register interrupt\n"); + return error; } error = input_register_device(keypad_data->input); - if (error < 0) { - dev_err(&pdev->dev, "failed to register input device\n"); - goto err_free_irq; + if (error) { + dev_err(dev, "failed to register input device\n"); + return error; } - device_init_wakeup(&pdev->dev, true); - error = dev_pm_set_wake_irq(&pdev->dev, keypad_data->irq); + device_init_wakeup(dev, true); + error = dev_pm_set_wake_irq(dev, keypad_data->irq); if (error) - dev_warn(&pdev->dev, - "failed to set up wakeup irq: %d\n", error); + dev_warn(dev, "failed to set up wakeup irq: %d\n", error); platform_set_drvdata(pdev, keypad_data); return 0; - -err_free_irq: - free_irq(keypad_data->irq, keypad_data); -err_free_keymap: - kfree(keypad_data->keymap); -err_free_input: - input_free_device(input_dev); -err_pm_disable: - pm_runtime_disable(&pdev->dev); - iounmap(keypad_data->base); -err_release_mem: - release_mem_region(res->start, resource_size(res)); -err_free_keypad: - kfree(keypad_data); - return error; } static int omap4_keypad_remove(struct platform_device *pdev) { - struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); - struct resource *res; - dev_pm_clear_wake_irq(&pdev->dev); - free_irq(keypad_data->irq, keypad_data); - - pm_runtime_disable(&pdev->dev); - - input_unregister_device(keypad_data->input); - - iounmap(keypad_data->base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(keypad_data->keymap); - kfree(keypad_data); - return 0; } -- cgit v1.2.3-59-g8ed1b From 7e0541b25702ccaa9560bdd2441caedca2d438e9 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 18:29:45 -0800 Subject: Input: omap4-keypad - disable unused long interrupts We are not using the long events and they produce extra interrupts. Let's not enable them at all. Note that also the v3.0.8 Linux Android kernel has long interrupts disabled. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20210110190529.46135-2-tony@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 55cc4855fb5c..ab761aa66b6d 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -176,10 +176,9 @@ static int omap4_keypad_open(struct input_dev *input) kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, - OMAP4_DEF_IRQENABLE_EVENTEN | - OMAP4_DEF_IRQENABLE_LONGKEY); + OMAP4_DEF_IRQENABLE_EVENTEN); kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE, - OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA); + OMAP4_DEF_WUP_EVENT_ENA); enable_irq(keypad_data->irq); -- cgit v1.2.3-59-g8ed1b From 905dbf1d39d7f008acc46475384b33f98c512baf Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 18:50:53 -0800 Subject: Input: omap4-keypad - scan keys in two phases and simplify with bitmask Because of errata i689 the keyboard can idle with state where no key up interrupts are seen until after the next key press. This means we need to first check for any lost key up events before scanning for new down events. For example, rapidly pressing shift-shift-j can sometimes produce a J instead of j. Let's fix the issue by scanning the keyboard in two phases. First we scan for any key up events that we may have missed, and then we scan for key down events. Let's also simplify things with for_each_set_bit() as suggested by Dmitry Torokhov . Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20210110190529.46135-3-tony@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 73 +++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 28 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index ab761aa66b6d..6dcf27af856d 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -78,7 +78,7 @@ struct omap4_keypad { u32 irqreg_offset; unsigned int row_shift; bool no_autorepeat; - unsigned char key_state[8]; + u64 keys; unsigned short *keymap; }; @@ -107,6 +107,33 @@ static void kbd_write_irqreg(struct omap4_keypad *keypad_data, keypad_data->base + keypad_data->irqreg_offset + offset); } +static int omap4_keypad_report_keys(struct omap4_keypad *keypad_data, + u64 keys, bool down) +{ + struct input_dev *input_dev = keypad_data->input; + unsigned int col, row, code; + DECLARE_BITMAP(mask, 64); + unsigned long bit; + int events = 0; + + bitmap_from_u64(mask, keys); + + for_each_set_bit(bit, mask, keypad_data->rows * BITS_PER_BYTE) { + row = bit / BITS_PER_BYTE; + col = bit % BITS_PER_BYTE; + code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad_data->keymap[code], down); + + events++; + } + + if (events) + input_sync(input_dev); + + return events; +} /* Interrupt handlers */ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) @@ -122,35 +149,25 @@ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) { struct omap4_keypad *keypad_data = dev_id; - struct input_dev *input_dev = keypad_data->input; - unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)]; - unsigned int col, row, code, changed; - u32 *new_state = (u32 *) key_state; - - *new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); - *(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); - - for (row = 0; row < keypad_data->rows; row++) { - changed = key_state[row] ^ keypad_data->key_state[row]; - if (!changed) - continue; - - for (col = 0; col < keypad_data->cols; col++) { - if (changed & (1 << col)) { - code = MATRIX_SCAN_CODE(row, col, - keypad_data->row_shift); - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, - keypad_data->keymap[code], - key_state[row] & (1 << col)); - } - } - } + u32 low, high; + u64 keys, changed; + + low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); + high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); + keys = low | (u64)high << 32; + + changed = keys ^ keypad_data->keys; + + /* + * Report key up events separately and first. This matters in case we + * lost key-up interrupt and just now catching up. + */ + omap4_keypad_report_keys(keypad_data, changed & ~keys, false); - input_sync(input_dev); + /* Report key down events */ + omap4_keypad_report_keys(keypad_data, changed & keys, true); - memcpy(keypad_data->key_state, key_state, - sizeof(keypad_data->key_state)); + keypad_data->keys = keys; /* clear pending interrupts */ kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, -- cgit v1.2.3-59-g8ed1b From 98b0c88d4bfb53b309350d9cdada96fd0deaa4af Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 20:28:40 -0800 Subject: Input: omap4-keypad - move rest of key scanning to a separate function Let's move rest of the key scanning code to omap4_keypad_scan_keys(). We will use omap4_keypad_scan_keys() also for implementing errata handling to clear the stuck last key in the following patch. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20210110190529.46135-4-tony@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 39 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 6dcf27af856d..c48705dd049b 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -71,6 +71,7 @@ struct omap4_keypad { void __iomem *base; unsigned int irq; + struct mutex lock; /* for key scan */ unsigned int rows; unsigned int cols; @@ -135,6 +136,28 @@ static int omap4_keypad_report_keys(struct omap4_keypad *keypad_data, return events; } +static void omap4_keypad_scan_keys(struct omap4_keypad *keypad_data, u64 keys) +{ + u64 changed; + + mutex_lock(&keypad_data->lock); + + changed = keys ^ keypad_data->keys; + + /* + * Report key up events separately and first. This matters in case we + * lost key-up interrupt and just now catching up. + */ + omap4_keypad_report_keys(keypad_data, changed & ~keys, false); + + /* Report key down events */ + omap4_keypad_report_keys(keypad_data, changed & keys, true); + + keypad_data->keys = keys; + + mutex_unlock(&keypad_data->lock); +} + /* Interrupt handlers */ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) { @@ -150,24 +173,13 @@ static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) { struct omap4_keypad *keypad_data = dev_id; u32 low, high; - u64 keys, changed; + u64 keys; low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); keys = low | (u64)high << 32; - changed = keys ^ keypad_data->keys; - - /* - * Report key up events separately and first. This matters in case we - * lost key-up interrupt and just now catching up. - */ - omap4_keypad_report_keys(keypad_data, changed & ~keys, false); - - /* Report key down events */ - omap4_keypad_report_keys(keypad_data, changed & keys, true); - - keypad_data->keys = keys; + omap4_keypad_scan_keys(keypad_data, keys); /* clear pending interrupts */ kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, @@ -300,6 +312,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) } keypad_data->irq = irq; + mutex_init(&keypad_data->lock); error = omap4_keypad_parse_dt(dev, keypad_data); if (error) -- cgit v1.2.3-59-g8ed1b From 29bf35e5ee808dbbcc8cff7a0bf6d4349148b586 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 22:16:51 -0800 Subject: Input: omap4-keypad - use PM runtime autosuspend Implement PM runtime autosuspend support to prepare for adding handling to clear stuck last pressed key in the following patches. The hardware has support for autoidle and can wake up to keypress events. Let's also update omap4_keypad_probe() to use dev instead of &pdev->dev since we already have it from the earlier changes. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/X/vqCs5/EDURprAJ@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 50 +++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index c48705dd049b..291970c09091 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -172,9 +172,17 @@ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) { struct omap4_keypad *keypad_data = dev_id; + struct device *dev = keypad_data->input->dev.parent; u32 low, high; + int error; u64 keys; + error = pm_runtime_get_sync(dev); + if (error < 0) { + pm_runtime_put_noidle(dev); + return IRQ_NONE; + } + low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); keys = low | (u64)high << 32; @@ -185,14 +193,23 @@ static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return IRQ_HANDLED; } static int omap4_keypad_open(struct input_dev *input) { struct omap4_keypad *keypad_data = input_get_drvdata(input); + struct device *dev = input->dev.parent; + int error; - pm_runtime_get_sync(input->dev.parent); + error = pm_runtime_get_sync(dev); + if (error < 0) { + pm_runtime_put_noidle(dev); + return error; + } disable_irq(keypad_data->irq); @@ -211,6 +228,9 @@ static int omap4_keypad_open(struct input_dev *input) enable_irq(keypad_data->irq); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; } @@ -228,14 +248,20 @@ static void omap4_keypad_stop(struct omap4_keypad *keypad_data) static void omap4_keypad_close(struct input_dev *input) { - struct omap4_keypad *keypad_data; + struct omap4_keypad *keypad_data = input_get_drvdata(input); + struct device *dev = input->dev.parent; + int error; + + error = pm_runtime_get_sync(dev); + if (error < 0) + pm_runtime_put_noidle(dev); - keypad_data = input_get_drvdata(input); disable_irq(keypad_data->irq); omap4_keypad_stop(keypad_data); enable_irq(keypad_data->irq); - pm_runtime_put_sync(input->dev.parent); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } static int omap4_keypad_parse_dt(struct device *dev, @@ -282,6 +308,7 @@ static int omap4_keypad_check_revision(struct device *dev, static void omap4_disable_pm(void *d) { + pm_runtime_dont_use_autosuspend(d); pm_runtime_disable(d); } @@ -313,6 +340,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) keypad_data->irq = irq; mutex_init(&keypad_data->lock); + platform_set_drvdata(pdev, keypad_data); error = omap4_keypad_parse_dt(dev, keypad_data); if (error) @@ -322,6 +350,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (IS_ERR(keypad_data->base)) return PTR_ERR(keypad_data->base); + pm_runtime_use_autosuspend(dev); pm_runtime_enable(dev); error = devm_add_action_or_reset(dev, omap4_disable_pm, dev); @@ -334,20 +363,21 @@ static int omap4_keypad_probe(struct platform_device *pdev) * Enable clocks for the keypad module so that we can read * revision register. */ - error = pm_runtime_get_sync(&pdev->dev); + error = pm_runtime_get_sync(dev); if (error) { - dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); - pm_runtime_put_noidle(&pdev->dev); + dev_err(dev, "pm_runtime_get_sync() failed\n"); + pm_runtime_put_noidle(dev); return error; } - error = omap4_keypad_check_revision(&pdev->dev, keypad_data); + error = omap4_keypad_check_revision(dev, keypad_data); if (!error) { /* Ensure device does not raise interrupts */ omap4_keypad_stop(keypad_data); } - pm_runtime_put_sync(&pdev->dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); if (error) return error; @@ -411,8 +441,6 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (error) dev_warn(dev, "failed to set up wakeup irq: %d\n", error); - platform_set_drvdata(pdev, keypad_data); - return 0; } -- cgit v1.2.3-59-g8ed1b From 2f6aa61d87e7d93c39e37b7ba2c4a9035000a0c8 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 10 Jan 2021 22:20:25 -0800 Subject: Input: omap4-keypad - implement errata check for lost key-up events We are still missing handling for errata i689 related issues for the case where we never see a key up interrupt for the last pressed key. To fix the issue, we must scan the key state again after the keyboard controller has idled to check if a key up event was missed. This is described in the omap4 silicon errata documentation for Errata ID i689 "1.32 Keyboard Key Up Event Can Be Missed": "When a key is released for a time shorter than the debounce time, in-between 2 key press (KP1 and KP2), the keyboard state machine will go to idle mode and will never detect the key release (after KP1, and also after KP2), and thus will never generate a new IRQ indicating the key release." We can use PM runtime autosuspend features to check the keyboard state after it enters idle. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/X/vrygoBxzGyXhfc@atomide.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 291970c09091..43375b38ee59 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -60,6 +60,8 @@ ((((dbms) * 1000) / ((1 << ((ptv) + 1)) * (1000000 / 32768))) - 1) #define OMAP4_VAL_DEBOUNCINGTIME_16MS \ OMAP4_KEYPAD_DEBOUNCINGTIME_MS(16, OMAP4_KEYPAD_PTV_DIV_128) +#define OMAP4_KEYPAD_AUTOIDLE_MS 50 /* Approximate measured time */ +#define OMAP4_KEYPAD_IDLE_CHECK_MS (OMAP4_KEYPAD_AUTOIDLE_MS / 2) enum { KBD_REVISION_OMAP4 = 0, @@ -306,6 +308,32 @@ static int omap4_keypad_check_revision(struct device *dev, return 0; } +/* + * Errata ID i689 "1.32 Keyboard Key Up Event Can Be Missed". + * Interrupt may not happen for key-up events. We must clear stuck + * key-up events after the keyboard hardware has auto-idled. + */ +static int __maybe_unused omap4_keypad_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + u32 active; + + active = kbd_readl(keypad_data, OMAP4_KBD_STATEMACHINE); + if (active) { + pm_runtime_mark_last_busy(dev); + return -EBUSY; + } + + omap4_keypad_scan_keys(keypad_data, 0); + + return 0; +} + +static const struct dev_pm_ops omap4_keypad_pm_ops = { + SET_RUNTIME_PM_OPS(omap4_keypad_runtime_suspend, NULL, NULL) +}; + static void omap4_disable_pm(void *d) { pm_runtime_dont_use_autosuspend(d); @@ -351,6 +379,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) return PTR_ERR(keypad_data->base); pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, OMAP4_KEYPAD_IDLE_CHECK_MS); pm_runtime_enable(dev); error = devm_add_action_or_reset(dev, omap4_disable_pm, dev); @@ -463,6 +492,7 @@ static struct platform_driver omap4_keypad_driver = { .driver = { .name = "omap4-keypad", .of_match_table = omap_keypad_dt_match, + .pm = &omap4_keypad_pm_ops, }, }; module_platform_driver(omap4_keypad_driver); -- cgit v1.2.3-59-g8ed1b From b2e3543b5e193c2be802ae2db0a8ae82ec8c0f66 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 19 Feb 2021 10:39:16 -0800 Subject: Input: add missing dependencies on CONFIG_HAS_IOMEM devm_ioremap_resource() is only guaranteed to be present if CONFIG_HAS_IOMEM is set. Reported-by: kernel test robot Link: https://lore.kernel.org/r/YCyauGyqxut69JNz@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 4 ++-- drivers/input/serio/Kconfig | 2 +- drivers/input/touchscreen/Kconfig | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 94eab82086b2..32d15809ae58 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -685,7 +685,7 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4+ keypad support" - depends on OF || ARCH_OMAP2PLUS + depends on (OF && HAS_IOMEM) || ARCH_OMAP2PLUS select INPUT_MATRIXKMAP help Say Y here if you want to use the OMAP4+ keypad. @@ -773,7 +773,7 @@ config KEYBOARD_CAP11XX config KEYBOARD_BCM tristate "Broadcom keypad driver" - depends on OF && HAVE_CLK + depends on OF && HAVE_CLK && HAS_IOMEM select INPUT_MATRIXKMAP default ARCH_BCM_CYGNUS help diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 0754744b9ce5..f39b7b3f7942 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -255,7 +255,7 @@ config SERIO_ARC_PS2 config SERIO_APBPS2 tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller" - depends on OF + depends on OF && HAS_IOMEM help Say Y here if you want support for GRLIB APBPS2 peripherals used to connect to PS/2 keyboard and/or mouse. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f012fe746df0..d1b6ef9fd8d4 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -607,7 +607,7 @@ config TOUCHSCREEN_MTOUCH config TOUCHSCREEN_IMX6UL_TSC tristate "Freescale i.MX6UL touchscreen controller" - depends on (OF && GPIOLIB) || COMPILE_TEST + depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM help Say Y here if you have a Freescale i.MX6UL, and want to use the internal touchscreen controller. -- cgit v1.2.3-59-g8ed1b From 0ce1ac23149c6da939a5926c098c270c58c317a0 Mon Sep 17 00:00:00 2001 From: Ronald Tschalär Date: Fri, 19 Feb 2021 11:10:51 -0800 Subject: Input: applespi - don't wait for responses to commands indefinitely. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The response to a command may never arrive or it may be corrupted (and hence dropped) for some reason. While exceedingly rare, when it did happen it blocked all further commands. One way to fix this was to do a suspend/resume. However, recovering automatically seems like a nicer option. Hence this puts a time limit (1 sec) on how long we're willing to wait for a response, after which we assume it got lost. Signed-off-by: Ronald Tschalär Link: https://lore.kernel.org/r/20210217190718.11035-1-ronald@innovation.ch Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/applespi.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c index d22223154177..27e87c45edf2 100644 --- a/drivers/input/keyboard/applespi.c +++ b/drivers/input/keyboard/applespi.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -409,7 +410,7 @@ struct applespi_data { unsigned int cmd_msg_cntr; /* lock to protect the above parameters and flags below */ spinlock_t cmd_msg_lock; - bool cmd_msg_queued; + ktime_t cmd_msg_queued; enum applespi_evt_type cmd_evt_type; struct led_classdev backlight_info; @@ -729,7 +730,7 @@ static void applespi_msg_complete(struct applespi_data *applespi, wake_up_all(&applespi->drain_complete); if (is_write_msg) { - applespi->cmd_msg_queued = false; + applespi->cmd_msg_queued = 0; applespi_send_cmd_msg(applespi); } @@ -771,8 +772,16 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi) return 0; /* check whether send is in progress */ - if (applespi->cmd_msg_queued) - return 0; + if (applespi->cmd_msg_queued) { + if (ktime_ms_delta(ktime_get(), applespi->cmd_msg_queued) < 1000) + return 0; + + dev_warn(&applespi->spi->dev, "Command %d timed out\n", + applespi->cmd_evt_type); + + applespi->cmd_msg_queued = 0; + applespi->write_active = false; + } /* set up packet */ memset(packet, 0, APPLESPI_PACKET_SIZE); @@ -869,7 +878,7 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi) return sts; } - applespi->cmd_msg_queued = true; + applespi->cmd_msg_queued = ktime_get_coarse(); applespi->write_active = true; return 0; @@ -1921,7 +1930,7 @@ static int __maybe_unused applespi_resume(struct device *dev) applespi->drain = false; applespi->have_cl_led_on = false; applespi->have_bl_level = 0; - applespi->cmd_msg_queued = false; + applespi->cmd_msg_queued = 0; applespi->read_active = false; applespi->write_active = false; -- cgit v1.2.3-59-g8ed1b From e64123949e6c9581c97fc14594f1cf34bf1d87a8 Mon Sep 17 00:00:00 2001 From: Ronald Tschalär Date: Fri, 19 Feb 2021 11:12:00 -0800 Subject: Input: applespi - fix occasional crc errors under load. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason, when the system is under heavy CPU load, the read following the write sometimes occurs unusually quickly, resulting in the read data not being quite ready and hence a bad packet getting read. Adding another delay after reading the status message appears to fix this. Signed-off-by: Ronald Tschalär Link: https://lore.kernel.org/r/20210217190718.11035-2-ronald@innovation.ch Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/applespi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c index 27e87c45edf2..eda1b23002b5 100644 --- a/drivers/input/keyboard/applespi.c +++ b/drivers/input/keyboard/applespi.c @@ -749,6 +749,8 @@ static void applespi_async_write_complete(void *context) applespi->tx_status, APPLESPI_STATUS_SIZE); + udelay(SPI_RW_CHG_DELAY_US); + if (!applespi_check_write_status(applespi, applespi->wr_m.status)) { /* * If we got an error, we presumably won't get the expected -- cgit v1.2.3-59-g8ed1b From 820c8727956da82b7a841c299fabb2fdca9a37d4 Mon Sep 17 00:00:00 2001 From: Philip Chen Date: Mon, 22 Feb 2021 21:05:04 -0800 Subject: Input: cros-ec-keyb - expose function row physical map to userspace The top-row keys in a keyboard usually have dual functionalities. E.g. A function key "F1" is also an action key "Browser back". Therefore, when an application receives an action key code from a top-row key press, the application needs to know how to correlate the action key code with the function key code and do the conversion whenever necessary. Since the userpace already knows the key scanlines (row/column) associated with a received key code. Essentially, the userspace only needs a mapping between the key row/column and the matching physical location in the top row. So, enhance the cros-ec-keyb driver to create such a mapping and expose it to userspace in the form of a function_row_physmap attribute. The attribute would be a space separated ordered list of row/column codes for the keys in the function row, in a left-to-right order. The attribute will only be present when the device has a custom design for the top-row keys. Signed-off-by: Philip Chen Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20210115122412.v7.2.I6542d7d9d0b246e7079bb16b41e697b2ac4b4e39@changeid Signed-off-by: Dmitry Torokhov --- .../ABI/testing/sysfs-driver-input-cros-ec-keyb | 6 ++ drivers/input/keyboard/cros_ec_keyb.c | 79 ++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb (limited to 'drivers/input/keyboard') diff --git a/Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb b/Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb new file mode 100644 index 000000000000..c7afc2328045 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb @@ -0,0 +1,6 @@ +What: /sys/class/input/input(x)/device/function_row_physmap +Date: January 2021 +Contact: Philip Chen +Description: A space separated list of scancodes for the top row keys, + ordered by the physical positions of the keys, from left + to right. diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index b379ed762878..38457d9641bd 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -27,6 +27,8 @@ #include +#define MAX_NUM_TOP_ROW_KEYS 15 + /** * struct cros_ec_keyb - Structure representing EC keyboard device * @@ -42,6 +44,9 @@ * @idev: The input device for the matrix keys. * @bs_idev: The input device for non-matrix buttons and switches (or NULL). * @notifier: interrupt event notifier for transport devices + * @function_row_physmap: An array of the encoded rows/columns for the top + * row function keys, in an order from left to right + * @num_function_row_keys: The number of top row keys in a custom keyboard */ struct cros_ec_keyb { unsigned int rows; @@ -58,6 +63,9 @@ struct cros_ec_keyb { struct input_dev *idev; struct input_dev *bs_idev; struct notifier_block notifier; + + u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS]; + size_t num_function_row_keys; }; /** @@ -527,6 +535,11 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) struct input_dev *idev; const char *phys; int err; + struct property *prop; + const __be32 *p; + u16 *physmap; + u32 key_pos; + int row, col; err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); if (err) @@ -578,6 +591,21 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) ckdev->idev = idev; cros_ec_keyb_compute_valid_keys(ckdev); + physmap = ckdev->function_row_physmap; + of_property_for_each_u32(dev->of_node, "function-row-physmap", + prop, p, key_pos) { + if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) { + dev_warn(dev, "Only support up to %d top row keys\n", + MAX_NUM_TOP_ROW_KEYS); + break; + } + row = KEY_ROW(key_pos); + col = KEY_COL(key_pos); + *physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + physmap++; + ckdev->num_function_row_keys++; + } + err = input_register_device(ckdev->idev); if (err) { dev_err(dev, "cannot register input device\n"); @@ -587,6 +615,51 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) return 0; } +static ssize_t function_row_physmap_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t size = 0; + int i; + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + u16 *physmap = ckdev->function_row_physmap; + + for (i = 0; i < ckdev->num_function_row_keys; i++) + size += scnprintf(buf + size, PAGE_SIZE - size, + "%s%02X", size ? " " : "", physmap[i]); + if (size) + size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); + + return size; +} + +static DEVICE_ATTR_RO(function_row_physmap); + +static struct attribute *cros_ec_keyb_attrs[] = { + &dev_attr_function_row_physmap.attr, + NULL, +}; + +static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj, + struct attribute *attr, + int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + + if (attr == &dev_attr_function_row_physmap.attr && + !ckdev->num_function_row_keys) + return 0; + + return attr->mode; +} + +static const struct attribute_group cros_ec_keyb_attr_group = { + .is_visible = cros_ec_keyb_attr_is_visible, + .attrs = cros_ec_keyb_attrs, +}; + + static int cros_ec_keyb_probe(struct platform_device *pdev) { struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); @@ -617,6 +690,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) return err; } + err = devm_device_add_group(dev, &cros_ec_keyb_attr_group); + if (err) { + dev_err(dev, "failed to create attributes. err=%d\n", err); + return err; + } + ckdev->notifier.notifier_call = cros_ec_keyb_work; err = blocking_notifier_chain_register(&ckdev->ec->event_notifier, &ckdev->notifier); -- cgit v1.2.3-59-g8ed1b