| From 021bf10e7177aa806915d634054101dd9e60d7fd Mon Sep 17 00:00:00 2001 |
| From: David Jander <david@protonic.nl> |
| Date: Sat, 9 Jul 2011 12:41:46 -0700 |
| Subject: Input: gpio_keys - add support for device-tree platform data |
| |
| This patch enables fetching configuration data, which is normally provided |
| via platform_data, from the device-tree instead. |
| |
| If the device is configured from device-tree data, the platform_data struct |
| is not used, and button data needs to be allocated dynamically. Big part of |
| this patch deals with confining pdata usage to the probe function, to make |
| this possible. |
| |
| Signed-off-by: David Jander <david@protonic.nl> |
| Signed-off-by: Dmitry Torokhov <dtor@mail.ru> |
| (cherry picked from commit fd05d08920b54d189aa247c5c5701a08e539ed0b) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| .../devicetree/bindings/gpio/gpio_keys.txt | 36 +++++ |
| drivers/input/keyboard/gpio_keys.c | 148 +++++++++++++++++--- |
| 2 files changed, 168 insertions(+), 16 deletions(-) |
| create mode 100644 Documentation/devicetree/bindings/gpio/gpio_keys.txt |
| |
| diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt |
| new file mode 100644 |
| index 0000000..7190c99 |
| --- /dev/null |
| +++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt |
| @@ -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>; |
| + }; |
| + ... |
| diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c |
| index 97bada4..ad11e86 100644 |
| --- a/drivers/input/keyboard/gpio_keys.c |
| +++ b/drivers/input/keyboard/gpio_keys.c |
| @@ -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 |
| @@ -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; |
| @@ -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); |
| @@ -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; |
| @@ -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); |
| @@ -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); |
| } |
| @@ -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, |
| } |
| }; |
| |
| -- |
| 1.7.10 |
| |