Re: [PATCH v4 2/3] Input: gpio_keys.c: Added support for device-tree platform data

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Grant,

On Thu, 16 Jun 2011 13:25:59 -0600
Grant Likely <grant.likely@xxxxxxxxxxxx> wrote:

> On Tue, Jun 14, 2011 at 11:08:10AM +0200, David Jander wrote:
> > 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@xxxxxxxxxxx>
> > ---
> >  .../devicetree/bindings/gpio/gpio_keys.txt         |   49 +++++++
> >  drivers/input/keyboard/gpio_keys.c                 |  147
> > ++++++++++++++++++-- 2 files changed, 186 insertions(+), 10 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..60a4d8e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
> > @@ -0,0 +1,49 @@
> > +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:
> > +
> > +	- reg: GPIO number the key is bound to if linux GPIO numbering is
> > used.
> 
> Wait.  That won't work.  There is no concept of "linux gpio numbering"
> in the device tree data.  When using device tree, the gpio numbers
> usually get dynamically assigned.

Yes I know, but suppose you want to use this driver with a GPIO-driver that
does not have devaice-tree support yet? I need a way of binding the button to
a GPIO pin that does not have a device-tree definition. How should I do this
otherwise?
I am using this driver with a pca963x, as you might have suspected already,
and I just added OF device-tree support to it, so that will work, but
others...?
Before "fixing" pca963x, I used this property and it worked perfectly well, so
please explain what is not supposed to work. Please keep in mind that this
is meant as more of a backwards-compatibility feature. If you think that being
backwards compatible with non-OF GPIO drivers is a bad idea, I'll remove this.

> > +	- gpios: OF devcie-tree gpio specification, can be used
> > alternatively
> > +	  if 'reg' is not specified, to use device-tree GPIOs.
> > +	- label: Descriptive name of the key.
> > +	- linux,code: Keycode to emit.
> > +
> > +Optional subnode-properties:
> > +	- active-low: Boolean flag to specify active-low GPIO input. Not
> > used
> > +	  if device-tree gpios property is used.
> > +	- linux,input-type: Specify event type this button/key generates.
> > +	  Default if unspecified is <1> == EV_KEY.
> > +	- debounce-interval: Debouncing interval time in milliseconds.
> > +	  Default if unspecified is 5.
> > +	- wakeup: Boolean, button can wake-up the system.
> 
> "wakeup" is potentially too generic a property name (potential to
> conflict with a generic wakeup binding if one ever exists).  Just to
> be defencive, I'd suggest prefixing these custom gpio keys properties
> with something like "gpio-key,".

Ok, "gpio-key,wakeup" it will be then? But isn't this function something
potentially other IO-pins/keys/buttons/interrupts/etc... could have to be able
to wake up the system?

> > +
> > +Example nodes:
> > +
> > +	gpio_keys {
> > +			compatible = "gpio-keys";
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +			autorepeat;
> > +			button@20 {
> > +				label = "GPIO Key ESC";
> > +				linux,code = <1>;
> > +				reg = <0x20>;
> > +				key-active-low;
> > +				linux,input-type = <1>;
> > +			};
> > +			button@21 {
> > +				label = "GPIO Key UP";
> > +				linux,code = <103>;
> > +				gpios = <&gpio1 0 1>;
> > +				linux,input-type = <1>;
> > +			};
> > +			...
> > +
> > diff --git a/drivers/input/keyboard/gpio_keys.c
> > b/drivers/input/keyboard/gpio_keys.c index 987498e..78aeeaa 100644
> > --- a/drivers/input/keyboard/gpio_keys.c
> > +++ b/drivers/input/keyboard/gpio_keys.c
> > @@ -3,6 +3,9 @@
> >   *
> >   * Copyright 2005 Phil Blundell
> >   *
> > + * Added OF support:
> > + * Copyright 2010 David Jander <david@xxxxxxxxxxx>
> > + *
> 
> But it's 2011!  :-)

Ooops :-) You see... this patch is rather old already (in my tree). I actually
wrote it in 2010, but I am submitting it now. I guess it should be "2010, 2011"
then?

> >   * 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
> >   * published by the Free Software Foundation.
> > @@ -25,6 +28,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;
> > @@ -442,15 +447,124 @@ 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 struct gpio_keys_platform_data *
> > +gpio_keys_get_devtree_pdata(struct device *dev,
> > +			    struct gpio_keys_platform_data *altp)
> > +{
> > +	struct gpio_keys_platform_data *pdata = altp;
> 
> pdata is always the same as altp.

Ok, right.

> You don't need this on the stack, and the return value should be an error
> code instead of a pointer because the pointer is already passed in!

Hmm... I was (mis-)using the returned pointer as an error code. Will try to
come up with something more sensible.

> > +	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 NULL;
> > +
> > +	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 NULL;
> > +
> > +	buttons = kzalloc(pdata->nbuttons * (sizeof *buttons),
> > GFP_KERNEL);
> > +	if (!buttons)
> > +		return NULL;
> > +
> > +	pp = NULL;
> > +	i = 0;
> > +	while ((pp = of_get_next_child(node, pp))) {
> > +		const char *lbl;
> > +		enum of_gpio_flags flags;
> > +
> > +		reg = of_get_property(pp, "reg", &len);
> > +		if (!reg && !of_find_property(pp, "gpios", NULL)) {
> > +			pdata->nbuttons--;
> > +			dev_warn(dev, "Found button without reg and
> > without gpios\n");
> > +			continue;
> > +		}
> > +		if (reg) {
> > +			buttons[i].gpio = be32_to_cpup(reg);
> 
> As mentioned above, this won't work.  Linux gpio numbers cannot be
> encoded into the DT.

Why not? It worked fine for me before I "fixed" pca963x.
If you have a non-OF GPIO controller, that one will need a numeric range of
GPIO numbers. If that range is fixed, I can perfectly well give this number to
the driver here.... again, this is only used if the GPIO driver does not
have a device-tree node.

> > +			buttons[i].active_low = !!of_get_property(pp,
> > "key-active-low", NULL);
> > +		} else {
> > +			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);
> > +
> > +		lbl = of_get_property(pp, "label", &len);
> > +		buttons[i].desc = (char *)lbl;
> > +
> > +		reg = of_get_property(pp, "linux,input-type", &len);
> > +		if (reg)
> > +			buttons[i].type = be32_to_cpup(reg);
> > +		else
> > +			buttons[i].type = EV_KEY;
> how about:
> 		buttons[i].type = reg ? be32_to_cpup(reg) : EV_KEY;

Ok, if you prefer this notation.... just an "if...else" in another dress ;-)

> > +
> > +		buttons[i].wakeup = !!of_get_property(pp, "wakeup", NULL);
> > +
> > +		reg = of_get_property(pp, "debounce-interval", &len);
> > +		if (reg)
> > +			buttons[i].debounce_interval = be32_to_cpup(reg);
> > +		else
> > +			buttons[i].debounce_interval = 5;
> 
> Ditto here.

Ok, as you wish.

> > +		i++;
> > +	}
> > +
> > +	pdata->buttons = buttons;
> > +
> > +	return pdata;
> > +
> > +out_fail:
> > +	kfree(buttons);
> > +	return NULL;
> > +}
> > +#else
> > +static struct gpio_keys_platform_data *
> > +gpio_keys_get_devtree_pdata(struct device *dev,
> > +			    struct gpio_keys_platform_data *altp)
> > +{
> > +	return NULL;
> > +}
> > +#endif
> > +
> >  static int __devinit gpio_keys_probe(struct platform_device *pdev)
> >  {
> >  	struct gpio_keys_drvdata *ddata;
> >  	struct device *dev = &pdev->dev;
> >  	struct gpio_keys_platform_data *pdata = dev->platform_data;
> > +	struct gpio_keys_platform_data alt_pdata;
> >  	struct input_dev *input;
> >  	int i, error;
> >  	int wakeup = 0;
> >  
> > +	if (!pdata) {
> > +		pdata = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
> > +		if (!pdata)
> > +			return -ENODEV;
> > +	}
> 
> then this would become:
> 
> 	if (!pdata) {
> 		rc = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
> 		if (rc)
> 			return rc;
> 		pdata = &alt_pdata;
> 	}

Yes, of course. I just need to "invent" which error codes to use for the
different failure cases.... no problem.

> > +
> >  	ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
> >  			pdata->nbuttons * sizeof(struct gpio_button_data),
> >  			GFP_KERNEL);
> > @@ -548,7 +662,6 @@ static int __devinit gpio_keys_probe(struct
> > platform_device *pdev) static int __devexit gpio_keys_remove(struct
> > platform_device *pdev) {
> >  	struct device *dev = &pdev->dev;
> > -	struct gpio_keys_platform_data *pdata = dev->platform_data;
> >  	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
> >  	struct input_dev *input = ddata->input;
> >  	int i;
> > @@ -557,30 +670,42 @@ static int __devexit gpio_keys_remove(struct
> > platform_device *pdev) 
> >  	device_init_wakeup(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);
> >  	}
> >  
> > +	/*
> > +	 * 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 (!dev->platform_data)
> > +		kfree(ddata->data[0].button);
> > +
> >  	input_unregister_device(input);
> >  
> >  	return 0;
> >  }
> >  
> > +static struct of_device_id gpio_keys_of_match[] = {
> > +	{ .compatible = "gpio-keys", },
> > +	{},
> > +};
> >  
> >  #ifdef CONFIG_PM
> >  static int gpio_keys_suspend(struct device *dev)
> >  {
> > -	struct gpio_keys_platform_data *pdata = dev->platform_data;
> > +	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
> >  	int i;
> >  
> >  	if (device_may_wakeup(dev)) {
> > -		for (i = 0; i < pdata->nbuttons; i++) {
> > -			struct gpio_keys_button *button =
> > &pdata->buttons[i];
> > +		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);
> > @@ -594,12 +719,11 @@ 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);
> > -	struct gpio_keys_platform_data *pdata = dev->platform_data;
> >  	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];
> > +		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);
> > @@ -618,6 +742,8 @@ static const struct dev_pm_ops gpio_keys_pm_ops = {
> >  };
> >  #endif
> >  
> > +MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
> > +
> 
> Modules device table needs to be #ifdef CONFIG_OF protected.
> Otherwise the driver advertises behaviour that it cannot provide.

Ah, ok. Will add an #ifdef. Thanks for pointing out.

Best regards,

-- 
David Jander
Protonic Holland.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux