All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sourav Poddar <sourav.poddar@ti.com>
To: <devicetree-discuss@lists.ozlabs.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-omap@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<linux-input@vger.kernel.org>, <sourav.poddar@ti.com>
Cc: <b-cousson@ti.com>, <balbi@ti.com>, <santosh.shilimkar@ti.com>,
	<dmitry.torokhov@gmail.com>
Subject: [PATCHv2 2/4] Input: keypad: Add smsc ece1099 keypad driver
Date: Wed, 5 Sep 2012 17:06:37 +0530	[thread overview]
Message-ID: <1346844997-24868-1-git-send-email-sourav.poddar@ti.com> (raw)

From: G, Manjunath Kondaiah <manjugk@ti.com>

SMSC ECE1099 is a keyboard scan or GPIO expansion device.The device
supports a keypad scan matrix of 23*8.This driver uses this
device as a keypad driver.

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benoit Cousson <b-cousson@ti.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: G, Manjunath Kondaiah <manjugk@ti.com>
Signed-off-by: Sourav Poddar <sourav.poddar@ti.com>
Acked-by: Felipe Balbi <balbi@ti.com>
---
Changes since v1:
 - Prevent the use of kfree since devm_kzalloc was used.
 - Use devexit around remove api
 drivers/input/keyboard/Kconfig               |   11 +
 drivers/input/keyboard/Makefile              |    1 +
 drivers/input/keyboard/smsc-ece1099-keypad.c |  306 ++++++++++++++++++++++++++
 3 files changed, 318 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/keyboard/smsc-ece1099-keypad.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index c50fa75..2a2d374 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -593,6 +593,17 @@ config KEYBOARD_TWL4030
 	  To compile this driver as a module, choose M here: the
 	  module will be called twl4030_keypad.
 
+config KEYBOARD_SMSC
+       tristate "SMSC ECE1099 keypad support"
+       depends on I2C=y
+       help
+         Say Y here if your board use the smsc keypad controller
+         for omap5 defconfig. It's safe to say enable this
+         even on boards that don't use the keypad controller.
+
+         To compile this driver as a module, choose M here: the
+         module will be called smsc-ece1099-keypad.
+
 config KEYBOARD_XTKBD
 	tristate "XT keyboard"
 	select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 44e7600..0f2aa26 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -52,5 +52,6 @@ obj-$(CONFIG_KEYBOARD_TC3589X)		+= tc3589x-keypad.o
 obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
 obj-$(CONFIG_KEYBOARD_TNETV107X)	+= tnetv107x-keypad.o
 obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
+obj-$(CONFIG_KEYBOARD_SMSC)            += smsc-ece1099-keypad.o
 obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
 obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
diff --git a/drivers/input/keyboard/smsc-ece1099-keypad.c b/drivers/input/keyboard/smsc-ece1099-keypad.c
new file mode 100644
index 0000000..71cd7d6
--- /dev/null
+++ b/drivers/input/keyboard/smsc-ece1099-keypad.c
@@ -0,0 +1,306 @@
+/*
+ * SMSC_ECE1099 Keypad driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/smsc.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+
+#define KEYPRESS_TIME          200
+
+struct smsc_keypad {
+	struct smsc *smsc;
+	struct matrix_keymap_data *keymap_data;
+	unsigned int last_key_state[16];
+	unsigned int last_col;
+	unsigned int last_key_ms[16];
+	unsigned short *keymap;
+	struct i2c_client *client;
+	struct input_dev *input;
+	int rows, cols;
+	int row_shift;
+	bool no_autorepeat;
+	unsigned        irq;
+	struct device *dev;
+};
+
+static void smsc_kp_scan(struct smsc_keypad *kp)
+{
+	struct input_dev *input = kp->input;
+	int i, j;
+	int row, col;
+	int temp, code;
+	unsigned int new_state[16];
+	unsigned int bits_changed;
+	int this_ms;
+
+	smsc_write(kp->dev, SMSC_KP_INT_MASK, 0x00);
+	smsc_write(kp->dev, SMSC_KP_INT_STAT, 0xFF);
+
+	/* Scan for row and column */
+	for (i = 0; i < kp->cols; i++) {
+		smsc_write(kp->dev, SMSC_KP_OUT, SMSC_KSO_EVAL + i);
+		/* Read Row Status */
+		smsc_read(kp->dev, SMSC_KP_IN, &temp);
+		if (temp == 0xFF)
+			continue;
+
+		col = i;
+		for (j = 0; j < kp->rows; j++) {
+			if ((temp & 0x01) != 0x00) {
+				temp = temp >> 1;
+				continue;
+			}
+
+			row = j;
+			new_state[col] =  (1 << row);
+			bits_changed = kp->last_key_state[col] ^ new_state[col];
+			this_ms = jiffies_to_msecs(jiffies);
+			if (bits_changed != 0 || (!bits_changed &&
+					((this_ms - kp->last_key_ms[col]) >= KEYPRESS_TIME))) {
+				code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
+				input_event(input, EV_MSC, MSC_SCAN, code);
+				input_report_key(input, kp->keymap[code], 1);
+				input_report_key(input, kp->keymap[code], 0);
+				kp->last_key_state[col] = new_state[col];
+				if (kp->last_col != col)
+					kp->last_key_state[kp->last_col] = 0;
+				kp->last_key_ms[col] = this_ms;
+			}
+			temp = temp >> 1;
+		}
+	}
+	input_sync(input);
+
+	smsc_write(kp->dev, SMSC_KP_INT_MASK, 0xFF);
+
+	/* Set up Low Power Mode (Wake-up) (0xFB) */
+	smsc_write(kp->dev, SMSC_WKUP_CTRL, SMSC_KP_SET_LOW_PWR);
+
+	/*Enable Keypad Scan (generate interrupt on key press) (0x40)*/
+	smsc_write(kp->dev, SMSC_KP_OUT, SMSC_KSO_ALL_LOW);
+}
+
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+	struct smsc_keypad *kp = _kp;
+	int int_status;
+
+	smsc_read(kp->dev, SMSC_KP_INT_STAT, &int_status);
+	if (int_status)
+		smsc_kp_scan(kp);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+static int __devinit smsc_keypad_parse_dt(struct device *dev,
+				struct smsc_keypad *kp)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!np) {
+		dev_err(dev, "missing DT data");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(np, "keypad,num-rows", &kp->rows);
+	of_property_read_u32(np, "keypad,num-columns", &kp->cols);
+	if (!kp->rows || !kp->cols) {
+		dev_err(dev, "number of keypad rows/columns not specified\n");
+		return -EINVAL;
+	}
+
+	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
+		kp->no_autorepeat = true;
+
+	return 0;
+}
+#else
+static inline int smsc_keypad_parse_dt(struct device *dev,
+				struct smsc_keypad *kp)
+{
+	return -ENOSYS;
+}
+#endif
+
+static int __devinit
+smsc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct smsc *smsc = dev_get_drvdata(pdev->dev.parent);
+	struct input_dev *input;
+	struct smsc_keypad *kp;
+	int ret = 0, error;
+	int col, i, max_keys, row_shift;
+	int irq;
+	int addr_start, addr;
+
+	kp = devm_kzalloc(dev, sizeof(*kp), GFP_KERNEL);
+
+	input = input_allocate_device();
+	if (!kp || !input) {
+		error = -ENOMEM;
+		goto err1;
+	}
+
+	error = smsc_keypad_parse_dt(&pdev->dev, kp);
+	if (error)
+		return error;
+
+	/* Get the debug Device */
+	kp->input = input;
+	kp->smsc = smsc;
+	kp->irq = platform_get_irq(pdev, 0);
+	kp->dev = dev;
+
+	for (col = 0; col < 16; col++) {
+		kp->last_key_state[col] = 0;
+		kp->last_key_ms[col] = 0;
+	}
+
+	/* setup input device */
+	 __set_bit(EV_KEY, input->evbit);
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (!(kp->no_autorepeat))
+		__set_bit(EV_REP, input->evbit);
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	input->name             = "SMSC Keypad";
+	input->phys             = "smsc_keypad/input0";
+	input->dev.parent       = &pdev->dev;
+	input->id.bustype       = BUS_HOST;
+	input->id.vendor        = 0x0001;
+	input->id.product       = 0x0001;
+	input->id.version       = 0x0003;
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(kp->dev,
+			"Unable to register twl4030 keypad device\n");
+		goto err1;
+	}
+
+	/* Mask all GPIO interrupts (0x37-0x3B) */
+	for (addr = 0x37; addr < 0x3B; addr++)
+		smsc_write(dev, addr, 0);
+
+	/* Set all outputs high (0x05-0x09) */
+	for (addr = 0x05; addr < 0x09; addr++)
+		smsc_write(dev, addr, 0xff);
+
+	/* Clear all GPIO interrupts (0x32-0x36) */
+	for (addr = 0x32; addr < 0x36; addr++)
+		smsc_write(dev, addr, 0xff);
+
+	addr_start = 0x12;
+	for (i = 0; i <= kp->rows; i++) {
+		addr = 0x12 + i;
+		smsc_write(dev, addr, SMSC_KP_KSI);
+	}
+
+	addr_start =  0x1A;
+	for (i = 0; i <= kp->cols; i++) {
+		addr = 0x1A + i;
+		smsc_write(dev, addr, SMSC_KP_KSO);
+	}
+
+	addr = SMSC_KP_INT_STAT;
+	smsc_write(dev, addr, SMSC_KP_SET_HIGH);
+
+	addr = SMSC_WKUP_CTRL;
+	smsc_write(dev, addr, SMSC_KP_SET_LOW_PWR);
+
+	addr = SMSC_KP_OUT;
+	smsc_write(dev, addr, SMSC_KSO_ALL_LOW);
+
+	row_shift = get_count_order(kp->cols);
+	max_keys = kp->rows << row_shift;
+
+	kp->row_shift = row_shift;
+	kp->keymap = kzalloc(max_keys * sizeof(kp->keymap[0]),
+					GFP_KERNEL);
+	if (!kp->keymap) {
+		dev_err(&pdev->dev, "Not enough memory for keymap\n");
+		error = -ENOMEM;
+	}
+
+	matrix_keypad_build_keymap(NULL, NULL, kp->rows,
+			kp->cols, kp->keymap, input);
+
+	/*
+	* This ISR will always execute in kernel thread context because of
+	* the need to access the SMSC over the I2C bus.
+	*/
+	ret = devm_request_threaded_irq(dev, kp->irq, NULL, do_kp_irq,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT, pdev->name, kp);
+	if (ret) {
+		dev_dbg(&pdev->dev, "request_irq failed for irq no=%d\n",
+			irq);
+		goto err2;
+	}
+
+	/* Enable smsc keypad interrupts */
+	ret = smsc_write(dev, SMSC_KP_INT_MASK, 0xff);
+	if (ret < 0)
+		goto err2;
+
+	return 0;
+
+err2:
+	input_unregister_device(input);
+err1:
+	input_free_device(input);
+	return ret;
+}
+
+static int __devexit smsc_remove(struct platform_device *pdev)
+{
+	struct smsc_keypad *kp = platform_get_drvdata(pdev);
+	input_unregister_device(kp->input);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id smsc_keypad_dt_match[] = {
+	{ .compatible = "smsc,keypad" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, smsc_keypad_dt_match);
+#endif
+
+static struct platform_driver smsc_driver = {
+	.driver = {
+		.name	= "smsc-keypad",
+		.of_match_table = of_match_ptr(smsc_keypad_dt_match),
+		.owner  = THIS_MODULE,
+	},
+	.probe		= smsc_probe,
+	.remove		= __devexit_p(smsc_remove),
+};
+
+module_platform_driver(smsc_driver);
+
+MODULE_AUTHOR("G Kondaiah Manjunath <manjugk@ti.com>");
+MODULE_DESCRIPTION("SMSC ECE1099 Keypad driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.1


WARNING: multiple messages have this Message-ID (diff)
From: Sourav Poddar <sourav.poddar@ti.com>
To: devicetree-discuss@lists.ozlabs.org,
	linux-arm-kernel@lists.infradead.org, linux-omap@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-input@vger.kernel.org,
	sourav.poddar@ti.com
Cc: b-cousson@ti.com, balbi@ti.com, santosh.shilimkar@ti.com,
	dmitry.torokhov@gmail.com
Subject: [PATCHv2 2/4] Input: keypad: Add smsc ece1099 keypad driver
Date: Wed, 5 Sep 2012 17:06:37 +0530	[thread overview]
Message-ID: <1346844997-24868-1-git-send-email-sourav.poddar@ti.com> (raw)

From: G, Manjunath Kondaiah <manjugk@ti.com>

SMSC ECE1099 is a keyboard scan or GPIO expansion device.The device
supports a keypad scan matrix of 23*8.This driver uses this
device as a keypad driver.

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benoit Cousson <b-cousson@ti.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: G, Manjunath Kondaiah <manjugk@ti.com>
Signed-off-by: Sourav Poddar <sourav.poddar@ti.com>
Acked-by: Felipe Balbi <balbi@ti.com>
---
Changes since v1:
 - Prevent the use of kfree since devm_kzalloc was used.
 - Use devexit around remove api
 drivers/input/keyboard/Kconfig               |   11 +
 drivers/input/keyboard/Makefile              |    1 +
 drivers/input/keyboard/smsc-ece1099-keypad.c |  306 ++++++++++++++++++++++++++
 3 files changed, 318 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/keyboard/smsc-ece1099-keypad.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index c50fa75..2a2d374 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -593,6 +593,17 @@ config KEYBOARD_TWL4030
 	  To compile this driver as a module, choose M here: the
 	  module will be called twl4030_keypad.
 
+config KEYBOARD_SMSC
+       tristate "SMSC ECE1099 keypad support"
+       depends on I2C=y
+       help
+         Say Y here if your board use the smsc keypad controller
+         for omap5 defconfig. It's safe to say enable this
+         even on boards that don't use the keypad controller.
+
+         To compile this driver as a module, choose M here: the
+         module will be called smsc-ece1099-keypad.
+
 config KEYBOARD_XTKBD
 	tristate "XT keyboard"
 	select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 44e7600..0f2aa26 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -52,5 +52,6 @@ obj-$(CONFIG_KEYBOARD_TC3589X)		+= tc3589x-keypad.o
 obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
 obj-$(CONFIG_KEYBOARD_TNETV107X)	+= tnetv107x-keypad.o
 obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
+obj-$(CONFIG_KEYBOARD_SMSC)            += smsc-ece1099-keypad.o
 obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
 obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
diff --git a/drivers/input/keyboard/smsc-ece1099-keypad.c b/drivers/input/keyboard/smsc-ece1099-keypad.c
new file mode 100644
index 0000000..71cd7d6
--- /dev/null
+++ b/drivers/input/keyboard/smsc-ece1099-keypad.c
@@ -0,0 +1,306 @@
+/*
+ * SMSC_ECE1099 Keypad driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/smsc.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+
+#define KEYPRESS_TIME          200
+
+struct smsc_keypad {
+	struct smsc *smsc;
+	struct matrix_keymap_data *keymap_data;
+	unsigned int last_key_state[16];
+	unsigned int last_col;
+	unsigned int last_key_ms[16];
+	unsigned short *keymap;
+	struct i2c_client *client;
+	struct input_dev *input;
+	int rows, cols;
+	int row_shift;
+	bool no_autorepeat;
+	unsigned        irq;
+	struct device *dev;
+};
+
+static void smsc_kp_scan(struct smsc_keypad *kp)
+{
+	struct input_dev *input = kp->input;
+	int i, j;
+	int row, col;
+	int temp, code;
+	unsigned int new_state[16];
+	unsigned int bits_changed;
+	int this_ms;
+
+	smsc_write(kp->dev, SMSC_KP_INT_MASK, 0x00);
+	smsc_write(kp->dev, SMSC_KP_INT_STAT, 0xFF);
+
+	/* Scan for row and column */
+	for (i = 0; i < kp->cols; i++) {
+		smsc_write(kp->dev, SMSC_KP_OUT, SMSC_KSO_EVAL + i);
+		/* Read Row Status */
+		smsc_read(kp->dev, SMSC_KP_IN, &temp);
+		if (temp == 0xFF)
+			continue;
+
+		col = i;
+		for (j = 0; j < kp->rows; j++) {
+			if ((temp & 0x01) != 0x00) {
+				temp = temp >> 1;
+				continue;
+			}
+
+			row = j;
+			new_state[col] =  (1 << row);
+			bits_changed = kp->last_key_state[col] ^ new_state[col];
+			this_ms = jiffies_to_msecs(jiffies);
+			if (bits_changed != 0 || (!bits_changed &&
+					((this_ms - kp->last_key_ms[col]) >= KEYPRESS_TIME))) {
+				code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
+				input_event(input, EV_MSC, MSC_SCAN, code);
+				input_report_key(input, kp->keymap[code], 1);
+				input_report_key(input, kp->keymap[code], 0);
+				kp->last_key_state[col] = new_state[col];
+				if (kp->last_col != col)
+					kp->last_key_state[kp->last_col] = 0;
+				kp->last_key_ms[col] = this_ms;
+			}
+			temp = temp >> 1;
+		}
+	}
+	input_sync(input);
+
+	smsc_write(kp->dev, SMSC_KP_INT_MASK, 0xFF);
+
+	/* Set up Low Power Mode (Wake-up) (0xFB) */
+	smsc_write(kp->dev, SMSC_WKUP_CTRL, SMSC_KP_SET_LOW_PWR);
+
+	/*Enable Keypad Scan (generate interrupt on key press) (0x40)*/
+	smsc_write(kp->dev, SMSC_KP_OUT, SMSC_KSO_ALL_LOW);
+}
+
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+	struct smsc_keypad *kp = _kp;
+	int int_status;
+
+	smsc_read(kp->dev, SMSC_KP_INT_STAT, &int_status);
+	if (int_status)
+		smsc_kp_scan(kp);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+static int __devinit smsc_keypad_parse_dt(struct device *dev,
+				struct smsc_keypad *kp)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!np) {
+		dev_err(dev, "missing DT data");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(np, "keypad,num-rows", &kp->rows);
+	of_property_read_u32(np, "keypad,num-columns", &kp->cols);
+	if (!kp->rows || !kp->cols) {
+		dev_err(dev, "number of keypad rows/columns not specified\n");
+		return -EINVAL;
+	}
+
+	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
+		kp->no_autorepeat = true;
+
+	return 0;
+}
+#else
+static inline int smsc_keypad_parse_dt(struct device *dev,
+				struct smsc_keypad *kp)
+{
+	return -ENOSYS;
+}
+#endif
+
+static int __devinit
+smsc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct smsc *smsc = dev_get_drvdata(pdev->dev.parent);
+	struct input_dev *input;
+	struct smsc_keypad *kp;
+	int ret = 0, error;
+	int col, i, max_keys, row_shift;
+	int irq;
+	int addr_start, addr;
+
+	kp = devm_kzalloc(dev, sizeof(*kp), GFP_KERNEL);
+
+	input = input_allocate_device();
+	if (!kp || !input) {
+		error = -ENOMEM;
+		goto err1;
+	}
+
+	error = smsc_keypad_parse_dt(&pdev->dev, kp);
+	if (error)
+		return error;
+
+	/* Get the debug Device */
+	kp->input = input;
+	kp->smsc = smsc;
+	kp->irq = platform_get_irq(pdev, 0);
+	kp->dev = dev;
+
+	for (col = 0; col < 16; col++) {
+		kp->last_key_state[col] = 0;
+		kp->last_key_ms[col] = 0;
+	}
+
+	/* setup input device */
+	 __set_bit(EV_KEY, input->evbit);
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (!(kp->no_autorepeat))
+		__set_bit(EV_REP, input->evbit);
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	input->name             = "SMSC Keypad";
+	input->phys             = "smsc_keypad/input0";
+	input->dev.parent       = &pdev->dev;
+	input->id.bustype       = BUS_HOST;
+	input->id.vendor        = 0x0001;
+	input->id.product       = 0x0001;
+	input->id.version       = 0x0003;
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(kp->dev,
+			"Unable to register twl4030 keypad device\n");
+		goto err1;
+	}
+
+	/* Mask all GPIO interrupts (0x37-0x3B) */
+	for (addr = 0x37; addr < 0x3B; addr++)
+		smsc_write(dev, addr, 0);
+
+	/* Set all outputs high (0x05-0x09) */
+	for (addr = 0x05; addr < 0x09; addr++)
+		smsc_write(dev, addr, 0xff);
+
+	/* Clear all GPIO interrupts (0x32-0x36) */
+	for (addr = 0x32; addr < 0x36; addr++)
+		smsc_write(dev, addr, 0xff);
+
+	addr_start = 0x12;
+	for (i = 0; i <= kp->rows; i++) {
+		addr = 0x12 + i;
+		smsc_write(dev, addr, SMSC_KP_KSI);
+	}
+
+	addr_start =  0x1A;
+	for (i = 0; i <= kp->cols; i++) {
+		addr = 0x1A + i;
+		smsc_write(dev, addr, SMSC_KP_KSO);
+	}
+
+	addr = SMSC_KP_INT_STAT;
+	smsc_write(dev, addr, SMSC_KP_SET_HIGH);
+
+	addr = SMSC_WKUP_CTRL;
+	smsc_write(dev, addr, SMSC_KP_SET_LOW_PWR);
+
+	addr = SMSC_KP_OUT;
+	smsc_write(dev, addr, SMSC_KSO_ALL_LOW);
+
+	row_shift = get_count_order(kp->cols);
+	max_keys = kp->rows << row_shift;
+
+	kp->row_shift = row_shift;
+	kp->keymap = kzalloc(max_keys * sizeof(kp->keymap[0]),
+					GFP_KERNEL);
+	if (!kp->keymap) {
+		dev_err(&pdev->dev, "Not enough memory for keymap\n");
+		error = -ENOMEM;
+	}
+
+	matrix_keypad_build_keymap(NULL, NULL, kp->rows,
+			kp->cols, kp->keymap, input);
+
+	/*
+	* This ISR will always execute in kernel thread context because of
+	* the need to access the SMSC over the I2C bus.
+	*/
+	ret = devm_request_threaded_irq(dev, kp->irq, NULL, do_kp_irq,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT, pdev->name, kp);
+	if (ret) {
+		dev_dbg(&pdev->dev, "request_irq failed for irq no=%d\n",
+			irq);
+		goto err2;
+	}
+
+	/* Enable smsc keypad interrupts */
+	ret = smsc_write(dev, SMSC_KP_INT_MASK, 0xff);
+	if (ret < 0)
+		goto err2;
+
+	return 0;
+
+err2:
+	input_unregister_device(input);
+err1:
+	input_free_device(input);
+	return ret;
+}
+
+static int __devexit smsc_remove(struct platform_device *pdev)
+{
+	struct smsc_keypad *kp = platform_get_drvdata(pdev);
+	input_unregister_device(kp->input);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id smsc_keypad_dt_match[] = {
+	{ .compatible = "smsc,keypad" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, smsc_keypad_dt_match);
+#endif
+
+static struct platform_driver smsc_driver = {
+	.driver = {
+		.name	= "smsc-keypad",
+		.of_match_table = of_match_ptr(smsc_keypad_dt_match),
+		.owner  = THIS_MODULE,
+	},
+	.probe		= smsc_probe,
+	.remove		= __devexit_p(smsc_remove),
+};
+
+module_platform_driver(smsc_driver);
+
+MODULE_AUTHOR("G Kondaiah Manjunath <manjugk@ti.com>");
+MODULE_DESCRIPTION("SMSC ECE1099 Keypad driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.1


WARNING: multiple messages have this Message-ID (diff)
From: sourav.poddar@ti.com (Sourav Poddar)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCHv2 2/4] Input: keypad: Add smsc ece1099 keypad driver
Date: Wed, 5 Sep 2012 17:06:37 +0530	[thread overview]
Message-ID: <1346844997-24868-1-git-send-email-sourav.poddar@ti.com> (raw)

From: G, Manjunath Kondaiah <manjugk@ti.com>

SMSC ECE1099 is a keyboard scan or GPIO expansion device.The device
supports a keypad scan matrix of 23*8.This driver uses this
device as a keypad driver.

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benoit Cousson <b-cousson@ti.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: G, Manjunath Kondaiah <manjugk@ti.com>
Signed-off-by: Sourav Poddar <sourav.poddar@ti.com>
Acked-by: Felipe Balbi <balbi@ti.com>
---
Changes since v1:
 - Prevent the use of kfree since devm_kzalloc was used.
 - Use devexit around remove api
 drivers/input/keyboard/Kconfig               |   11 +
 drivers/input/keyboard/Makefile              |    1 +
 drivers/input/keyboard/smsc-ece1099-keypad.c |  306 ++++++++++++++++++++++++++
 3 files changed, 318 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/keyboard/smsc-ece1099-keypad.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index c50fa75..2a2d374 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -593,6 +593,17 @@ config KEYBOARD_TWL4030
 	  To compile this driver as a module, choose M here: the
 	  module will be called twl4030_keypad.
 
+config KEYBOARD_SMSC
+       tristate "SMSC ECE1099 keypad support"
+       depends on I2C=y
+       help
+         Say Y here if your board use the smsc keypad controller
+         for omap5 defconfig. It's safe to say enable this
+         even on boards that don't use the keypad controller.
+
+         To compile this driver as a module, choose M here: the
+         module will be called smsc-ece1099-keypad.
+
 config KEYBOARD_XTKBD
 	tristate "XT keyboard"
 	select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 44e7600..0f2aa26 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -52,5 +52,6 @@ obj-$(CONFIG_KEYBOARD_TC3589X)		+= tc3589x-keypad.o
 obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
 obj-$(CONFIG_KEYBOARD_TNETV107X)	+= tnetv107x-keypad.o
 obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
+obj-$(CONFIG_KEYBOARD_SMSC)            += smsc-ece1099-keypad.o
 obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
 obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
diff --git a/drivers/input/keyboard/smsc-ece1099-keypad.c b/drivers/input/keyboard/smsc-ece1099-keypad.c
new file mode 100644
index 0000000..71cd7d6
--- /dev/null
+++ b/drivers/input/keyboard/smsc-ece1099-keypad.c
@@ -0,0 +1,306 @@
+/*
+ * SMSC_ECE1099 Keypad driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/smsc.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+
+#define KEYPRESS_TIME          200
+
+struct smsc_keypad {
+	struct smsc *smsc;
+	struct matrix_keymap_data *keymap_data;
+	unsigned int last_key_state[16];
+	unsigned int last_col;
+	unsigned int last_key_ms[16];
+	unsigned short *keymap;
+	struct i2c_client *client;
+	struct input_dev *input;
+	int rows, cols;
+	int row_shift;
+	bool no_autorepeat;
+	unsigned        irq;
+	struct device *dev;
+};
+
+static void smsc_kp_scan(struct smsc_keypad *kp)
+{
+	struct input_dev *input = kp->input;
+	int i, j;
+	int row, col;
+	int temp, code;
+	unsigned int new_state[16];
+	unsigned int bits_changed;
+	int this_ms;
+
+	smsc_write(kp->dev, SMSC_KP_INT_MASK, 0x00);
+	smsc_write(kp->dev, SMSC_KP_INT_STAT, 0xFF);
+
+	/* Scan for row and column */
+	for (i = 0; i < kp->cols; i++) {
+		smsc_write(kp->dev, SMSC_KP_OUT, SMSC_KSO_EVAL + i);
+		/* Read Row Status */
+		smsc_read(kp->dev, SMSC_KP_IN, &temp);
+		if (temp == 0xFF)
+			continue;
+
+		col = i;
+		for (j = 0; j < kp->rows; j++) {
+			if ((temp & 0x01) != 0x00) {
+				temp = temp >> 1;
+				continue;
+			}
+
+			row = j;
+			new_state[col] =  (1 << row);
+			bits_changed = kp->last_key_state[col] ^ new_state[col];
+			this_ms = jiffies_to_msecs(jiffies);
+			if (bits_changed != 0 || (!bits_changed &&
+					((this_ms - kp->last_key_ms[col]) >= KEYPRESS_TIME))) {
+				code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
+				input_event(input, EV_MSC, MSC_SCAN, code);
+				input_report_key(input, kp->keymap[code], 1);
+				input_report_key(input, kp->keymap[code], 0);
+				kp->last_key_state[col] = new_state[col];
+				if (kp->last_col != col)
+					kp->last_key_state[kp->last_col] = 0;
+				kp->last_key_ms[col] = this_ms;
+			}
+			temp = temp >> 1;
+		}
+	}
+	input_sync(input);
+
+	smsc_write(kp->dev, SMSC_KP_INT_MASK, 0xFF);
+
+	/* Set up Low Power Mode (Wake-up) (0xFB) */
+	smsc_write(kp->dev, SMSC_WKUP_CTRL, SMSC_KP_SET_LOW_PWR);
+
+	/*Enable Keypad Scan (generate interrupt on key press) (0x40)*/
+	smsc_write(kp->dev, SMSC_KP_OUT, SMSC_KSO_ALL_LOW);
+}
+
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+	struct smsc_keypad *kp = _kp;
+	int int_status;
+
+	smsc_read(kp->dev, SMSC_KP_INT_STAT, &int_status);
+	if (int_status)
+		smsc_kp_scan(kp);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+static int __devinit smsc_keypad_parse_dt(struct device *dev,
+				struct smsc_keypad *kp)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!np) {
+		dev_err(dev, "missing DT data");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(np, "keypad,num-rows", &kp->rows);
+	of_property_read_u32(np, "keypad,num-columns", &kp->cols);
+	if (!kp->rows || !kp->cols) {
+		dev_err(dev, "number of keypad rows/columns not specified\n");
+		return -EINVAL;
+	}
+
+	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
+		kp->no_autorepeat = true;
+
+	return 0;
+}
+#else
+static inline int smsc_keypad_parse_dt(struct device *dev,
+				struct smsc_keypad *kp)
+{
+	return -ENOSYS;
+}
+#endif
+
+static int __devinit
+smsc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct smsc *smsc = dev_get_drvdata(pdev->dev.parent);
+	struct input_dev *input;
+	struct smsc_keypad *kp;
+	int ret = 0, error;
+	int col, i, max_keys, row_shift;
+	int irq;
+	int addr_start, addr;
+
+	kp = devm_kzalloc(dev, sizeof(*kp), GFP_KERNEL);
+
+	input = input_allocate_device();
+	if (!kp || !input) {
+		error = -ENOMEM;
+		goto err1;
+	}
+
+	error = smsc_keypad_parse_dt(&pdev->dev, kp);
+	if (error)
+		return error;
+
+	/* Get the debug Device */
+	kp->input = input;
+	kp->smsc = smsc;
+	kp->irq = platform_get_irq(pdev, 0);
+	kp->dev = dev;
+
+	for (col = 0; col < 16; col++) {
+		kp->last_key_state[col] = 0;
+		kp->last_key_ms[col] = 0;
+	}
+
+	/* setup input device */
+	 __set_bit(EV_KEY, input->evbit);
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (!(kp->no_autorepeat))
+		__set_bit(EV_REP, input->evbit);
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	input->name             = "SMSC Keypad";
+	input->phys             = "smsc_keypad/input0";
+	input->dev.parent       = &pdev->dev;
+	input->id.bustype       = BUS_HOST;
+	input->id.vendor        = 0x0001;
+	input->id.product       = 0x0001;
+	input->id.version       = 0x0003;
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(kp->dev,
+			"Unable to register twl4030 keypad device\n");
+		goto err1;
+	}
+
+	/* Mask all GPIO interrupts (0x37-0x3B) */
+	for (addr = 0x37; addr < 0x3B; addr++)
+		smsc_write(dev, addr, 0);
+
+	/* Set all outputs high (0x05-0x09) */
+	for (addr = 0x05; addr < 0x09; addr++)
+		smsc_write(dev, addr, 0xff);
+
+	/* Clear all GPIO interrupts (0x32-0x36) */
+	for (addr = 0x32; addr < 0x36; addr++)
+		smsc_write(dev, addr, 0xff);
+
+	addr_start = 0x12;
+	for (i = 0; i <= kp->rows; i++) {
+		addr = 0x12 + i;
+		smsc_write(dev, addr, SMSC_KP_KSI);
+	}
+
+	addr_start =  0x1A;
+	for (i = 0; i <= kp->cols; i++) {
+		addr = 0x1A + i;
+		smsc_write(dev, addr, SMSC_KP_KSO);
+	}
+
+	addr = SMSC_KP_INT_STAT;
+	smsc_write(dev, addr, SMSC_KP_SET_HIGH);
+
+	addr = SMSC_WKUP_CTRL;
+	smsc_write(dev, addr, SMSC_KP_SET_LOW_PWR);
+
+	addr = SMSC_KP_OUT;
+	smsc_write(dev, addr, SMSC_KSO_ALL_LOW);
+
+	row_shift = get_count_order(kp->cols);
+	max_keys = kp->rows << row_shift;
+
+	kp->row_shift = row_shift;
+	kp->keymap = kzalloc(max_keys * sizeof(kp->keymap[0]),
+					GFP_KERNEL);
+	if (!kp->keymap) {
+		dev_err(&pdev->dev, "Not enough memory for keymap\n");
+		error = -ENOMEM;
+	}
+
+	matrix_keypad_build_keymap(NULL, NULL, kp->rows,
+			kp->cols, kp->keymap, input);
+
+	/*
+	* This ISR will always execute in kernel thread context because of
+	* the need to access the SMSC over the I2C bus.
+	*/
+	ret = devm_request_threaded_irq(dev, kp->irq, NULL, do_kp_irq,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT, pdev->name, kp);
+	if (ret) {
+		dev_dbg(&pdev->dev, "request_irq failed for irq no=%d\n",
+			irq);
+		goto err2;
+	}
+
+	/* Enable smsc keypad interrupts */
+	ret = smsc_write(dev, SMSC_KP_INT_MASK, 0xff);
+	if (ret < 0)
+		goto err2;
+
+	return 0;
+
+err2:
+	input_unregister_device(input);
+err1:
+	input_free_device(input);
+	return ret;
+}
+
+static int __devexit smsc_remove(struct platform_device *pdev)
+{
+	struct smsc_keypad *kp = platform_get_drvdata(pdev);
+	input_unregister_device(kp->input);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id smsc_keypad_dt_match[] = {
+	{ .compatible = "smsc,keypad" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, smsc_keypad_dt_match);
+#endif
+
+static struct platform_driver smsc_driver = {
+	.driver = {
+		.name	= "smsc-keypad",
+		.of_match_table = of_match_ptr(smsc_keypad_dt_match),
+		.owner  = THIS_MODULE,
+	},
+	.probe		= smsc_probe,
+	.remove		= __devexit_p(smsc_remove),
+};
+
+module_platform_driver(smsc_driver);
+
+MODULE_AUTHOR("G Kondaiah Manjunath <manjugk@ti.com>");
+MODULE_DESCRIPTION("SMSC ECE1099 Keypad driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.1

             reply	other threads:[~2012-09-05 11:36 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-09-05 11:36 Sourav Poddar [this message]
2012-09-05 11:36 ` [PATCHv2 2/4] Input: keypad: Add smsc ece1099 keypad driver Sourav Poddar
2012-09-05 11:36 ` Sourav Poddar
2012-09-05 18:31 ` Vaibhav Hiremath
2012-09-05 18:31   ` Vaibhav Hiremath
2012-09-05 18:31   ` Vaibhav Hiremath
     [not found]   ` <CAKdam55GZBo7Gx5MRkX-Wia9nXmtFAhanhmyCky7ikPQjoSOfw@mail.gmail.com>
2012-09-06  5:38     ` Poddar, Sourav
2012-09-06  5:38       ` Poddar, Sourav

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1346844997-24868-1-git-send-email-sourav.poddar@ti.com \
    --to=sourav.poddar@ti.com \
    --cc=b-cousson@ti.com \
    --cc=balbi@ti.com \
    --cc=devicetree-discuss@lists.ozlabs.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=santosh.shilimkar@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.