Commit d8ee4a1c authored by Laxman Dewangan's avatar Laxman Dewangan Committed by Dmitry Torokhov

Input: gpio_keys - add support for interrupt only keys

Some of buttons, like power-on key or onkey, may only generate interrupts
when pressed and not actually be mapped as gpio in the system. Allow
setting gpio to invalid value and specify IRQ instead to support such
keys. The debounce timer is used not to debounce but to ignore new IRQs
coming while button is kept pressed.
Signed-off-by: default avatarLaxman Dewangan <ldewangan@nvidia.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent a16ca239
...@@ -28,14 +28,18 @@ ...@@ -28,14 +28,18 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/spinlock.h>
struct gpio_button_data { struct gpio_button_data {
const struct gpio_keys_button *button; const struct gpio_keys_button *button;
struct input_dev *input; struct input_dev *input;
struct timer_list timer; struct timer_list timer;
struct work_struct work; struct work_struct work;
int timer_debounce; /* in msecs */ unsigned int timer_debounce; /* in msecs */
unsigned int irq;
spinlock_t lock;
bool disabled; bool disabled;
bool key_pressed;
}; };
struct gpio_keys_drvdata { struct gpio_keys_drvdata {
...@@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) ...@@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
/* /*
* Disable IRQ and possible debouncing timer. * Disable IRQ and possible debouncing timer.
*/ */
disable_irq(gpio_to_irq(bdata->button->gpio)); disable_irq(bdata->irq);
if (bdata->timer_debounce) if (bdata->timer_debounce)
del_timer_sync(&bdata->timer); del_timer_sync(&bdata->timer);
...@@ -135,7 +139,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) ...@@ -135,7 +139,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
static void gpio_keys_enable_button(struct gpio_button_data *bdata) static void gpio_keys_enable_button(struct gpio_button_data *bdata)
{ {
if (bdata->disabled) { if (bdata->disabled) {
enable_irq(gpio_to_irq(bdata->button->gpio)); enable_irq(bdata->irq);
bdata->disabled = false; bdata->disabled = false;
} }
} }
...@@ -320,7 +324,7 @@ static struct attribute_group gpio_keys_attr_group = { ...@@ -320,7 +324,7 @@ static struct attribute_group gpio_keys_attr_group = {
.attrs = gpio_keys_attrs, .attrs = gpio_keys_attrs,
}; };
static void gpio_keys_report_event(struct gpio_button_data *bdata) static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{ {
const struct gpio_keys_button *button = bdata->button; const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input; struct input_dev *input = bdata->input;
...@@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) ...@@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
input_sync(input); input_sync(input);
} }
static void gpio_keys_work_func(struct work_struct *work) static void gpio_keys_gpio_work_func(struct work_struct *work)
{ {
struct gpio_button_data *bdata = struct gpio_button_data *bdata =
container_of(work, struct gpio_button_data, work); container_of(work, struct gpio_button_data, work);
gpio_keys_report_event(bdata); gpio_keys_gpio_report_event(bdata);
} }
static void gpio_keys_timer(unsigned long _data) static void gpio_keys_gpio_timer(unsigned long _data)
{ {
struct gpio_button_data *data = (struct gpio_button_data *)_data; struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
schedule_work(&data->work); schedule_work(&bdata->work);
} }
static irqreturn_t gpio_keys_isr(int irq, void *dev_id) static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{ {
struct gpio_button_data *bdata = dev_id; struct gpio_button_data *bdata = dev_id;
const struct gpio_keys_button *button = bdata->button;
BUG_ON(irq != gpio_to_irq(button->gpio)); BUG_ON(irq != bdata->irq);
if (bdata->timer_debounce) if (bdata->timer_debounce)
mod_timer(&bdata->timer, mod_timer(&bdata->timer,
...@@ -367,6 +370,53 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) ...@@ -367,6 +370,53 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void gpio_keys_irq_timer(unsigned long _data)
{
struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
struct input_dev *input = bdata->input;
unsigned long flags;
spin_lock_irqsave(&bdata->lock, flags);
if (bdata->key_pressed) {
input_event(input, EV_KEY, bdata->button->code, 0);
input_sync(input);
bdata->key_pressed = false;
}
spin_unlock_irqrestore(&bdata->lock, flags);
}
static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned long flags;
BUG_ON(irq != bdata->irq);
spin_lock_irqsave(&bdata->lock, flags);
if (!bdata->key_pressed) {
input_event(input, EV_KEY, button->code, 1);
input_sync(input);
if (!bdata->timer_debounce) {
input_event(input, EV_KEY, button->code, 0);
input_sync(input);
goto out;
}
bdata->key_pressed = true;
}
if (bdata->timer_debounce)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(bdata->timer_debounce));
out:
spin_unlock_irqrestore(&bdata->lock, flags);
return IRQ_HANDLED;
}
static int __devinit gpio_keys_setup_key(struct platform_device *pdev, static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct input_dev *input, struct input_dev *input,
struct gpio_button_data *bdata, struct gpio_button_data *bdata,
...@@ -374,27 +424,29 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, ...@@ -374,27 +424,29 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
{ {
const char *desc = button->desc ? button->desc : "gpio_keys"; const char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
irq_handler_t isr;
unsigned long irqflags; unsigned long irqflags;
int irq, error; int irq, error;
setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
INIT_WORK(&bdata->work, gpio_keys_work_func);
bdata->input = input; bdata->input = input;
bdata->button = button; bdata->button = button;
spin_lock_init(&bdata->lock);
if (gpio_is_valid(button->gpio)) {
error = gpio_request(button->gpio, desc); error = gpio_request(button->gpio, desc);
if (error < 0) { if (error < 0) {
dev_err(dev, "failed to request GPIO %d, error %d\n", dev_err(dev, "Failed to request GPIO %d, error %d\n",
button->gpio, error); button->gpio, error);
goto fail2; return error;
} }
error = gpio_direction_input(button->gpio); error = gpio_direction_input(button->gpio);
if (error < 0) { if (error < 0) {
dev_err(dev, "failed to configure" dev_err(dev,
" direction for GPIO %d, error %d\n", "Failed to configure direction for GPIO %d, error %d\n",
button->gpio, error); button->gpio, error);
goto fail3; goto fail;
} }
if (button->debounce_interval) { if (button->debounce_interval) {
...@@ -402,18 +454,49 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, ...@@ -402,18 +454,49 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
button->debounce_interval * 1000); button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */ /* use timer if gpiolib doesn't provide debounce */
if (error < 0) if (error < 0)
bdata->timer_debounce = button->debounce_interval; bdata->timer_debounce =
button->debounce_interval;
} }
irq = gpio_to_irq(button->gpio); irq = gpio_to_irq(button->gpio);
if (irq < 0) { if (irq < 0) {
error = irq; error = irq;
dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", dev_err(dev,
"Unable to get irq number for GPIO %d, error %d\n",
button->gpio, error); button->gpio, error);
goto fail3; goto fail;
} }
bdata->irq = irq;
INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
setup_timer(&bdata->timer,
gpio_keys_gpio_timer, (unsigned long)bdata);
isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
} else {
if (!button->irq) {
dev_err(dev, "No IRQ specified\n");
return -EINVAL;
}
bdata->irq = button->irq;
if (button->type && button->type != EV_KEY) {
dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
return -EINVAL;
}
bdata->timer_debounce = button->debounce_interval;
setup_timer(&bdata->timer,
gpio_keys_irq_timer, (unsigned long)bdata);
isr = gpio_keys_irq_isr;
irqflags = 0;
}
input_set_capability(input, button->type ?: EV_KEY, button->code);
/* /*
* If platform has specified that the button can be disabled, * If platform has specified that the button can be disabled,
* we don't want it to share the interrupt line. * we don't want it to share the interrupt line.
...@@ -421,19 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, ...@@ -421,19 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
if (!button->can_disable) if (!button->can_disable)
irqflags |= IRQF_SHARED; irqflags |= IRQF_SHARED;
error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata); error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata);
if (error < 0) { if (error < 0) {
dev_err(dev, "Unable to claim irq %d; error %d\n", dev_err(dev, "Unable to claim irq %d; error %d\n",
irq, error); bdata->irq, error);
goto fail3; goto fail;
} }
input_set_capability(input, button->type ?: EV_KEY, button->code);
return 0; return 0;
fail3: fail:
if (gpio_is_valid(button->gpio))
gpio_free(button->gpio); gpio_free(button->gpio);
fail2:
return error; return error;
} }
...@@ -553,10 +636,11 @@ static int gpio_keys_get_devtree_pdata(struct device *dev, ...@@ -553,10 +636,11 @@ static int gpio_keys_get_devtree_pdata(struct device *dev,
static void gpio_remove_key(struct gpio_button_data *bdata) static void gpio_remove_key(struct gpio_button_data *bdata)
{ {
free_irq(gpio_to_irq(bdata->button->gpio), bdata); free_irq(bdata->irq, bdata);
if (bdata->timer_debounce) if (bdata->timer_debounce)
del_timer_sync(&bdata->timer); del_timer_sync(&bdata->timer);
cancel_work_sync(&bdata->work); cancel_work_sync(&bdata->work);
if (gpio_is_valid(bdata->button->gpio))
gpio_free(bdata->button->gpio); gpio_free(bdata->button->gpio);
} }
...@@ -637,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) ...@@ -637,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
goto fail3; goto fail3;
} }
/* get current state of buttons */ /* get current state of buttons that are connected to GPIOs */
for (i = 0; i < pdata->nbuttons; i++) for (i = 0; i < pdata->nbuttons; i++) {
gpio_keys_report_event(&ddata->data[i]); struct gpio_button_data *bdata = &ddata->data[i];
if (gpio_is_valid(bdata->button->gpio))
gpio_keys_gpio_report_event(bdata);
}
input_sync(input); input_sync(input);
device_init_wakeup(&pdev->dev, wakeup); device_init_wakeup(&pdev->dev, wakeup);
...@@ -695,16 +782,13 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) ...@@ -695,16 +782,13 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
static int gpio_keys_suspend(struct device *dev) static int gpio_keys_suspend(struct device *dev)
{ {
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
const struct gpio_keys_button *button;
int i; int i;
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->n_buttons; i++) { for (i = 0; i < ddata->n_buttons; i++) {
button = ddata->data[i].button; struct gpio_button_data *bdata = &ddata->data[i];
if (button->wakeup) { if (bdata->button->wakeup)
int irq = gpio_to_irq(button->gpio); enable_irq_wake(bdata->irq);
enable_irq_wake(irq);
}
} }
} }
...@@ -714,17 +798,15 @@ static int gpio_keys_suspend(struct device *dev) ...@@ -714,17 +798,15 @@ static int gpio_keys_suspend(struct device *dev)
static int gpio_keys_resume(struct device *dev) static int gpio_keys_resume(struct device *dev)
{ {
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
const struct gpio_keys_button *button;
int i; int i;
for (i = 0; i < ddata->n_buttons; i++) { for (i = 0; i < ddata->n_buttons; i++) {
button = ddata->data[i].button; struct gpio_button_data *bdata = &ddata->data[i];
if (button->wakeup && device_may_wakeup(dev)) { if (bdata->button->wakeup && device_may_wakeup(dev))
int irq = gpio_to_irq(button->gpio); disable_irq_wake(bdata->irq);
disable_irq_wake(irq);
}
gpio_keys_report_event(&ddata->data[i]); if (gpio_is_valid(bdata->button->gpio))
gpio_keys_gpio_report_event(bdata);
} }
input_sync(ddata->input); input_sync(ddata->input);
......
...@@ -6,7 +6,7 @@ struct device; ...@@ -6,7 +6,7 @@ struct device;
struct gpio_keys_button { struct gpio_keys_button {
/* Configuration parameters */ /* Configuration parameters */
unsigned int code; /* input event code (KEY_*, SW_*) */ unsigned int code; /* input event code (KEY_*, SW_*) */
int gpio; int gpio; /* -1 if this key does not support gpio */
int active_low; int active_low;
const char *desc; const char *desc;
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */
...@@ -14,6 +14,7 @@ struct gpio_keys_button { ...@@ -14,6 +14,7 @@ struct gpio_keys_button {
int debounce_interval; /* debounce ticks interval in msecs */ int debounce_interval; /* debounce ticks interval in msecs */
bool can_disable; bool can_disable;
int value; /* axis value for EV_ABS */ int value; /* axis value for EV_ABS */
unsigned int irq; /* Irq number in case of interrupt keys */
}; };
struct gpio_keys_platform_data { struct gpio_keys_platform_data {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment