From dced35aeb0367dda2636ee9ee914bda14510dcc9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Mar 2011 17:49:12 +0200 Subject: drivers: Final irq namespace conversion Scripted with coccinelle. Signed-off-by: Thomas Gleixner --- drivers/input/keyboard/lm8323.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index b732870ecc89..71f744a8e686 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -809,7 +809,7 @@ static int lm8323_suspend(struct device *dev) struct lm8323_chip *lm = i2c_get_clientdata(client); int i; - set_irq_wake(client->irq, 0); + irq_set_irq_wake(client->irq, 0); disable_irq(client->irq); mutex_lock(&lm->lock); @@ -838,7 +838,7 @@ static int lm8323_resume(struct device *dev) led_classdev_resume(&lm->pwm[i].cdev); enable_irq(client->irq); - set_irq_wake(client->irq, 1); + irq_set_irq_wake(client->irq, 1); return 0; } -- cgit v1.2.2 From 25985edcedea6396277003854657b5f3cb31a628 Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Wed, 30 Mar 2011 22:57:33 -0300 Subject: Fix common misspellings Fixes generated by 'codespell' and manually reviewed. Signed-off-by: Lucas De Marchi --- drivers/input/keyboard/davinci_keyscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index a91ee941b5c1..cd89d17162a3 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -5,7 +5,7 @@ * * Author: Miguel Aguilar * - * Intial Code: Sandeep Paulraj + * Initial Code: Sandeep Paulraj * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit v1.2.2 From 92a47674f57b4a84a43ce93b0dfdb596c0543749 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 11 Apr 2011 23:34:37 -0700 Subject: Input: gpio_keys - add support for EV_ABS With this patch you can setup a group of GPIOs representing a specific position on an EV_ABS axis. Signed-off-by: Alexander Stein Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index eb3006361ee4..73e58a96ab99 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -324,7 +324,12 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; - input_event(input, type, button->code, !!state); + if (type == EV_ABS) { + if (state) + input_event(input, type, button->code, button->value); + } else { + input_event(input, type, button->code, !!state); + } input_sync(input); } @@ -363,7 +368,7 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata, struct gpio_keys_button *button) { - char *desc = button->desc ? button->desc : "gpio_keys"; + const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; unsigned long irqflags; int irq, error; -- cgit v1.2.2 From 467112777c462a592c27338eeea5d1a320e82b5f Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 11 Apr 2011 23:34:48 -0700 Subject: Input: gpio-keys - add support for setting device name This patch allows to set a device name which helps distinguishing several gpio-keys devices. Signed-off-by: Alexander Stein Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 73e58a96ab99..6e6145b9a4c1 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -473,7 +473,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); - input->name = pdev->name; + input->name = pdata->name ? : pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; input->open = gpio_keys_open; -- cgit v1.2.2 From 94bb530c247a29f75fc728e5f8374a83d59d7e45 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 27 Apr 2011 21:39:20 -0700 Subject: Input: qt1070 - add MODULE_DEVICE_TABLE Adding the necessary MODULE_DEVICE_TABLE() information allows the driver to be automatically loaded by udev Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/qt1070.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index fba8404c7297..ca7b89196ab7 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -248,6 +248,7 @@ static const struct i2c_device_id qt1070_id[] = { { "qt1070", 0 }, { }, }; +MODULE_DEVICE_TABLE(i2c, qt1070_id); static struct i2c_driver qt1070_driver = { .driver = { -- cgit v1.2.2 From 60c8ba6478bb483ecd6069f566d02e33d319e72e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 27 Apr 2011 21:39:57 -0700 Subject: Input: omap-keypad - fix a wrong free_irq() parameter In current implementation, free_irq() was using NULL rather than the driver data as the data pointer so free_irq() wouldn't have matched. Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap-keypad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 0e2a19cb43d8..f23a743817db 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -413,7 +413,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) return 0; err5: for (i = irq_idx - 1; i >=0; i--) - free_irq(row_gpios[i], NULL); + free_irq(row_gpios[i], omap_kp); err4: input_unregister_device(omap_kp->input); input_dev = NULL; @@ -444,11 +444,11 @@ static int __devexit omap_kp_remove(struct platform_device *pdev) gpio_free(col_gpios[i]); for (i = 0; i < omap_kp->rows; i++) { gpio_free(row_gpios[i]); - free_irq(gpio_to_irq(row_gpios[i]), NULL); + free_irq(gpio_to_irq(row_gpios[i]), omap_kp); } } else { omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - free_irq(omap_kp->irq, NULL); + free_irq(omap_kp->irq, omap_kp); } del_timer_sync(&omap_kp->timer); -- cgit v1.2.2 From 34abeeb23575c9c25b8c582d582e5bcfcd1cf338 Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Wed, 27 Apr 2011 23:18:15 -0700 Subject: Input: tegra-kbc - add ghost key filter Add ghost key filtering support for the Nvidia Tegra matrix keyboard. Signed-off-by: Rakesh Iyer Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 99ce9032d08c..5fb4b972f205 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -72,6 +72,7 @@ struct tegra_kbc { unsigned int repoll_dly; unsigned long cp_dly_jiffies; bool use_fn_map; + bool use_ghost_filter; const struct tegra_kbc_platform_data *pdata; unsigned short keycode[KBC_MAX_KEY * 2]; unsigned short current_keys[KBC_MAX_KPENT]; @@ -260,6 +261,8 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) unsigned int num_down = 0; unsigned long flags; bool fn_keypress = false; + bool key_in_same_row = false; + bool key_in_same_col = false; spin_lock_irqsave(&kbc->lock, flags); for (i = 0; i < KBC_MAX_KPENT; i++) { @@ -284,6 +287,34 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) val >>= 8; } + /* + * Matrix keyboard designs are prone to keyboard ghosting. + * Ghosting occurs if there are 3 keys such that - + * any 2 of the 3 keys share a row, and any 2 of them share a column. + * If so ignore the key presses for this iteration. + */ + if ((kbc->use_ghost_filter) && (num_down >= 3)) { + for (i = 0; i < num_down; i++) { + unsigned int j; + u8 curr_col = scancodes[i] & 0x07; + u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT; + + /* + * Find 2 keys such that one key is in the same row + * and the other is in the same column as the i-th key. + */ + for (j = i + 1; j < num_down; j++) { + u8 col = scancodes[j] & 0x07; + u8 row = scancodes[j] >> KBC_ROW_SHIFT; + + if (col == curr_col) + key_in_same_col = true; + if (row == curr_row) + key_in_same_row = true; + } + } + } + /* * If the platform uses Fn keymaps, translate keys on a Fn keypress. * Function keycodes are KBC_MAX_KEY apart from the plain keycodes. @@ -297,6 +328,10 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) spin_unlock_irqrestore(&kbc->lock, flags); + /* Ignore the key presses for this iteration? */ + if (key_in_same_col && key_in_same_row) + return; + tegra_kbc_report_released_keys(kbc->idev, kbc->current_keys, kbc->num_pressed_keys, keycodes, num_down); @@ -652,6 +687,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) input_dev->keycodemax *= 2; kbc->use_fn_map = pdata->use_fn_map; + kbc->use_ghost_filter = pdata->use_ghost_filter; keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, input_dev->keycode, input_dev->keybit); -- cgit v1.2.2 From b6d2a3e6ff6a7019b4a240b6bc4a4d74ce82d533 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 28 Apr 2011 09:32:44 -0700 Subject: Input: sh_keysc - implement runtime PM support Convert the KEYSC driver from MSTP bit control through a magic clock string to making use of Runtime PM. Also, update the system suspend/resume callbacks to drop the Runtime PM reference in the case of wakeup disabled. Signed-off-by: Magnus Damm Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/sh_keysc.c | 40 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 25 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index d7dafd9425b6..429ac2b77438 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -37,7 +37,6 @@ static const struct { struct sh_keysc_priv { void __iomem *iomem_base; - struct clk *clk; DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS); struct input_dev *input; struct sh_keysc_info pdata; @@ -169,7 +168,6 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) struct sh_keysc_info *pdata; struct resource *res; struct input_dev *input; - char clk_name[8]; int i; int irq, error; @@ -210,19 +208,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) goto err1; } - snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id); - priv->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - error = PTR_ERR(priv->clk); - goto err2; - } - priv->input = input_allocate_device(); if (!priv->input) { dev_err(&pdev->dev, "failed to allocate input device\n"); error = -ENOMEM; - goto err3; + goto err2; } input = priv->input; @@ -244,7 +234,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto err4; + goto err3; } for (i = 0; i < SH_KEYSC_MAXKEYS; i++) @@ -254,10 +244,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { dev_err(&pdev->dev, "failed to register input device\n"); - goto err5; + goto err4; } - clk_enable(priv->clk); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) | pdata->scan_timing); @@ -267,12 +258,10 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) return 0; - err5: - free_irq(irq, pdev); err4: - input_free_device(input); + free_irq(irq, pdev); err3: - clk_put(priv->clk); + input_free_device(input); err2: iounmap(priv->iomem_base); err1: @@ -292,8 +281,8 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), pdev); iounmap(priv->iomem_base); - clk_disable(priv->clk); - clk_put(priv->clk); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); platform_set_drvdata(pdev, NULL); kfree(priv); @@ -311,14 +300,13 @@ static int sh_keysc_suspend(struct device *dev) value = sh_keysc_read(priv, KYCR1); if (device_may_wakeup(dev)) { - value |= 0x80; + sh_keysc_write(priv, KYCR1, value | 0x80); enable_irq_wake(irq); } else { - value &= ~0x80; + sh_keysc_write(priv, KYCR1, value & ~0x80); + pm_runtime_put_sync(dev); } - sh_keysc_write(priv, KYCR1, value); - return 0; } @@ -329,6 +317,8 @@ static int sh_keysc_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); + else + pm_runtime_get_sync(dev); return 0; } -- cgit v1.2.2 From 2dfb9a85ecb86f00b085d6fca7d4bb8dc6764710 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 28 Apr 2011 09:34:32 -0700 Subject: Input: sh_keysc - switch to threaded IRQ handler Update the KEYSC driver to make use of threaded IRQs with IRQF_ONESHOT to improve interrupt latency. The driver is using udelay() in the ISR to make sure the hardware has stabilized. Without using interrupt threads this delay may affect the latency of the rest of the system. Also pass dev_name() as string for /proc/interrupts. Signed-off-by: Magnus Damm Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/sh_keysc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 429ac2b77438..66eea50fccf5 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -231,7 +231,8 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) input->keycodesize = sizeof(pdata->keycodes[0]); input->keycodemax = ARRAY_SIZE(pdata->keycodes); - error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); + error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT, + dev_name(&pdev->dev), pdev); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); goto err3; -- cgit v1.2.2 From 12d0cef701775f7f52667c33e57a5fe61fa49bd5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 2 May 2011 23:09:19 -0700 Subject: Input: sh_keysc - make sh_keysc_device_driver static This variable is not used outside of the module so we should mark it as static. Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/sh_keysc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 66eea50fccf5..cb4f258477ce 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -329,7 +329,7 @@ static const struct dev_pm_ops sh_keysc_dev_pm_ops = { .resume = sh_keysc_resume, }; -struct platform_driver sh_keysc_device_driver = { +static struct platform_driver sh_keysc_device_driver = { .probe = sh_keysc_probe, .remove = __devexit_p(sh_keysc_remove), .driver = { -- cgit v1.2.2 From 9cc7c80b57c66d08e67f297169783d1f07288066 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 4 May 2011 20:54:27 -0700 Subject: Input: sh_keysc - only compile PM code if PM is enabled Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/sh_keysc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index cb4f258477ce..834cf98e7efb 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -291,6 +291,7 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev) return 0; } +#if CONFIG_PM_SLEEP static int sh_keysc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -323,11 +324,10 @@ static int sh_keysc_resume(struct device *dev) return 0; } +#endif -static const struct dev_pm_ops sh_keysc_dev_pm_ops = { - .suspend = sh_keysc_suspend, - .resume = sh_keysc_resume, -}; +static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops, + sh_keysc_suspend, sh_keysc_resume); static struct platform_driver sh_keysc_device_driver = { .probe = sh_keysc_probe, -- cgit v1.2.2 From baafb435372dd48e3854028aec669effae957c9e Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Wed, 11 May 2011 14:24:10 -0700 Subject: Input: tegra-kbc - change wakeup logic to be all or nothing Tegra hardware design cannot reliably support an arbitrary set of keys waking up the system. Modify wakeup logic so either any key wakes the system up or none will do. Signed-off-by: Rakesh Iyer Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 5fb4b972f205..2b3b73ec6689 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -66,8 +66,6 @@ struct tegra_kbc { void __iomem *mmio; struct input_dev *idev; unsigned int irq; - unsigned int wake_enable_rows; - unsigned int wake_enable_cols; spinlock_t lock; unsigned int repoll_dly; unsigned long cp_dly_jiffies; @@ -418,21 +416,11 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) int i; unsigned int rst_val; - BUG_ON(pdata->wake_cnt > KBC_MAX_KEY); - rst_val = (filter && pdata->wake_cnt) ? ~0 : 0; + /* Either mask all keys or none. */ + rst_val = (filter && !pdata->wakeup) ? ~0 : 0; for (i = 0; i < KBC_MAX_ROW; i++) writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); - - if (filter) { - for (i = 0; i < pdata->wake_cnt; i++) { - u32 val, addr; - addr = pdata->wake_cfg[i].row * 4 + KBC_ROW0_MASK_0; - val = readl(kbc->mmio + addr); - val &= ~(1 << pdata->wake_cfg[i].col); - writel(val, kbc->mmio + addr); - } - } } static void tegra_kbc_config_pins(struct tegra_kbc *kbc) @@ -594,7 +582,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) struct resource *res; int irq; int err; - int i; int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; @@ -651,13 +638,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) goto err_iounmap; } - kbc->wake_enable_rows = 0; - kbc->wake_enable_cols = 0; - for (i = 0; i < pdata->wake_cnt; i++) { - kbc->wake_enable_rows |= (1 << pdata->wake_cfg[i].row); - kbc->wake_enable_cols |= (1 << pdata->wake_cfg[i].col); - } - /* * The time delay between two consecutive reads of the FIFO is * the sum of the repeat time and the time taken for scanning -- cgit v1.2.2 From 36682b81de32090f5c9fac165e71ab8939aab8c0 Mon Sep 17 00:00:00 2001 From: Zhang Jiejing Date: Wed, 11 May 2011 14:32:01 -0700 Subject: Input: add driver FSL MPR121 capacitive touch sensor This patch adds basic support for Freescale MPR121 capacitive touch sensor. It's an i2c controller with up to 12 capacitance sensing inputs. Product information (data sheet, application notes) can be found here: http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPR121 Signed-off-by: Zhang Jiejing Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 12 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/mpr121_touchkey.c | 339 +++++++++++++++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 drivers/input/keyboard/mpr121_touchkey.c (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b16bed038f72..dec090ac7d31 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -325,6 +325,18 @@ config KEYBOARD_MCS To compile this driver as a module, choose M here: the module will be called mcs_touchkey. +config KEYBOARD_MPR121 + tristate "Freescale MPR121 Touchkey" + depends on I2C + help + Say Y here if you have Freescale MPR121 touchkey controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mpr121_touchkey. + config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 878e6c20deb0..b45361009f2e 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o +obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c new file mode 100644 index 000000000000..0a9e81194888 --- /dev/null +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -0,0 +1,339 @@ +/* + * Touchkey driver for Freescale MPR121 Controllor + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Zhang Jiejing + * + * Based on mcs_touchkey.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define ELE_TOUCH_STATUS_0_ADDR 0x0 +#define ELE_TOUCH_STATUS_1_ADDR 0X1 +#define MHD_RISING_ADDR 0x2b +#define NHD_RISING_ADDR 0x2c +#define NCL_RISING_ADDR 0x2d +#define FDL_RISING_ADDR 0x2e +#define MHD_FALLING_ADDR 0x2f +#define NHD_FALLING_ADDR 0x30 +#define NCL_FALLING_ADDR 0x31 +#define FDL_FALLING_ADDR 0x32 +#define ELE0_TOUCH_THRESHOLD_ADDR 0x41 +#define ELE0_RELEASE_THRESHOLD_ADDR 0x42 +#define AFE_CONF_ADDR 0x5c +#define FILTER_CONF_ADDR 0x5d + +/* + * ELECTRODE_CONF_ADDR: This register configures the number of + * enabled capacitance sensing inputs and its run/suspend mode. + */ +#define ELECTRODE_CONF_ADDR 0x5e +#define AUTO_CONFIG_CTRL_ADDR 0x7b +#define AUTO_CONFIG_USL_ADDR 0x7d +#define AUTO_CONFIG_LSL_ADDR 0x7e +#define AUTO_CONFIG_TL_ADDR 0x7f + +/* Threshold of touch/release trigger */ +#define TOUCH_THRESHOLD 0x0f +#define RELEASE_THRESHOLD 0x0a +/* Masks for touch and release triggers */ +#define TOUCH_STATUS_MASK 0xfff +/* MPR121 has 12 keys */ +#define MPR121_MAX_KEY_COUNT 12 + +struct mpr121_touchkey { + struct i2c_client *client; + struct input_dev *input_dev; + unsigned int key_val; + unsigned int statusbits; + unsigned int keycount; + u16 keycodes[MPR121_MAX_KEY_COUNT]; +}; + +struct mpr121_init_register { + int addr; + u8 val; +}; + +static const struct mpr121_init_register init_reg_table[] __devinitconst = { + { MHD_RISING_ADDR, 0x1 }, + { NHD_RISING_ADDR, 0x1 }, + { MHD_FALLING_ADDR, 0x1 }, + { NHD_FALLING_ADDR, 0x1 }, + { NCL_FALLING_ADDR, 0xff }, + { FDL_FALLING_ADDR, 0x02 }, + { FILTER_CONF_ADDR, 0x04 }, + { AFE_CONF_ADDR, 0x0b }, + { AUTO_CONFIG_CTRL_ADDR, 0x0b }, +}; + +static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) +{ + struct mpr121_touchkey *mpr121 = dev_id; + struct i2c_client *client = mpr121->client; + struct input_dev *input = mpr121->input_dev; + unsigned int key_num, key_val, pressed; + int reg; + + reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg <<= 8; + reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg &= TOUCH_STATUS_MASK; + /* use old press bit to figure out which bit changed */ + key_num = ffs(reg ^ mpr121->statusbits) - 1; + pressed = reg & (1 << key_num); + mpr121->statusbits = reg; + + key_val = mpr121->keycodes[key_num]; + + input_event(input, EV_MSC, MSC_SCAN, key_num); + input_report_key(input, key_val, pressed); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, + pressed ? "pressed" : "released"); + +out: + return IRQ_HANDLED; +} + +static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata, + struct mpr121_touchkey *mpr121, + struct i2c_client *client) +{ + const struct mpr121_init_register *reg; + unsigned char usl, lsl, tl; + int i, t, vdd, ret; + + /* Set up touch/release threshold for ele0-ele11 */ + for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) { + t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2); + ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, t + 1, + RELEASE_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + } + + /* Set up init register */ + for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) { + reg = &init_reg_table[i]; + ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val); + if (ret < 0) + goto err_i2c_write; + } + + + /* + * Capacitance on sensing input varies and needs to be compensated. + * The internal MPR121-auto-configuration can do this if it's + * registers are set properly (based on pdata->vdd_uv). + */ + vdd = pdata->vdd_uv / 1000; + usl = ((vdd - 700) * 256) / vdd; + lsl = (usl * 65) / 100; + tl = (usl * 90) / 100; + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl); + ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + mpr121->keycount); + if (ret != 0) + goto err_i2c_write; + + dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount); + + return 0; + +err_i2c_write: + dev_err(&client->dev, "i2c write error: %d\n", ret); + return ret; +} + +static int __devinit mpr_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mpr121_platform_data *pdata = client->dev.platform_data; + struct mpr121_touchkey *mpr121; + struct input_dev *input_dev; + int error; + int i; + + if (!pdata) { + dev_err(&client->dev, "no platform data defined\n"); + return -EINVAL; + } + + if (!pdata->keymap || !pdata->keymap_size) { + dev_err(&client->dev, "missing keymap data\n"); + return -EINVAL; + } + + if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) { + dev_err(&client->dev, "too many keys defined\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, "irq number should not be zero\n"); + return -EINVAL; + } + + mpr121 = kzalloc(sizeof(struct mpr121_touchkey), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!mpr121 || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + mpr121->client = client; + mpr121->input_dev = input_dev; + mpr121->keycount = pdata->keymap_size; + + input_dev->name = "Freescale MPR121 Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + + input_dev->keycode = mpr121->keycodes; + input_dev->keycodesize = sizeof(mpr121->keycodes[0]); + input_dev->keycodemax = mpr121->keycount; + + for (i = 0; i < pdata->keymap_size; i++) { + input_set_capability(input_dev, EV_KEY, pdata->keymap[i]); + mpr121->keycodes[i] = pdata->keymap[i]; + } + + error = mpr121_phys_init(pdata, mpr121, client); + if (error) { + dev_err(&client->dev, "Failed to init register\n"); + goto err_free_mem; + } + + error = request_threaded_irq(client->irq, NULL, + mpr_touchkey_interrupt, + IRQF_TRIGGER_FALLING, + client->dev.driver->name, mpr121); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, mpr121); + device_init_wakeup(&client->dev, pdata->wakeup); + + return 0; + +err_free_irq: + free_irq(client->irq, mpr121); +err_free_mem: + input_free_device(input_dev); + kfree(mpr121); + return error; +} + +static int __devexit mpr_touchkey_remove(struct i2c_client *client) +{ + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); + + free_irq(client->irq, mpr121); + input_unregister_device(mpr121->input_dev); + kfree(mpr121); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mpr_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + + return 0; +} + +static int mpr_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + mpr121->keycount); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume); + +static const struct i2c_device_id mpr121_id[] = { + { "mpr121_touchkey", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpr121_id); + +static struct i2c_driver mpr_touchkey_driver = { + .driver = { + .name = "mpr121", + .owner = THIS_MODULE, + .pm = &mpr121_touchkey_pm_ops, + }, + .id_table = mpr121_id, + .probe = mpr_touchkey_probe, + .remove = __devexit_p(mpr_touchkey_remove), +}; + +static int __init mpr_touchkey_init(void) +{ + return i2c_add_driver(&mpr_touchkey_driver); +} +module_init(mpr_touchkey_init); + +static void __exit mpr_touchkey_exit(void) +{ + i2c_del_driver(&mpr_touchkey_driver); +} +module_exit(mpr_touchkey_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhang Jiejing "); +MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip"); -- cgit v1.2.2 From 9d2e173644bb5c42ff1b280fbdda3f195a7cf1f7 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Thu, 19 May 2011 00:59:32 -0700 Subject: Input: ADP5589 - new driver for I2C Keypad Decoder and I/O Expander From http://www.analog.com/ADP5589: The ADP5589 is an I/O port expander and keypad matrix decoder designed for QWERTY type phones that require a large keypad matrix and expanded I/O lines. Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/adp5589-keys.c | 771 ++++++++++++++++++++++++++++++++++ 3 files changed, 782 insertions(+) create mode 100644 drivers/input/keyboard/adp5589-keys.c (limited to 'drivers/input/keyboard') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index dec090ac7d31..69badb4e06aa 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -32,6 +32,16 @@ config KEYBOARD_ADP5588 To compile this driver as a module, choose M here: the module will be called adp5588-keys. +config KEYBOARD_ADP5589 + tristate "ADP5589 I2C QWERTY Keypad and IO Expander" + depends on I2C + help + Say Y here if you want to use a ADP5589 attached to your + system I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adp5589-keys. + config KEYBOARD_AMIGA tristate "Amiga keyboard" depends on AMIGA diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index b45361009f2e..c49cf8e04cd7 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o +obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c new file mode 100644 index 000000000000..631598663aab --- /dev/null +++ b/drivers/input/keyboard/adp5589-keys.c @@ -0,0 +1,771 @@ +/* + * Description: keypad driver for ADP5589 + * I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2010-2011 Analog Devices Inc. + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* GENERAL_CFG Register */ +#define OSC_EN (1 << 7) +#define CORE_CLK(x) (((x) & 0x3) << 5) +#define LCK_TRK_LOGIC (1 << 4) +#define LCK_TRK_GPI (1 << 3) +#define INT_CFG (1 << 1) +#define RST_CFG (1 << 0) + +/* INT_EN Register */ +#define LOGIC2_IEN (1 << 5) +#define LOGIC1_IEN (1 << 4) +#define LOCK_IEN (1 << 3) +#define OVRFLOW_IEN (1 << 2) +#define GPI_IEN (1 << 1) +#define EVENT_IEN (1 << 0) + +/* Interrupt Status Register */ +#define LOGIC2_INT (1 << 5) +#define LOGIC1_INT (1 << 4) +#define LOCK_INT (1 << 3) +#define OVRFLOW_INT (1 << 2) +#define GPI_INT (1 << 1) +#define EVENT_INT (1 << 0) + +/* STATUS Register */ + +#define LOGIC2_STAT (1 << 7) +#define LOGIC1_STAT (1 << 6) +#define LOCK_STAT (1 << 5) +#define KEC 0xF + +/* PIN_CONFIG_D Register */ +#define C4_EXTEND_CFG (1 << 6) /* RESET2 */ +#define R4_EXTEND_CFG (1 << 5) /* RESET1 */ + +/* LOCK_CFG */ +#define LOCK_EN (1 << 0) + +#define PTIME_MASK 0x3 +#define LTIME_MASK 0x3 + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KEYP_MAX_EVENT 16 + +#define MAXGPIO 19 +#define ADP_BANK(offs) ((offs) >> 3) +#define ADP_BIT(offs) (1u << ((offs) & 0x7)) + +struct adp5589_kpad { + struct i2c_client *client; + struct input_dev *input; + unsigned short keycode[ADP5589_KEYMAPSIZE]; + const struct adp5589_gpi_map *gpimap; + unsigned short gpimapsize; + unsigned extend_cfg; +#ifdef CONFIG_GPIOLIB + unsigned char gpiomap[MAXGPIO]; + bool export_gpio; + struct gpio_chip gc; + struct mutex gpio_lock; /* Protect cached dir, dat_out */ + u8 dat_out[3]; + u8 dir[3]; +#endif +}; + +static int adp5589_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5589_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +#ifdef CONFIG_GPIOLIB +static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + return !!(adp5589_read(kpad->client, ADP5589_GPI_STATUS_A + bank) & + bit); +} + +static void adp5589_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + mutex_lock(&kpad->gpio_lock); + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank, + kpad->dat_out[bank]); + + mutex_unlock(&kpad->gpio_lock); +} + +static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] &= ~bit; + ret = adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5589_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] |= bit; + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + ret = adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank, + kpad->dat_out[bank]); + ret |= adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int __devinit adp5589_build_gpiomap(struct adp5589_kpad *kpad, + const struct adp5589_kpad_platform_data *pdata) +{ + bool pin_used[MAXGPIO]; + int n_unused = 0; + int i; + + memset(pin_used, false, sizeof(pin_used)); + + for (i = 0; i < MAXGPIO; i++) + if (pdata->keypad_en_mask & (1 << i)) + pin_used[i] = true; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - ADP5589_GPI_PIN_BASE] = true; + + if (kpad->extend_cfg & R4_EXTEND_CFG) + pin_used[4] = true; + + if (kpad->extend_cfg & C4_EXTEND_CFG) + pin_used[12] = true; + + for (i = 0; i < MAXGPIO; i++) + if (!pin_used[i]) + kpad->gpiomap[n_unused++] = i; + + return n_unused; +} + +static int __devinit adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, error; + + if (!gpio_data) + return 0; + + kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata); + if (kpad->gc.ngpio == 0) { + dev_info(dev, "No unused gpios left to export\n"); + return 0; + } + + kpad->export_gpio = true; + + kpad->gc.direction_input = adp5589_gpio_direction_input; + kpad->gc.direction_output = adp5589_gpio_direction_output; + kpad->gc.get = adp5589_gpio_get_value; + kpad->gc.set = adp5589_gpio_set_value; + kpad->gc.can_sleep = 1; + + kpad->gc.base = gpio_data->gpio_start; + kpad->gc.label = kpad->client->name; + kpad->gc.owner = THIS_MODULE; + + mutex_init(&kpad->gpio_lock); + + error = gpiochip_add(&kpad->gc); + if (error) { + dev_err(dev, "gpiochip_add failed, err: %d\n", error); + return error; + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + kpad->dat_out[i] = adp5589_read(kpad->client, + ADP5589_GPO_DATA_OUT_A + i); + kpad->dir[i] = adp5589_read(kpad->client, + ADP5589_GPIO_DIRECTION_A + i); + } + + if (gpio_data->setup) { + error = gpio_data->setup(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "setup failed, %d\n", error); + } + + return 0; +} + +static void __devexit adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + if (!kpad->export_gpio) + return; + + if (gpio_data->teardown) { + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "teardown failed %d\n", error); + } + + error = gpiochip_remove(&kpad->gc); + if (error) + dev_warn(dev, "gpiochip_remove failed %d\n", error); +} +#else +static inline int adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + return 0; +} + +static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ +} +#endif + +static void adp5589_report_switches(struct adp5589_kpad *kpad, + int key, int key_val) +{ + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + if (key_val == kpad->gpimap[i].pin) { + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + key & KEY_EV_PRESSED); + break; + } + } +} + +static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt) +{ + int i; + + for (i = 0; i < ev_cnt; i++) { + int key = adp5589_read(kpad->client, ADP5589_FIFO_1 + i); + int key_val = key & KEY_EV_MASK; + + if (key_val >= ADP5589_GPI_PIN_BASE && + key_val <= ADP5589_GPI_PIN_END) { + adp5589_report_switches(kpad, key, key_val); + } else { + input_report_key(kpad->input, + kpad->keycode[key_val - 1], + key & KEY_EV_PRESSED); + } + } +} + +static irqreturn_t adp5589_irq(int irq, void *handle) +{ + struct adp5589_kpad *kpad = handle; + struct i2c_client *client = kpad->client; + int status, ev_cnt; + + status = adp5589_read(client, ADP5589_INT_STATUS); + + if (status & OVRFLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & EVENT_INT) { + ev_cnt = adp5589_read(client, ADP5589_STATUS) & KEC; + if (ev_cnt) { + adp5589_report_events(kpad, ev_cnt); + input_sync(kpad->input); + } + } + + adp5589_write(client, ADP5589_INT_STATUS, status); /* Status is W1C */ + + return IRQ_HANDLED; +} + +static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key) +{ + int i; + + for (i = 0; i < ADP5589_KEYMAPSIZE; i++) + if (key == kpad->keycode[i]) + return (i + 1) | KEY_EV_PRESSED; + + dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n"); + + return -EINVAL; +} + +static int __devinit adp5589_setup(struct adp5589_kpad *kpad) +{ + struct i2c_client *client = kpad->client; + const struct adp5589_kpad_platform_data *pdata = + client->dev.platform_data; + int i, ret; + unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; + unsigned char pull_mask = 0; + + ret = adp5589_write(client, ADP5589_PIN_CONFIG_A, + pdata->keypad_en_mask & 0xFF); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_B, + (pdata->keypad_en_mask >> 8) & 0xFF); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C, + (pdata->keypad_en_mask >> 16) & 0xFF); + + if (pdata->en_keylock) { + ret |= adp5589_write(client, ADP5589_UNLOCK1, + pdata->unlock_key1); + ret |= adp5589_write(client, ADP5589_UNLOCK2, + pdata->unlock_key2); + ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS, + pdata->unlock_timer & LTIME_MASK); + ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5589_read(client, ADP5589_FIFO_1 + i); + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin <= ADP5589_GPI_PIN_ROW_END) { + evt_mode1 |= (1 << (pin - ADP5589_GPI_PIN_ROW_BASE)); + } else { + evt_mode2 |= + ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) & 0xFF); + evt_mode3 |= + ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) >> 8); + } + } + + if (pdata->gpimapsize) { + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_A, evt_mode1); + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_B, evt_mode2); + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_C, evt_mode3); + } + + if (pdata->pull_dis_mask & pdata->pullup_en_100k & + pdata->pullup_en_300k & pdata->pulldown_en_300k) + dev_warn(&client->dev, "Conflicting pull resistor config\n"); + + for (i = 0; i < MAXGPIO; i++) { + unsigned val = 0; + + if (pdata->pullup_en_300k & (1 << i)) + val = 0; + else if (pdata->pulldown_en_300k & (1 << i)) + val = 1; + else if (pdata->pullup_en_100k & (1 << i)) + val = 2; + else if (pdata->pull_dis_mask & (1 << i)) + val = 3; + + pull_mask |= val << (2 * (i & 0x3)); + + if ((i & 0x3) == 0x3 || i == MAXGPIO - 1) { + ret |= adp5589_write(client, + ADP5589_RPULL_CONFIG_A + (i >> 2), + pull_mask); + pull_mask = 0; + } + } + + if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) { + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_A, + adp5589_get_evcode(kpad, + pdata->reset1_key_1)); + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_B, + adp5589_get_evcode(kpad, + pdata->reset1_key_2)); + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_C, + adp5589_get_evcode(kpad, + pdata->reset1_key_3)); + kpad->extend_cfg |= R4_EXTEND_CFG; + } + + if (pdata->reset2_key_1 && pdata->reset2_key_2) { + ret |= adp5589_write(client, ADP5589_RESET2_EVENT_A, + adp5589_get_evcode(kpad, + pdata->reset2_key_1)); + ret |= adp5589_write(client, ADP5589_RESET2_EVENT_B, + adp5589_get_evcode(kpad, + pdata->reset2_key_2)); + kpad->extend_cfg |= C4_EXTEND_CFG; + } + + if (kpad->extend_cfg) { + ret |= adp5589_write(client, ADP5589_RESET_CFG, + pdata->reset_cfg); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_D, + kpad->extend_cfg); + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) + ret |= adp5589_write(client, ADP5589_DEBOUNCE_DIS_A + i, + pdata->debounce_dis_mask >> (i * 8)); + + ret |= adp5589_write(client, ADP5589_POLL_PTIME_CFG, + pdata->scan_cycle_time & PTIME_MASK); + ret |= adp5589_write(client, ADP5589_INT_STATUS, LOGIC2_INT | + LOGIC1_INT | OVRFLOW_INT | LOCK_INT | + GPI_INT | EVENT_INT); /* Status is W1C */ + + ret |= adp5589_write(client, ADP5589_GENERAL_CFG, + INT_CFG | OSC_EN | CORE_CLK(3)); + ret |= adp5589_write(client, ADP5589_INT_EN, + OVRFLOW_IEN | GPI_IEN | EVENT_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static void __devinit adp5589_report_switch_state(struct adp5589_kpad *kpad) +{ + int gpi_stat1 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_A); + int gpi_stat2 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_B); + int gpi_stat3 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_C); + int gpi_stat_tmp, pin_loc; + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + unsigned short pin = kpad->gpimap[i].pin; + + if (pin <= ADP5589_GPI_PIN_ROW_END) { + gpi_stat_tmp = gpi_stat1; + pin_loc = pin - ADP5589_GPI_PIN_ROW_BASE; + } else if ((pin - ADP5589_GPI_PIN_COL_BASE) < 8) { + gpi_stat_tmp = gpi_stat2; + pin_loc = pin - ADP5589_GPI_PIN_COL_BASE; + } else { + gpi_stat_tmp = gpi_stat3; + pin_loc = pin - ADP5589_GPI_PIN_COL_BASE - 8; + } + + if (gpi_stat_tmp < 0) { + dev_err(&kpad->client->dev, + "Can't read GPIO_DAT_STAT switch" + " %d default to OFF\n", pin); + gpi_stat_tmp = 0; + } + + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + !(gpi_stat_tmp & (1 << pin_loc))); + } + + input_sync(kpad->input); +} + +static int __devinit adp5589_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5589_kpad *kpad; + const struct adp5589_kpad_platform_data *pdata; + struct input_dev *input; + unsigned int revid; + int ret, i; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + if (!((pdata->keypad_en_mask & 0xFF) && + (pdata->keypad_en_mask >> 8)) || !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + if (pdata->keymapsize != ADP5589_KEYMAPSIZE) { + dev_err(&client->dev, "invalid keymapsize\n"); + return -EINVAL; + } + + if (!pdata->gpimap && pdata->gpimapsize) { + dev_err(&client->dev, "invalid gpimap from pdata\n"); + return -EINVAL; + } + + if (pdata->gpimapsize > ADP5589_GPIMAPSIZE_MAX) { + dev_err(&client->dev, "invalid gpimapsize\n"); + return -EINVAL; + } + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin < ADP5589_GPI_PIN_BASE || pin > ADP5589_GPI_PIN_END) { + dev_err(&client->dev, "invalid gpi pin data\n"); + return -EINVAL; + } + + if ((1 << (pin - ADP5589_GPI_PIN_ROW_BASE)) & + pdata->keypad_en_mask) { + dev_err(&client->dev, "invalid gpi row/col data\n"); + return -EINVAL; + } + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + return -EINVAL; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + input = input_allocate_device(); + if (!kpad || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + kpad->client = client; + kpad->input = input; + + ret = adp5589_read(client, ADP5589_ID); + if (ret < 0) { + error = ret; + goto err_free_mem; + } + + revid = (u8) ret & ADP5589_DEVICE_ID_MASK; + + input->name = client->name; + input->phys = "adp5589-keys/input0"; + input->dev.parent = &client->dev; + + input_set_drvdata(input, kpad); + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = revid; + + input->keycodesize = sizeof(kpad->keycode[0]); + input->keycodemax = pdata->keymapsize; + input->keycode = kpad->keycode; + + memcpy(kpad->keycode, pdata->keymap, + pdata->keymapsize * input->keycodesize); + + kpad->gpimap = pdata->gpimap; + kpad->gpimapsize = pdata->gpimapsize; + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + if (kpad->gpimapsize) + __set_bit(EV_SW, input->evbit); + for (i = 0; i < kpad->gpimapsize; i++) + __set_bit(kpad->gpimap[i].sw_evt, input->swbit); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, "unable to register input device\n"); + goto err_free_mem; + } + + error = request_threaded_irq(client->irq, NULL, adp5589_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, kpad); + if (error) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_unreg_dev; + } + + error = adp5589_setup(kpad); + if (error) + goto err_free_irq; + + if (kpad->gpimapsize) + adp5589_report_switch_state(kpad); + + error = adp5589_gpio_add(kpad); + if (error) + goto err_free_irq; + + device_init_wakeup(&client->dev, 1); + i2c_set_clientdata(client, kpad); + + dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); + return 0; + +err_free_irq: + free_irq(client->irq, kpad); +err_unreg_dev: + input_unregister_device(input); + input = NULL; +err_free_mem: + input_free_device(input); + kfree(kpad); + + return error; +} + +static int __devexit adp5589_remove(struct i2c_client *client) +{ + struct adp5589_kpad *kpad = i2c_get_clientdata(client); + + adp5589_write(client, ADP5589_GENERAL_CFG, 0); + free_irq(client->irq, kpad); + input_unregister_device(kpad->input); + adp5589_gpio_remove(kpad); + kfree(kpad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int adp5589_suspend(struct device *dev) +{ + struct adp5589_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + disable_irq(client->irq); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int adp5589_resume(struct device *dev) +{ + struct adp5589_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + enable_irq(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume); + +static const struct i2c_device_id adp5589_id[] = { + {"adp5589-keys", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, adp5589_id); + +static struct i2c_driver adp5589_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .pm = &adp5589_dev_pm_ops, + }, + .probe = adp5589_probe, + .remove = __devexit_p(adp5589_remove), + .id_table = adp5589_id, +}; + +static int __init adp5589_init(void) +{ + return i2c_add_driver(&adp5589_driver); +} +module_init(adp5589_init); + +static void __exit adp5589_exit(void) +{ + i2c_del_driver(&adp5589_driver); +} +module_exit(adp5589_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADP5589 Keypad driver"); -- cgit v1.2.2