aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-10-25 07:59:01 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-25 07:59:01 -0700
commit3a99c6319064af3f2e18eb929f638d555dbf7a62 (patch)
treee611927f41142123dc8efed7e07a3a91151edb01 /drivers/input/keyboard
parent1dfd166e93f98892aa4427069a23ed73259983c8 (diff)
parent49327ad2bbbaf1945d5ba431522201574219d150 (diff)
downloadkernel_samsung_tuna-3a99c6319064af3f2e18eb929f638d555dbf7a62.zip
kernel_samsung_tuna-3a99c6319064af3f2e18eb929f638d555dbf7a62.tar.gz
kernel_samsung_tuna-3a99c6319064af3f2e18eb929f638d555dbf7a62.tar.bz2
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (75 commits) Input: wacom - specify Cinitq supported tools Input: ab8500-ponkey - fix IRQ freeing in error path Input: adp5588-keys - use more obvious i2c_device_id name string Input: ad7877 - switch to using threaded IRQ Input: ad7877 - use attribute group to control visibility of attributes Input: serio - add support for PS2Mult multiplexer protocol Input: wacom - properly enable runtime PM Input: ad7877 - filter events where pressure is beyond the maximum Input: ad7877 - implement EV_KEY:BTN_TOUCH reporting Input: ad7877 - implement specified chip select behavior Input: hp680_ts_input - use cancel_delayed_work_sync() Input: mousedev - correct lockdep annotation Input: ads7846 - switch to using threaded IRQ Input: serio - support multiple child devices per single parent Input: synaptics - simplify pass-through port handling Input: add ROHM BU21013 touch panel controller support Input: omap4-keypad - wake-up on events & long presses Input: omap4-keypad - fix interrupt line configuration Input: omap4-keypad - SYSCONFIG register configuration Input: omap4-keypad - use platform device helpers ...
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r--drivers/input/keyboard/Kconfig19
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/adp5588-keys.c2
-rw-r--r--drivers/input/keyboard/hil_kbd.c2
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c408
-rw-r--r--drivers/input/keyboard/omap4-keypad.c318
-rw-r--r--drivers/input/keyboard/twl4030_keypad.c7
7 files changed, 753 insertions, 5 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index aa037fe..0426630 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
+config KEYBOARD_NOMADIK
+ tristate "ST-Ericsson Nomadik SKE keyboard"
+ depends on PLAT_NOMADIK
+ help
+ Say Y here if you want to use a keypad provided on the SKE controller
+ used on the Ux500 and Nomadik platforms
+
+ To compile this driver as a module, choose M here: the
+ module will be called nmk-ske-keypad.
+
config KEYBOARD_OPENCORES
tristate "OpenCores Keyboard Controller"
help
@@ -424,6 +434,15 @@ config KEYBOARD_OMAP
To compile this driver as a module, choose M here: the
module will be called omap-keypad.
+config KEYBOARD_OMAP4
+ tristate "TI OMAP4 keypad support"
+ depends on ARCH_OMAP4
+ help
+ Say Y here if you want to use the OMAP4 keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap4-keypad.
+
config KEYBOARD_TWL4030
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
depends on TWL4030_CORE
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 504b591..c13809c 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -28,7 +28,9 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
index d6918cb..b92d1cd 100644
--- a/drivers/input/keyboard/adp5588-keys.c
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = {
#endif
static const struct i2c_device_id adp5588_id[] = {
- { KBUILD_MODNAME, 0 },
+ { "adp5588-keys", 0 },
{ "adp5587-keys", 0 },
{ }
};
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index 19fa94a..fed31e0 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -570,6 +570,8 @@ static struct serio_device_id hil_dev_ids[] = {
{ 0 }
};
+MODULE_DEVICE_TABLE(serio, hil_dev_ids);
+
static struct serio_driver hil_serio_drv = {
.driver = {
.name = "hil_dev",
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
new file mode 100644
index 0000000..6e0f230
--- /dev/null
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * License terms:GNU General Public License (GPL) version 2
+ *
+ * Keypad controller driver for the SKE (Scroll Key Encoder) module used in
+ * the Nomadik 8815 and Ux500 platforms.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <plat/ske.h>
+
+/* SKE_CR bits */
+#define SKE_KPMLT (0x1 << 6)
+#define SKE_KPCN (0x7 << 3)
+#define SKE_KPASEN (0x1 << 2)
+#define SKE_KPASON (0x1 << 7)
+
+/* SKE_IMSC bits */
+#define SKE_KPIMA (0x1 << 2)
+
+/* SKE_ICR bits */
+#define SKE_KPICS (0x1 << 3)
+#define SKE_KPICA (0x1 << 2)
+
+/* SKE_RIS bits */
+#define SKE_KPRISA (0x1 << 2)
+
+#define SKE_KEYPAD_ROW_SHIFT 3
+#define SKE_KPD_KEYMAP_SIZE (8 * 8)
+
+/* keypad auto scan registers */
+#define SKE_ASR0 0x20
+#define SKE_ASR1 0x24
+#define SKE_ASR2 0x28
+#define SKE_ASR3 0x2C
+
+#define SKE_NUM_ASRX_REGISTERS (4)
+
+/**
+ * struct ske_keypad - data structure used by keypad driver
+ * @irq: irq no
+ * @reg_base: ske regsiters base address
+ * @input: pointer to input device object
+ * @board: keypad platform device
+ * @keymap: matrix scan code table for keycodes
+ * @clk: clock structure pointer
+ */
+struct ske_keypad {
+ int irq;
+ void __iomem *reg_base;
+ struct input_dev *input;
+ const struct ske_keypad_platform_data *board;
+ unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
+ struct clk *clk;
+ spinlock_t ske_keypad_lock;
+};
+
+static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
+ u8 mask, u8 data)
+{
+ u32 ret;
+
+ spin_lock(&keypad->ske_keypad_lock);
+
+ ret = readl(keypad->reg_base + addr);
+ ret &= ~mask;
+ ret |= data;
+ writel(ret, keypad->reg_base + addr);
+
+ spin_unlock(&keypad->ske_keypad_lock);
+}
+
+/*
+ * ske_keypad_chip_init: init keypad controller configuration
+ *
+ * Enable Multi key press detection, auto scan mode
+ */
+static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
+{
+ u32 value;
+ int timeout = 50;
+
+ /* check SKE_RIS to be 0 */
+ while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
+ cpu_relax();
+
+ if (!timeout)
+ return -EINVAL;
+
+ /*
+ * set debounce value
+ * keypad dbounce is configured in DBCR[15:8]
+ * dbounce value in steps of 32/32.768 ms
+ */
+ spin_lock(&keypad->ske_keypad_lock);
+ value = readl(keypad->reg_base + SKE_DBCR);
+ value = value & 0xff;
+ value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
+ writel(value, keypad->reg_base + SKE_DBCR);
+ spin_unlock(&keypad->ske_keypad_lock);
+
+ /* enable multi key detection */
+ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
+
+ /*
+ * set up the number of columns
+ * KPCN[5:3] defines no. of keypad columns to be auto scanned
+ */
+ value = (keypad->board->kcol - 1) << 3;
+ ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
+
+ /* clear keypad interrupt for auto(and pending SW) scans */
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
+
+ /* un-mask keypad interrupts */
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+ /* enable automatic scan */
+ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
+
+ return 0;
+}
+
+static void ske_keypad_read_data(struct ske_keypad *keypad)
+{
+ struct input_dev *input = keypad->input;
+ u16 status;
+ int col = 0, row = 0, code;
+ int ske_asr, ske_ris, key_pressed, i;
+
+ /*
+ * Read the auto scan registers
+ *
+ * Each SKE_ASRx (x=0 to x=3) contains two row values.
+ * lower byte contains row value for column 2*x,
+ * upper byte contains row value for column 2*x + 1
+ */
+ for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
+ ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
+ if (!ske_asr)
+ continue;
+
+ /* now that ASRx is zero, find out the column x and row y*/
+ if (ske_asr & 0xff) {
+ col = i * 2;
+ status = ske_asr & 0xff;
+ } else {
+ col = (i * 2) + 1;
+ status = (ske_asr & 0xff00) >> 8;
+ }
+
+ /* find out the row */
+ row = __ffs(status);
+
+ code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
+ ske_ris = readl(keypad->reg_base + SKE_RIS);
+ key_pressed = ske_ris & SKE_KPRISA;
+
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code], key_pressed);
+ input_sync(input);
+ }
+}
+
+static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
+{
+ struct ske_keypad *keypad = dev_id;
+ int retries = 20;
+
+ /* disable auto scan interrupt; mask the interrupt generated */
+ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+
+ while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)
+ msleep(5);
+
+ if (retries) {
+ /* SKEx registers are stable and can be read */
+ ske_keypad_read_data(keypad);
+ }
+
+ /* enable auto scan interrupts */
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ske_keypad_probe(struct platform_device *pdev)
+{
+ const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
+ struct ske_keypad *keypad;
+ struct input_dev *input;
+ struct resource *res;
+ int irq;
+ int error;
+
+ if (!plat) {
+ dev_err(&pdev->dev, "invalid keypad platform data\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keypad irq\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing platform resources\n");
+ return -EINVAL;
+ }
+
+ keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!keypad || !input) {
+ dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ keypad->irq = irq;
+ keypad->board = plat;
+ keypad->input = input;
+ spin_lock_init(&keypad->ske_keypad_lock);
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ keypad->reg_base = ioremap(res->start, resource_size(res));
+ if (!keypad->reg_base) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto err_free_mem_region;
+ }
+
+ keypad->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(keypad->clk)) {
+ dev_err(&pdev->dev, "failed to get clk\n");
+ error = PTR_ERR(keypad->clk);
+ goto err_iounmap;
+ }
+
+ input->id.bustype = BUS_HOST;
+ input->name = "ux500-ske-keypad";
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ clk_enable(keypad->clk);
+
+ /* go through board initialization helpers */
+ if (keypad->board->init)
+ keypad->board->init();
+
+ error = ske_keypad_chip_init(keypad);
+ if (error) {
+ dev_err(&pdev->dev, "unable to init keypad hardware\n");
+ goto err_clk_disable;
+ }
+
+ error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
+ IRQF_ONESHOT, "ske-keypad", keypad);
+ if (error) {
+ dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
+ goto err_clk_disable;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", error);
+ goto err_free_irq;
+ }
+
+ if (plat->wakeup_enable)
+ device_init_wakeup(&pdev->dev, true);
+
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+err_free_irq:
+ free_irq(keypad->irq, keypad);
+err_clk_disable:
+ clk_disable(keypad->clk);
+ clk_put(keypad->clk);
+err_iounmap:
+ iounmap(keypad->reg_base);
+err_free_mem_region:
+ release_mem_region(res->start, resource_size(res));
+err_free_mem:
+ input_free_device(input);
+ kfree(keypad);
+ return error;
+}
+
+static int __devexit ske_keypad_remove(struct platform_device *pdev)
+{
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ free_irq(keypad->irq, keypad);
+
+ input_unregister_device(keypad->input);
+
+ clk_disable(keypad->clk);
+ clk_put(keypad->clk);
+
+ if (keypad->board->exit)
+ keypad->board->exit();
+
+ iounmap(keypad->reg_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ske_keypad_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(irq);
+ else
+ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+
+ return 0;
+}
+
+static int ske_keypad_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(irq);
+ else
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
+ .suspend = ske_keypad_suspend,
+ .resume = ske_keypad_resume,
+};
+#endif
+
+struct platform_driver ske_keypad_driver = {
+ .driver = {
+ .name = "nmk-ske-keypad",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ske_keypad_dev_pm_ops,
+#endif
+ },
+ .probe = ske_keypad_probe,
+ .remove = __devexit_p(ske_keypad_remove),
+};
+
+static int __init ske_keypad_init(void)
+{
+ return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);
+}
+module_init(ske_keypad_init);
+
+static void __exit ske_keypad_exit(void)
+{
+ platform_driver_unregister(&ske_keypad_driver);
+}
+module_exit(ske_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
+MODULE_ALIAS("platform:nomadik-ske-keypad");
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
new file mode 100644
index 0000000..45bd097
--- /dev/null
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -0,0 +1,318 @@
+/*
+ * OMAP4 Keypad Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author: Abraham Arce <x0066660@ti.com>
+ * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <plat/omap4-keypad.h>
+
+/* OMAP4 registers */
+#define OMAP4_KBD_REVISION 0x00
+#define OMAP4_KBD_SYSCONFIG 0x10
+#define OMAP4_KBD_SYSSTATUS 0x14
+#define OMAP4_KBD_IRQSTATUS 0x18
+#define OMAP4_KBD_IRQENABLE 0x1C
+#define OMAP4_KBD_WAKEUPENABLE 0x20
+#define OMAP4_KBD_PENDING 0x24
+#define OMAP4_KBD_CTRL 0x28
+#define OMAP4_KBD_DEBOUNCINGTIME 0x2C
+#define OMAP4_KBD_LONGKEYTIME 0x30
+#define OMAP4_KBD_TIMEOUT 0x34
+#define OMAP4_KBD_STATEMACHINE 0x38
+#define OMAP4_KBD_ROWINPUTS 0x3C
+#define OMAP4_KBD_COLUMNOUTPUTS 0x40
+#define OMAP4_KBD_FULLCODE31_0 0x44
+#define OMAP4_KBD_FULLCODE63_32 0x48
+
+/* OMAP4 bit definitions */
+#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0)
+#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1)
+#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2)
+#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0)
+#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1)
+#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1)
+#define OMAP4_DEF_CTRLPTVVALUE (1 << 2)
+#define OMAP4_DEF_CTRLPTV (1 << 1)
+
+/* OMAP4 values */
+#define OMAP4_VAL_IRQDISABLE 0x00
+#define OMAP4_VAL_DEBOUNCINGTIME 0x07
+#define OMAP4_VAL_FUNCTIONALCFG 0x1E
+
+#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF
+
+struct omap4_keypad {
+ struct input_dev *input;
+
+ void __iomem *base;
+ int irq;
+
+ unsigned int rows;
+ unsigned int cols;
+ unsigned int row_shift;
+ unsigned char key_state[8];
+ unsigned short keymap[];
+};
+
+static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data)
+{
+ __raw_writel(OMAP4_VAL_FUNCTIONALCFG,
+ keypad_data->base + OMAP4_KBD_CTRL);
+ __raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
+ keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
+ __raw_writel(OMAP4_VAL_IRQDISABLE,
+ keypad_data->base + OMAP4_KBD_IRQSTATUS);
+ __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+ __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
+ keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
+}
+
+/* Interrupt handler */
+static irqreturn_t omap4_keypad_interrupt(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;
+
+ /* Disable interrupts */
+ __raw_writel(OMAP4_VAL_IRQDISABLE,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+
+ *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);
+ *(new_state + 1) = __raw_readl(keypad_data->base
+ + 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));
+ }
+ }
+ }
+
+ input_sync(input_dev);
+
+ memcpy(keypad_data->key_state, key_state,
+ sizeof(keypad_data->key_state));
+
+ /* clear pending interrupts */
+ __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
+ keypad_data->base + OMAP4_KBD_IRQSTATUS);
+
+ /* enable interrupts */
+ __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit omap4_keypad_probe(struct platform_device *pdev)
+{
+ const struct omap4_keypad_platform_data *pdata;
+ struct omap4_keypad *keypad_data;
+ struct input_dev *input_dev;
+ struct resource *res;
+ resource_size_t size;
+ unsigned int row_shift, max_keys;
+ int irq;
+ int error;
+
+ /* platform data */
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no base address specified\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no keyboard irq assigned\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->keymap_data) {
+ dev_err(&pdev->dev, "no keymap data defined\n");
+ return -EINVAL;
+ }
+
+ row_shift = get_count_order(pdata->cols);
+ max_keys = pdata->rows << row_shift;
+
+ keypad_data = kzalloc(sizeof(struct omap4_keypad) +
+ max_keys * sizeof(keypad_data->keymap[0]),
+ GFP_KERNEL);
+ if (!keypad_data) {
+ dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ size = resource_size(res);
+
+ res = request_mem_region(res->start, size, pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "can't request mem region\n");
+ error = -EBUSY;
+ goto err_free_keypad;
+ }
+
+ 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;
+ }
+
+ keypad_data->irq = irq;
+ keypad_data->row_shift = row_shift;
+ keypad_data->rows = pdata->rows;
+ keypad_data->cols = pdata->cols;
+
+ /* input device allocation */
+ keypad_data->input = input_dev = input_allocate_device();
+ if (!input_dev) {
+ error = -ENOMEM;
+ goto err_unmap;
+ }
+
+ 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;
+ input_dev->id.version = 0x0001;
+
+ input_dev->keycode = keypad_data->keymap;
+ input_dev->keycodesize = sizeof(keypad_data->keymap[0]);
+ input_dev->keycodemax = max_keys;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_REP, input_dev->evbit);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+ input_set_drvdata(input_dev, keypad_data);
+
+ matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
+ input_dev->keycode, input_dev->keybit);
+
+ omap4_keypad_config(keypad_data);
+
+ error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
+ IRQF_TRIGGER_RISING,
+ "omap4-keypad", keypad_data);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register interrupt\n");
+ goto err_free_input;
+ }
+
+ error = input_register_device(keypad_data->input);
+ if (error < 0) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto err_free_irq;
+ }
+
+
+ platform_set_drvdata(pdev, keypad_data);
+ return 0;
+
+err_free_irq:
+ free_irq(keypad_data->irq, keypad_data);
+err_free_input:
+ input_free_device(input_dev);
+err_unmap:
+ iounmap(keypad_data->base);
+err_release_mem:
+ release_mem_region(res->start, size);
+err_free_keypad:
+ kfree(keypad_data);
+ return error;
+}
+
+static int __devexit omap4_keypad_remove(struct platform_device *pdev)
+{
+ struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(keypad_data->irq, keypad_data);
+ 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);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver omap4_keypad_driver = {
+ .probe = omap4_keypad_probe,
+ .remove = __devexit_p(omap4_keypad_remove),
+ .driver = {
+ .name = "omap4-keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap4_keypad_init(void)
+{
+ return platform_driver_register(&omap4_keypad_driver);
+}
+module_init(omap4_keypad_init);
+
+static void __exit omap4_keypad_exit(void)
+{
+ platform_driver_unregister(&omap4_keypad_driver);
+}
+module_exit(omap4_keypad_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP4 Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap4-keypad");
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
index fb16b5e..09bef79 100644
--- a/drivers/input/keyboard/twl4030_keypad.c
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -406,23 +406,22 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
if (error) {
dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
kp->irq);
- goto err3;
+ goto err2;
}
/* Enable KP and TO interrupts now. */
reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
error = -EIO;
- goto err4;
+ goto err3;
}
platform_set_drvdata(pdev, kp);
return 0;
-err4:
+err3:
/* mask all events - we don't care about the result */
(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
-err3:
free_irq(kp->irq, NULL);
err2:
input_unregister_device(input);