mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Laxman Dewangan <ldewangan@nvidia.com>
To: dmitry.torokhov@gmail.com, grant.likely@secretlab.ca,
	linus.walleij@stericsson.com, david@protonic.nl,
	tklauser@distanz.ch, alexander.stein@informatik.tu-chemnitz.de
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	ldewangan@nvidia.com
Subject: [PATCH V1] input: keyboard: gpio: Support for interrupt only key
Date: Wed, 14 Mar 2012 14:22:03 +0530	[thread overview]
Message-ID: <1331715123-15099-1-git-send-email-ldewangan@nvidia.com> (raw)

Some of key in system generates interrupt only when it
pressed like power-on key or onkey. These keys do not
actually mapped as gpio in system.
Supporting the key pressed event on such keys which are
not actually gpio keys but generates interrupt only.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
When I pushed the new driver for interrupt key, the suggestion came
to include this functionality in the gpio-key driver. I added require
changed in gpio-keys.c to support interrupt key only.

 drivers/input/keyboard/gpio_keys.c |  117 +++++++++++++++++++++++++++++++++--
 include/linux/gpio_keys.h          |    3 +-
 2 files changed, 112 insertions(+), 8 deletions(-)

diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index ed1ed46..d38ddfd 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -28,6 +28,7 @@
 #include <linux/gpio.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
+#include <linux/spinlock.h>
 
 struct gpio_button_data {
 	struct gpio_keys_button *button;
@@ -36,6 +37,8 @@ struct gpio_button_data {
 	struct work_struct work;
 	int timer_debounce;	/* in msecs */
 	bool disabled;
+	spinlock_t lock;
+	bool key_pressed;
 };
 
 struct gpio_keys_drvdata {
@@ -95,6 +98,13 @@ static inline int get_n_events_by_type(int type)
 	return (type == EV_KEY) ? KEY_CNT : SW_CNT;
 }
 
+static int button_irq(struct gpio_keys_button *button)
+{
+	if (gpio_is_valid(button->gpio))
+		return gpio_to_irq(button->gpio);
+	return button->irq;
+}
+
 /**
  * gpio_keys_disable_button() - disables given GPIO button
  * @bdata: button data for button to be disabled
@@ -111,10 +121,11 @@ static inline int get_n_events_by_type(int type)
 static void gpio_keys_disable_button(struct gpio_button_data *bdata)
 {
 	if (!bdata->disabled) {
+		int irq = button_irq(bdata->button);
 		/*
 		 * Disable IRQ and possible debouncing timer.
 		 */
-		disable_irq(gpio_to_irq(bdata->button->gpio));
+		disable_irq(irq);
 		if (bdata->timer_debounce)
 			del_timer_sync(&bdata->timer);
 
@@ -135,7 +146,8 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
 static void gpio_keys_enable_button(struct gpio_button_data *bdata)
 {
 	if (bdata->disabled) {
-		enable_irq(gpio_to_irq(bdata->button->gpio));
+		int irq = button_irq(bdata->button);
+		enable_irq(irq);
 		bdata->disabled = false;
 	}
 }
@@ -325,8 +337,12 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
 	struct gpio_keys_button *button = bdata->button;
 	struct input_dev *input = bdata->input;
 	unsigned int type = button->type ?: EV_KEY;
-	int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
+	int state;
 
+	if (!gpio_is_valid(button->gpio))
+		return;
+
+	state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
 	if (type == EV_ABS) {
 		if (state)
 			input_event(input, type, button->code, button->value);
@@ -367,6 +383,85 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void interrupt_keys_timer(unsigned long _data)
+{
+	struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
+	struct gpio_keys_button *button = bdata->button;
+	struct input_dev *input = bdata->input;
+	unsigned int type = button->type ?: EV_KEY;
+	unsigned long iflags;
+
+	spin_lock_irqsave(&bdata->lock, iflags);
+	if (bdata->key_pressed) {
+		input_event(input, type, button->code, 0);
+		input_sync(input);
+		bdata->key_pressed = false;
+	}
+	spin_unlock_irqrestore(&bdata->lock, iflags);
+}
+
+static irqreturn_t interrupt_keys_isr(int irq, void *dev_id)
+{
+	struct gpio_button_data *bdata = dev_id;
+	struct gpio_keys_button *button = bdata->button;
+	struct input_dev *input = bdata->input;
+	int type = button->type ?: EV_KEY;
+	unsigned long iflags;
+
+	BUG_ON(irq != button->irq);
+
+	spin_lock_irqsave(&bdata->lock, iflags);
+	if (!bdata->key_pressed) {
+		input_event(input, type, button->code, 1);
+		input_sync(input);
+		if (!bdata->timer_debounce) {
+			input_event(input, type, button->code, 0);
+			input_sync(input);
+			spin_unlock_irqrestore(&bdata->lock, iflags);
+			return IRQ_HANDLED;
+		}
+		bdata->key_pressed = true;
+	}
+
+	spin_unlock_irqrestore(&bdata->lock, iflags);
+	if (bdata->timer_debounce)
+		mod_timer(&bdata->timer,
+			jiffies + msecs_to_jiffies(bdata->timer_debounce));
+	return IRQ_HANDLED;
+}
+
+static int __devinit interrupt_keys_setup_key(struct platform_device *pdev,
+					 struct gpio_button_data *bdata,
+					 struct gpio_keys_button *button)
+{
+	const char *desc = button->desc ? button->desc : "gpio_keys";
+	struct device *dev = &pdev->dev;
+	unsigned long irqflags = 0;
+	int irq, error;
+
+	if (button->debounce_interval) {
+		setup_timer(&bdata->timer, interrupt_keys_timer,
+						(unsigned long)bdata);
+		bdata->timer_debounce = button->debounce_interval;
+	}
+	spin_lock_init(&bdata->lock);
+	irq = button->irq;
+	if (irq <= 0) {
+		dev_err(dev, "%s(): Invalid irq number %d\n", __func__, irq);
+		return -EINVAL;
+	}
+	bdata->key_pressed = false;
+	if (!button->can_disable)
+		irqflags |= IRQF_SHARED;
+
+	error = request_threaded_irq(irq, NULL, interrupt_keys_isr,
+					irqflags, desc, bdata);
+	if (error < 0)
+		dev_err(dev, "Unable to claim irq %d; error %d\n",
+				irq, error);
+	return error;
+}
+
 static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
 					 struct gpio_button_data *bdata,
 					 struct gpio_keys_button *button)
@@ -376,6 +471,9 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
 	unsigned long irqflags;
 	int irq, error;
 
+	if (!gpio_is_valid(button->gpio))
+		return interrupt_keys_setup_key(pdev, bdata, button);
+
 	setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
 	INIT_WORK(&bdata->work, gpio_keys_work_func);
 
@@ -643,9 +741,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
 	sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
  fail2:
 	while (--i >= 0) {
-		free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
+		int irq = button_irq(&pdata->buttons[i]);
+		free_irq(irq, &ddata->data[i]);
 		if (ddata->data[i].timer_debounce)
 			del_timer_sync(&ddata->data[i].timer);
+		if (!gpio_is_valid(pdata->buttons[i].gpio))
+			continue;
 		cancel_work_sync(&ddata->data[i].work);
 		gpio_free(pdata->buttons[i].gpio);
 	}
@@ -672,10 +773,12 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
 	device_init_wakeup(&pdev->dev, 0);
 
 	for (i = 0; i < ddata->n_buttons; i++) {
-		int irq = gpio_to_irq(ddata->data[i].button->gpio);
+		int irq = button_irq(ddata->data[i].button);
 		free_irq(irq, &ddata->data[i]);
 		if (ddata->data[i].timer_debounce)
 			del_timer_sync(&ddata->data[i].timer);
+		if (!gpio_is_valid(ddata->data[i].button->gpio))
+			continue;
 		cancel_work_sync(&ddata->data[i].work);
 		gpio_free(ddata->data[i].button->gpio);
 	}
@@ -705,7 +808,7 @@ static int gpio_keys_suspend(struct device *dev)
 		for (i = 0; i < ddata->n_buttons; i++) {
 			struct gpio_keys_button *button = ddata->data[i].button;
 			if (button->wakeup) {
-				int irq = gpio_to_irq(button->gpio);
+				int irq = button_irq(button);
 				enable_irq_wake(irq);
 			}
 		}
@@ -723,7 +826,7 @@ static int gpio_keys_resume(struct device *dev)
 
 		struct gpio_keys_button *button = ddata->data[i].button;
 		if (button->wakeup && device_may_wakeup(dev)) {
-			int irq = gpio_to_irq(button->gpio);
+			int irq = button_irq(button);
 			disable_irq_wake(irq);
 		}
 
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 004ff33..dddcc95 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -6,7 +6,8 @@ struct device;
 struct gpio_keys_button {
 	/* Configuration parameters */
 	unsigned int code;	/* input event code (KEY_*, SW_*) */
-	int gpio;
+	int gpio;		/* -1 if this key does not support gpio */
+	int irq;		/* Irq number in case of interrupt keys */
 	int active_low;
 	const char *desc;
 	unsigned int type;	/* input event type (EV_KEY, EV_SW, EV_ABS) */
-- 
1.7.1.1


             reply	other threads:[~2012-03-14  8:56 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-14  8:52 Laxman Dewangan [this message]
2012-03-14 16:00 ` Dmitry Torokhov
2012-03-19  6:06   ` Dmitry Torokhov
2012-03-19  7:24     ` Laxman Dewangan

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=1331715123-15099-1-git-send-email-ldewangan@nvidia.com \
    --to=ldewangan@nvidia.com \
    --cc=alexander.stein@informatik.tu-chemnitz.de \
    --cc=david@protonic.nl \
    --cc=dmitry.torokhov@gmail.com \
    --cc=grant.likely@secretlab.ca \
    --cc=linus.walleij@stericsson.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=tklauser@distanz.ch \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®