Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 260778
b: refs/heads/master
c: fd05d08
h: refs/heads/master
v: v3
  • Loading branch information
David Jander authored and Dmitry Torokhov committed Jul 10, 2011
1 parent 4509d84 commit 12750da
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 17 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: eadba0c9c41bfa840b0f1e9ee369a869c2a322b3
refs/heads/master: fd05d08920b54d189aa247c5c5701a08e539ed0b
36 changes: 36 additions & 0 deletions trunk/Documentation/devicetree/bindings/gpio/gpio_keys.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Device-Tree bindings for input/gpio_keys.c keyboard driver

Required properties:
- compatible = "gpio-keys";

Optional properties:
- autorepeat: Boolean, Enable auto repeat feature of Linux input
subsystem.

Each button (key) is represented as a sub-node of "gpio-keys":
Subnode properties:

- gpios: OF devcie-tree gpio specificatin.
- label: Descriptive name of the key.
- linux,code: Keycode to emit.

Optional subnode-properties:
- linux,input-type: Specify event type this button/key generates.
If not specified defaults to <1> == EV_KEY.
- debounce-interval: Debouncing interval time in milliseconds.
If not specified defaults to 5.
- gpio-key,wakeup: Boolean, button can wake-up the system.

Example nodes:

gpio_keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
button@21 {
label = "GPIO Key UP";
linux,code = <103>;
gpios = <&gpio1 0 1>;
};
...
148 changes: 132 additions & 16 deletions trunk/drivers/input/keyboard/gpio_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Driver for keys on GPIO lines capable of generating interrupts.
*
* Copyright 2005 Phil Blundell
* Copyright 2010, 2011 David Jander <david@protonic.nl>
*
* 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
Expand All @@ -25,6 +26,8 @@
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>

struct gpio_button_data {
struct gpio_keys_button *button;
Expand Down Expand Up @@ -445,15 +448,120 @@ static void gpio_keys_close(struct input_dev *input)
ddata->disable(input->dev.parent);
}

/*
* Handlers for alternative sources of platform_data
*/
#ifdef CONFIG_OF
/*
* Translate OpenFirmware node properties into platform_data
*/
static int gpio_keys_get_devtree_pdata(struct device *dev,
struct gpio_keys_platform_data *pdata)
{
struct device_node *node, *pp;
int i;
struct gpio_keys_button *buttons;
const u32 *reg;
int len;

node = dev->of_node;
if (node == NULL)
return -ENODEV;

memset(pdata, 0, sizeof *pdata);

pdata->rep = !!of_get_property(node, "autorepeat", &len);

/* First count the subnodes */
pdata->nbuttons = 0;
pp = NULL;
while ((pp = of_get_next_child(node, pp)))
pdata->nbuttons++;

if (pdata->nbuttons == 0)
return -ENODEV;

buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL);
if (!buttons)
return -ENODEV;

pp = NULL;
i = 0;
while ((pp = of_get_next_child(node, pp))) {
enum of_gpio_flags flags;

if (!of_find_property(pp, "gpios", NULL)) {
pdata->nbuttons--;
dev_warn(dev, "Found button without gpios\n");
continue;
}
buttons[i].gpio = of_get_gpio_flags(pp, 0, &flags);
buttons[i].active_low = flags & OF_GPIO_ACTIVE_LOW;

reg = of_get_property(pp, "linux,code", &len);
if (!reg) {
dev_err(dev, "Button without keycode: 0x%x\n", buttons[i].gpio);
goto out_fail;
}
buttons[i].code = be32_to_cpup(reg);

buttons[i].desc = of_get_property(pp, "label", &len);

reg = of_get_property(pp, "linux,input-type", &len);
buttons[i].type = reg ? be32_to_cpup(reg) : EV_KEY;

buttons[i].wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);

reg = of_get_property(pp, "debounce-interval", &len);
buttons[i].debounce_interval = reg ? be32_to_cpup(reg) : 5;

i++;
}

pdata->buttons = buttons;

return 0;

out_fail:
kfree(buttons);
return -ENODEV;
}

static struct of_device_id gpio_keys_of_match[] = {
{ .compatible = "gpio-keys", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_of_match);

#else

static int gpio_keys_get_devtree_pdata(struct device *dev,
struct gpio_keys_platform_data *altp)
{
return -ENODEV;
}

#define gpio_keys_of_match NULL

#endif

static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
struct device *dev = &pdev->dev;
struct gpio_keys_platform_data alt_pdata;
struct input_dev *input;
int i, error;
int wakeup = 0;

if (!pdata) {
error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
if (error)
return error;
pdata = &alt_pdata;
}

ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
Expand Down Expand Up @@ -544,13 +652,15 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
fail1:
input_free_device(input);
kfree(ddata);
/* If we have no platform_data, we allocated buttons dynamically. */
if (!pdev->dev.platform_data)
kfree(pdata->buttons);

return error;
}

static int __devexit gpio_keys_remove(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
struct input_dev *input = ddata->input;
int i;
Expand All @@ -559,32 +669,39 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)

device_init_wakeup(&pdev->dev, 0);

for (i = 0; i < pdata->nbuttons; i++) {
int irq = gpio_to_irq(pdata->buttons[i].gpio);
for (i = 0; i < ddata->n_buttons; i++) {
int irq = gpio_to_irq(ddata->data[i].button->gpio);
free_irq(irq, &ddata->data[i]);
if (ddata->data[i].timer_debounce)
del_timer_sync(&ddata->data[i].timer);
cancel_work_sync(&ddata->data[i].work);
gpio_free(pdata->buttons[i].gpio);
gpio_free(ddata->data[i].button->gpio);
}

input_unregister_device(input);

/*
* If we had no platform_data, we allocated buttons dynamically, and
* must free them here. ddata->data[0].button is the pointer to the
* beginning of the allocated array.
*/
if (!pdev->dev.platform_data)
kfree(ddata->data[0].button);

kfree(ddata);

return 0;
}


#ifdef CONFIG_PM
static int gpio_keys_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
int i;

if (device_may_wakeup(&pdev->dev)) {
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
if (device_may_wakeup(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);
enable_irq_wake(irq);
Expand All @@ -597,15 +714,13 @@ static int gpio_keys_suspend(struct device *dev)

static int gpio_keys_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
int i;

for (i = 0; i < pdata->nbuttons; i++) {
for (i = 0; i < ddata->n_buttons; i++) {

struct gpio_keys_button *button = &pdata->buttons[i];
if (button->wakeup && device_may_wakeup(&pdev->dev)) {
struct gpio_keys_button *button = ddata->data[i].button;
if (button->wakeup && device_may_wakeup(dev)) {
int irq = gpio_to_irq(button->gpio);
disable_irq_wake(irq);
}
Expand All @@ -632,6 +747,7 @@ static struct platform_driver gpio_keys_device_driver = {
#ifdef CONFIG_PM
.pm = &gpio_keys_pm_ops,
#endif
.of_match_table = gpio_keys_of_match,
}
};

Expand Down

0 comments on commit 12750da

Please sign in to comment.