#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 {
                /*
                 * 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);
 
 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;
        }
 }
        .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;
        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,
        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 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;
 }
 
 
 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)
                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);
 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);
                }
        }
 
 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);