Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 297783
b: refs/heads/master
c: d8ee4a1
h: refs/heads/master
i:
  297781: 07e35ab
  297779: 4ae4e10
  297775: 7d70f9b
v: v3
  • Loading branch information
Laxman Dewangan authored and Dmitry Torokhov committed Mar 20, 2012
1 parent d4fa2d9 commit e66e492
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 68 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a16ca23935afc0d72215b139720bd07df3162a9f
refs/heads/master: d8ee4a1c90529ed06e1aa43d034986649f7b670b
214 changes: 148 additions & 66 deletions trunk/drivers/input/keyboard/gpio_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@
#include <linux/gpio.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/spinlock.h>

struct gpio_button_data {
const struct gpio_keys_button *button;
struct input_dev *input;
struct timer_list timer;
struct work_struct work;
int timer_debounce; /* in msecs */
unsigned int timer_debounce; /* in msecs */
unsigned int irq;
spinlock_t lock;
bool disabled;
bool key_pressed;
};

struct gpio_keys_drvdata {
Expand Down Expand Up @@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
/*
* Disable IRQ and possible debouncing timer.
*/
disable_irq(gpio_to_irq(bdata->button->gpio));
disable_irq(bdata->irq);
if (bdata->timer_debounce)
del_timer_sync(&bdata->timer);

Expand All @@ -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)
{
if (bdata->disabled) {
enable_irq(gpio_to_irq(bdata->button->gpio));
enable_irq(bdata->irq);
bdata->disabled = false;
}
}
Expand Down Expand Up @@ -320,7 +324,7 @@ static struct attribute_group gpio_keys_attr_group = {
.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;
struct input_dev *input = bdata->input;
Expand All @@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
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 =
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;
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)
mod_timer(&bdata->timer,
Expand All @@ -367,73 +370,153 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
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,
struct input_dev *input,
struct gpio_button_data *bdata,
const struct gpio_keys_button *button)
{
const char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev;
irq_handler_t isr;
unsigned long irqflags;
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->button = button;
spin_lock_init(&bdata->lock);

error = gpio_request(button->gpio, desc);
if (error < 0) {
dev_err(dev, "failed to request GPIO %d, error %d\n",
button->gpio, error);
goto fail2;
}
if (gpio_is_valid(button->gpio)) {

error = gpio_direction_input(button->gpio);
if (error < 0) {
dev_err(dev, "failed to configure"
" direction for GPIO %d, error %d\n",
button->gpio, error);
goto fail3;
}
error = gpio_request(button->gpio, desc);
if (error < 0) {
dev_err(dev, "Failed to request GPIO %d, error %d\n",
button->gpio, error);
return error;
}

if (button->debounce_interval) {
error = gpio_set_debounce(button->gpio,
button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */
if (error < 0)
bdata->timer_debounce = button->debounce_interval;
}
error = gpio_direction_input(button->gpio);
if (error < 0) {
dev_err(dev,
"Failed to configure direction for GPIO %d, error %d\n",
button->gpio, error);
goto fail;
}

irq = gpio_to_irq(button->gpio);
if (irq < 0) {
error = irq;
dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
button->gpio, error);
goto fail3;
if (button->debounce_interval) {
error = gpio_set_debounce(button->gpio,
button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */
if (error < 0)
bdata->timer_debounce =
button->debounce_interval;
}

irq = gpio_to_irq(button->gpio);
if (irq < 0) {
error = irq;
dev_err(dev,
"Unable to get irq number for GPIO %d, error %d\n",
button->gpio, error);
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;

} 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;
}

irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
input_set_capability(input, button->type ?: EV_KEY, button->code);

/*
* If platform has specified that the button can be disabled,
* we don't want it to share the interrupt line.
*/
if (!button->can_disable)
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) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
irq, error);
goto fail3;
bdata->irq, error);
goto fail;
}

input_set_capability(input, button->type ?: EV_KEY, button->code);
return 0;

fail3:
gpio_free(button->gpio);
fail2:
fail:
if (gpio_is_valid(button->gpio))
gpio_free(button->gpio);

return error;
}

Expand Down Expand Up @@ -553,11 +636,12 @@ static int gpio_keys_get_devtree_pdata(struct device *dev,

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)
del_timer_sync(&bdata->timer);
cancel_work_sync(&bdata->work);
gpio_free(bdata->button->gpio);
if (gpio_is_valid(bdata->button->gpio))
gpio_free(bdata->button->gpio);
}

static int __devinit gpio_keys_probe(struct platform_device *pdev)
Expand Down Expand Up @@ -637,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
goto fail3;
}

/* get current state of buttons */
for (i = 0; i < pdata->nbuttons; i++)
gpio_keys_report_event(&ddata->data[i]);
/* get current state of buttons that are connected to GPIOs */
for (i = 0; i < pdata->nbuttons; 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);

device_init_wakeup(&pdev->dev, wakeup);
Expand Down Expand Up @@ -695,16 +782,13 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
static int gpio_keys_suspend(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
const struct gpio_keys_button *button;
int i;

if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->n_buttons; i++) {
button = ddata->data[i].button;
if (button->wakeup) {
int irq = gpio_to_irq(button->gpio);
enable_irq_wake(irq);
}
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->wakeup)
enable_irq_wake(bdata->irq);
}
}

Expand All @@ -714,17 +798,15 @@ static int gpio_keys_suspend(struct device *dev)
static int gpio_keys_resume(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
const struct gpio_keys_button *button;
int i;

for (i = 0; i < ddata->n_buttons; i++) {
button = ddata->data[i].button;
if (button->wakeup && device_may_wakeup(dev)) {
int irq = gpio_to_irq(button->gpio);
disable_irq_wake(irq);
}
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->wakeup && device_may_wakeup(dev))
disable_irq_wake(bdata->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);

Expand Down
3 changes: 2 additions & 1 deletion trunk/include/linux/gpio_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ 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 active_low;
const char *desc;
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */
int wakeup; /* configure the button as a wake-up source */
int debounce_interval; /* debounce ticks interval in msecs */
bool can_disable;
int value; /* axis value for EV_ABS */
unsigned int irq; /* Irq number in case of interrupt keys */
};

struct gpio_keys_platform_data {
Expand Down

0 comments on commit e66e492

Please sign in to comment.