diff mbox

[powerpc] GPIO: Adding new Xilinx driver

Message ID 20081024195908.7D1A1142806C@mail74-wa4.bigfish.com (mailing list archive)
State Accepted, archived
Delegated to: Grant Likely
Headers show

Commit Message

John Linn Oct. 24, 2008, 7:59 p.m. UTC
This driver supports the Xilinx XPS GPIO IP core which has the typical
GPIO features.

Signed-off-by: Kiran Sutariya <kirans@xilinx.com>
Signed-off-by: John Linn <john.linn@xilinx.com>
---
 drivers/gpio/Kconfig       |    8 ++
 drivers/gpio/Makefile      |    1 +
 drivers/gpio/xilinx_gpio.c |  240 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 249 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpio/xilinx_gpio.c

Comments

Grant Likely Oct. 24, 2008, 8:03 p.m. UTC | #1
On Fri, Oct 24, 2008 at 1:59 PM, John Linn <john.linn@xilinx.com> wrote:
> This driver supports the Xilinx XPS GPIO IP core which has the typical
> GPIO features.
>
> Signed-off-by: Kiran Sutariya <kirans@xilinx.com>
> Signed-off-by: John Linn <john.linn@xilinx.com>

Acked-by: Grant Likely <grant.likely@secretlab.ca>

> ---
>  drivers/gpio/Kconfig       |    8 ++
>  drivers/gpio/Makefile      |    1 +
>  drivers/gpio/xilinx_gpio.c |  240 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 249 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/gpio/xilinx_gpio.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 7f2ee27..f6b0da8 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -65,6 +65,14 @@ config GPIO_SYSFS
>
>  # put expanders in the right section, in alphabetical order
>
> +comment "Memory mapped GPIO expanders:"
> +
> +config GPIO_XILINX
> +       bool "Xilinx GPIO support"
> +       depends on OF
> +       help
> +         Say yes here to support the Xilinx FPGA GPIO device
> +
>  comment "I2C GPIO expanders:"
>
>  config GPIO_MAX732X
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 6aafdeb..49ac64e 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -10,4 +10,5 @@ obj-$(CONFIG_GPIO_MCP23S08)   += mcp23s08.o
>  obj-$(CONFIG_GPIO_PCA953X)     += pca953x.o
>  obj-$(CONFIG_GPIO_PCF857X)     += pcf857x.o
>  obj-$(CONFIG_GPIO_TWL4030)     += twl4030-gpio.o
> +obj-$(CONFIG_GPIO_XILINX)      += xilinx_gpio.o
>  obj-$(CONFIG_GPIO_BT8XX)       += bt8xxgpio.o
> diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c
> new file mode 100644
> index 0000000..224d0d5
> --- /dev/null
> +++ b/drivers/gpio/xilinx_gpio.c
> @@ -0,0 +1,240 @@
> +/*
> + * Xilinx gpio driver
> + *
> + * Copyright 2008 Xilinx, Inc.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_gpio.h>
> +#include <linux/io.h>
> +#include <linux/gpio.h>
> +
> +/* Register Offset Definitions */
> +#define XGPIO_DATA_OFFSET   (0x0)      /* Data register  */
> +#define XGPIO_TRI_OFFSET    (0x4)      /* I/O direction register  */
> +
> +struct xgpio_instance {
> +       struct of_mm_gpio_chip mmchip;
> +       u32 gpio_state;         /* GPIO state shadow register */
> +       u32 gpio_dir;           /* GPIO direction shadow register */
> +       spinlock_t gpio_lock;   /* Lock used for synchronization */
> +};
> +
> +/**
> + * xgpio_get - Read the specified signal of the GPIO device.
> + * @gc:     Pointer to gpio_chip device structure.
> + * @gpio:   GPIO signal number.
> + *
> + * This function reads the specified signal of the GPIO device. It returns 0 if
> + * the signal clear, 1 if signal is set or negative value on error.
> + */
> +static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)
> +{
> +       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> +
> +       return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1;
> +}
> +
> +/**
> + * xgpio_set - Write the specified signal of the GPIO device.
> + * @gc:     Pointer to gpio_chip device structure.
> + * @gpio:   GPIO signal number.
> + * @val:    Value to be written to specified signal.
> + *
> + * This function writes the specified value in to the specified signal of the
> + * GPIO device.
> + */
> +static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
> +{
> +       unsigned long flags;
> +       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> +       struct xgpio_instance *chip =
> +           container_of(mm_gc, struct xgpio_instance, mmchip);
> +
> +       spin_lock_irqsave(&chip->gpio_lock, flags);
> +
> +       /* Write to GPIO signal and set its direction to output */
> +       if (val)
> +               chip->gpio_state |= 1 << gpio;
> +       else
> +               chip->gpio_state &= ~(1 << gpio);
> +       out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
> +
> +       spin_unlock_irqrestore(&chip->gpio_lock, flags);
> +}
> +
> +/**
> + * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
> + * @gc:     Pointer to gpio_chip device structure.
> + * @gpio:   GPIO signal number.
> + *
> + * This function sets the direction of specified GPIO signal as input.
> + * It returns 0 if direction of GPIO signals is set as input otherwise it
> + * returns negative error value.
> + */
> +static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
> +{
> +       unsigned long flags;
> +       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> +       struct xgpio_instance *chip =
> +           container_of(mm_gc, struct xgpio_instance, mmchip);
> +
> +       spin_lock_irqsave(&chip->gpio_lock, flags);
> +
> +       /* Set the GPIO bit in shadow register and set direction as input */
> +       chip->gpio_dir |= (1 << gpio);
> +       out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir);
> +
> +       spin_unlock_irqrestore(&chip->gpio_lock, flags);
> +
> +       return 0;
> +}
> +
> +/**
> + * xgpio_dir_out - Set the direction of the specified GPIO signal as output.
> + * @gc:     Pointer to gpio_chip device structure.
> + * @gpio:   GPIO signal number.
> + * @val:    Value to be written to specified signal.
> + *
> + * This function sets the direction of specified GPIO signal as output. If all
> + * GPIO signals of GPIO chip is configured as input then it returns
> + * error otherwise it returns 0.
> + */
> +static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
> +{
> +       unsigned long flags;
> +       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> +       struct xgpio_instance *chip =
> +           container_of(mm_gc, struct xgpio_instance, mmchip);
> +
> +       spin_lock_irqsave(&chip->gpio_lock, flags);
> +
> +       /* Write state of GPIO signal */
> +       if (val)
> +               chip->gpio_state |= 1 << gpio;
> +       else
> +               chip->gpio_state &= ~(1 << gpio);
> +       out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
> +
> +       /* Clear the GPIO bit in shadow register and set direction as output */
> +       chip->gpio_dir &= (~(1 << gpio));
> +       out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir);
> +
> +       spin_unlock_irqrestore(&chip->gpio_lock, flags);
> +
> +       return 0;
> +}
> +
> +/**
> + * xgpio_of_probe - Probe method for the GPIO device.
> + * @of_dev: pointer to OF device structure.
> + * @match:  pointer to the structure used for matching a device.
> + *
> + * This function probes the GPIO device in the device tree. It initializes the
> + * driver data structure. It returns 0, if the driver is bound to the GPIO
> + * device, or a negative value if there is an error.
> + */
> +static int __devinit xgpio_of_probe(struct of_device *ofdev,
> +                                   const struct of_device_id *match)
> +{
> +       struct xgpio_instance *chip;
> +       struct of_gpio_chip *ofchip;
> +       int status = 0;
> +       const u32 *tree_info;
> +
> +       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> +       if (!chip)
> +               return -ENOMEM;
> +       ofchip = &chip->mmchip.of_gc;
> +
> +       /* Update GPIO state shadow register with default value */
> +       tree_info = of_get_property(ofdev->node, "xlnx,dout-default", NULL);
> +       if (tree_info)
> +               chip->gpio_state = *tree_info;
> +
> +       /* Update GPIO direction shadow register with default value */
> +       chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */
> +       tree_info = of_get_property(ofdev->node, "xlnx,tri-default", NULL);
> +       if (tree_info)
> +               chip->gpio_dir = *tree_info;
> +
> +       /* Check device node and parent device node for device width */
> +       ofchip->gc.ngpio = 32; /* By default assume full GPIO controller */
> +       tree_info = of_get_property(ofdev->node, "xlnx,gpio-width", NULL);
> +       if (!tree_info)
> +               tree_info = of_get_property(ofdev->node->parent,
> +                                           "xlnx,gpio-width", NULL);
> +       if (tree_info)
> +               ofchip->gc.ngpio = *tree_info;
> +
> +       spin_lock_init(&chip->gpio_lock);
> +
> +       ofchip->gpio_cells = 2;
> +       ofchip->gc.direction_input = xgpio_dir_in;
> +       ofchip->gc.direction_output = xgpio_dir_out;
> +       ofchip->gc.get = xgpio_get;
> +       ofchip->gc.set = xgpio_set;
> +
> +       /* Call the OF gpio helper to setup and register the GPIO device */
> +       status = of_mm_gpiochip_add(ofdev->node, &chip->mmchip);
> +       if (status) {
> +               kfree(chip);
> +               dev_err(&ofdev->dev, "Error in probe function error value %d\n",
> +                       status);
> +               return status;
> +       }
> +
> +       /* Finally, write the initial state to the device */
> +       out_be32(chip->mmchip.regs + XGPIO_DATA_OFFSET, chip->gpio_state);
> +       out_be32(chip->mmchip.regs + XGPIO_TRI_OFFSET, chip->gpio_dir);
> +
> +       dev_info(&ofdev->dev, "registered Xilinx GPIO controller\n");
> +       return 0;
> +}
> +
> +/**
> + * xgpio_of_remove - Remove method for the GPIO device.
> + * @of_dev: pointer to OF device structure.
> + *
> + * This function returns a negative error as we cannot unregister GPIO chips.
> + */
> +static int __devexit xgpio_of_remove(struct of_device *ofdev)
> +{
> +       return -EBUSY;
> +}
> +
> +static struct of_device_id xgpio_of_match[] __devinitdata = {
> +       { .compatible = "xlnx,xps-gpio-1.00.a", },
> +       { /* end of list */ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, xgpio_of_match);
> +
> +static struct of_platform_driver xgpio_of_driver = {
> +       .name = "xilinx_gpio",
> +       .match_table = xgpio_of_match,
> +       .probe = xgpio_of_probe,
> +       .remove = __devexit_p(xgpio_of_remove),
> +};
> +
> +static int __init xgpio_init(void)
> +{
> +       return of_register_platform_driver(&xgpio_of_driver);
> +}
> +
> +/* Make sure we get initialized before anyone else tries to use us */
> +subsys_initcall(xgpio_init);
> +/* No exit call at the moment as we cannot unregister of GPIO chips */
> +
> +MODULE_AUTHOR("Xilinx, Inc.");
> +MODULE_DESCRIPTION("Xilinx GPIO driver");
> +MODULE_LICENSE("GPL");
> --
> 1.5.2.1
>
>
>
> This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
>
>
>
Anton Vorontsov Oct. 24, 2008, 9:41 p.m. UTC | #2
On Fri, Oct 24, 2008 at 12:59:00PM -0700, John Linn wrote:
> This driver supports the Xilinx XPS GPIO IP core which has the typical
> GPIO features.
> 
> Signed-off-by: Kiran Sutariya <kirans@xilinx.com>
> Signed-off-by: John Linn <john.linn@xilinx.com>

Looks good. Just few comments below.

> ---
>  drivers/gpio/Kconfig       |    8 ++
>  drivers/gpio/Makefile      |    1 +
>  drivers/gpio/xilinx_gpio.c |  240 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 249 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/gpio/xilinx_gpio.c
> 
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 7f2ee27..f6b0da8 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -65,6 +65,14 @@ config GPIO_SYSFS
>  
>  # put expanders in the right section, in alphabetical order
>  
> +comment "Memory mapped GPIO expanders:"
> +
> +config GPIO_XILINX
> +	bool "Xilinx GPIO support"
> +	depends on OF

I persume that the driver wasn't build-tested on SPARC, so I'd recommend
to change the depends to PPC_OF. Plus, the driver should also select
GENERIC_GPIO and ARCH_REQUIRE_GPIOLIB. (Later we'll switch to
ARCH_WANT_OPTIONAL_GPIOLIB for whole PPC.)

> +	help
> +	  Say yes here to support the Xilinx FPGA GPIO device

If nothing has changed, we should place the arch-specific GPIO
drivers into the arch/. So, Kconfig entry should go into the
arch/powerpc/platforms/Kconfig.

and the driver itself into arch/powerpc/sysdev/.

> @@ -0,0 +1,240 @@
> +/*
> + * Xilinx gpio driver
> + *
> + * Copyright 2008 Xilinx, Inc.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */

#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gpio.h>

> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_gpio.h>
> +#include <linux/io.h>
> +#include <linux/gpio.h>
> +

[...]
> +	spin_lock_init(&chip->gpio_lock);
> +
> +	ofchip->gpio_cells = 2;
> +	ofchip->gc.direction_input = xgpio_dir_in;
> +	ofchip->gc.direction_output = xgpio_dir_out;
> +	ofchip->gc.get = xgpio_get;
> +	ofchip->gc.set = xgpio_set;
> +
> +	/* Call the OF gpio helper to setup and register the GPIO device */
> +	status = of_mm_gpiochip_add(ofdev->node, &chip->mmchip);
> +	if (status) {
> +		kfree(chip);
> +		dev_err(&ofdev->dev, "Error in probe function error value %d\n",
> +			status);
> +		return status;
> +	}

At this point the GPIO controller is registered, so somebody
might already request and work with the GPIOs...

> +	/* Finally, write the initial state to the device */
> +	out_be32(chip->mmchip.regs + XGPIO_DATA_OFFSET, chip->gpio_state);
> +	out_be32(chip->mmchip.regs + XGPIO_TRI_OFFSET, chip->gpio_dir);

But initial values were set up just now, after the registration.

There is the `save_regs' callback in the of_mm_gpio_chip structure,
it is used exactly to avoid this situation.

> +	dev_info(&ofdev->dev, "registered Xilinx GPIO controller\n");
> +	return 0;
> +}
> +
> +/**
> + * xgpio_of_remove - Remove method for the GPIO device.
> + * @of_dev: pointer to OF device structure.
> + *
> + * This function returns a negative error as we cannot unregister GPIO chips.
> + */
> +static int __devexit xgpio_of_remove(struct of_device *ofdev)
> +{
> +	return -EBUSY;
> +}
> +
> +static struct of_device_id xgpio_of_match[] __devinitdata = {
> +	{ .compatible = "xlnx,xps-gpio-1.00.a", },
> +	{ /* end of list */ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, xgpio_of_match);
> +
> +static struct of_platform_driver xgpio_of_driver = {

There is no much point in doing the of_platform_driver for the
SOC GPIOs. More than that, of_platform bus is probed at the
device_initcall time, so there is also no point in the subsys_initcall
for this driver.

I'd recommend to do the registration as in
arch/powerpc/sysdev/qe_lib/gpio.c.

> +	.name = "xilinx_gpio",
> +	.match_table = xgpio_of_match,
> +	.probe = xgpio_of_probe,
> +	.remove = __devexit_p(xgpio_of_remove),
> +};
> +
> +static int __init xgpio_init(void)
> +{
> +	return of_register_platform_driver(&xgpio_of_driver);
> +}
> +
> +/* Make sure we get initialized before anyone else tries to use us */
> +subsys_initcall(xgpio_init);
> +/* No exit call at the moment as we cannot unregister of GPIO chips */
Grant Likely Oct. 24, 2008, 9:53 p.m. UTC | #3
On Fri, Oct 24, 2008 at 3:41 PM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> On Fri, Oct 24, 2008 at 12:59:00PM -0700, John Linn wrote:
>> This driver supports the Xilinx XPS GPIO IP core which has the typical
>> GPIO features.
>>
>> Signed-off-by: Kiran Sutariya <kirans@xilinx.com>
>> Signed-off-by: John Linn <john.linn@xilinx.com>
>
> Looks good. Just few comments below.
>
<snip>
>
> If nothing has changed, we should place the arch-specific GPIO
> drivers into the arch/. So, Kconfig entry should go into the
> arch/powerpc/platforms/Kconfig.
>
> and the driver itself into arch/powerpc/sysdev/.

This isn't just a powerpc driver.  It is also used by microblaze so
I'd like to keep it in the common directory (Microblaze is in the
process of being reviewed for merging).

Thanks for the comments.
g.
Grant Likely Oct. 25, 2008, 6:07 a.m. UTC | #4
On Fri, Oct 24, 2008 at 3:41 PM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> On Fri, Oct 24, 2008 at 12:59:00PM -0700, John Linn wrote:
>> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
>> index 7f2ee27..f6b0da8 100644
>> --- a/drivers/gpio/Kconfig
>> +++ b/drivers/gpio/Kconfig
>> @@ -65,6 +65,14 @@ config GPIO_SYSFS
>>
>>  # put expanders in the right section, in alphabetical order
>>
>> +comment "Memory mapped GPIO expanders:"
>> +
>> +config GPIO_XILINX
>> +     bool "Xilinx GPIO support"
>> +     depends on OF
>
> I persume that the driver wasn't build-tested on SPARC, so I'd recommend
> to change the depends to PPC_OF. Plus, the driver should also select
> GENERIC_GPIO and ARCH_REQUIRE_GPIOLIB. (Later we'll switch to
> ARCH_WANT_OPTIONAL_GPIOLIB for whole PPC.)

ARCH_WANT_OPTIONAL_GPIOLIB seems to already be set for all of powerpc
(arch/powerpc/Kconfig, line 113).

But, since this is a cross-arch driver living in drivers/gpio the
dependencies work the other way around.  This driver cannot be
selected unless GPIOLIB is turned on.

g.
Anton Vorontsov Oct. 25, 2008, 10:10 a.m. UTC | #5
On Sat, Oct 25, 2008 at 12:07:06AM -0600, Grant Likely wrote:
> On Fri, Oct 24, 2008 at 3:41 PM, Anton Vorontsov
> <avorontsov@ru.mvista.com> wrote:
> > On Fri, Oct 24, 2008 at 12:59:00PM -0700, John Linn wrote:
> >> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> >> index 7f2ee27..f6b0da8 100644
> >> --- a/drivers/gpio/Kconfig
> >> +++ b/drivers/gpio/Kconfig
> >> @@ -65,6 +65,14 @@ config GPIO_SYSFS
> >>
> >>  # put expanders in the right section, in alphabetical order
> >>
> >> +comment "Memory mapped GPIO expanders:"
> >> +
> >> +config GPIO_XILINX
> >> +     bool "Xilinx GPIO support"
> >> +     depends on OF
> >
> > I persume that the driver wasn't build-tested on SPARC, so I'd recommend
> > to change the depends to PPC_OF. Plus, the driver should also select
> > GENERIC_GPIO and ARCH_REQUIRE_GPIOLIB. (Later we'll switch to
> > ARCH_WANT_OPTIONAL_GPIOLIB for whole PPC.)
> 
> ARCH_WANT_OPTIONAL_GPIOLIB seems to already be set for all of powerpc
> (arch/powerpc/Kconfig, line 113).

Ah yes, I see it now. Thanks.
diff mbox

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7f2ee27..f6b0da8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -65,6 +65,14 @@  config GPIO_SYSFS
 
 # put expanders in the right section, in alphabetical order
 
+comment "Memory mapped GPIO expanders:"
+
+config GPIO_XILINX
+	bool "Xilinx GPIO support"
+	depends on OF
+	help
+	  Say yes here to support the Xilinx FPGA GPIO device
+
 comment "I2C GPIO expanders:"
 
 config GPIO_MAX732X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 6aafdeb..49ac64e 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -10,4 +10,5 @@  obj-$(CONFIG_GPIO_MCP23S08)	+= mcp23s08.o
 obj-$(CONFIG_GPIO_PCA953X)	+= pca953x.o
 obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o
 obj-$(CONFIG_GPIO_TWL4030)	+= twl4030-gpio.o
+obj-$(CONFIG_GPIO_XILINX)	+= xilinx_gpio.o
 obj-$(CONFIG_GPIO_BT8XX)	+= bt8xxgpio.o
diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c
new file mode 100644
index 0000000..224d0d5
--- /dev/null
+++ b/drivers/gpio/xilinx_gpio.c
@@ -0,0 +1,240 @@ 
+/*
+ * Xilinx gpio driver
+ *
+ * Copyright 2008 Xilinx, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+/* Register Offset Definitions */
+#define XGPIO_DATA_OFFSET   (0x0)	/* Data register  */
+#define XGPIO_TRI_OFFSET    (0x4)	/* I/O direction register  */
+
+struct xgpio_instance {
+	struct of_mm_gpio_chip mmchip;
+	u32 gpio_state;		/* GPIO state shadow register */
+	u32 gpio_dir;		/* GPIO direction shadow register */
+	spinlock_t gpio_lock;	/* Lock used for synchronization */
+};
+
+/**
+ * xgpio_get - Read the specified signal of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ *
+ * This function reads the specified signal of the GPIO device. It returns 0 if
+ * the signal clear, 1 if signal is set or negative value on error.
+ */
+static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+
+	return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1;
+}
+
+/**
+ * xgpio_set - Write the specified signal of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ * @val:    Value to be written to specified signal.
+ *
+ * This function writes the specified value in to the specified signal of the
+ * GPIO device.
+ */
+static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	unsigned long flags;
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct xgpio_instance *chip =
+	    container_of(mm_gc, struct xgpio_instance, mmchip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	/* Write to GPIO signal and set its direction to output */
+	if (val)
+		chip->gpio_state |= 1 << gpio;
+	else
+		chip->gpio_state &= ~(1 << gpio);
+	out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
+ * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ *
+ * This function sets the direction of specified GPIO signal as input.
+ * It returns 0 if direction of GPIO signals is set as input otherwise it
+ * returns negative error value.
+ */
+static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+	unsigned long flags;
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct xgpio_instance *chip =
+	    container_of(mm_gc, struct xgpio_instance, mmchip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	/* Set the GPIO bit in shadow register and set direction as input */
+	chip->gpio_dir |= (1 << gpio);
+	out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+	return 0;
+}
+
+/**
+ * xgpio_dir_out - Set the direction of the specified GPIO signal as output.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ * @val:    Value to be written to specified signal.
+ *
+ * This function sets the direction of specified GPIO signal as output. If all
+ * GPIO signals of GPIO chip is configured as input then it returns
+ * error otherwise it returns 0.
+ */
+static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	unsigned long flags;
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct xgpio_instance *chip =
+	    container_of(mm_gc, struct xgpio_instance, mmchip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	/* Write state of GPIO signal */
+	if (val)
+		chip->gpio_state |= 1 << gpio;
+	else
+		chip->gpio_state &= ~(1 << gpio);
+	out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+
+	/* Clear the GPIO bit in shadow register and set direction as output */
+	chip->gpio_dir &= (~(1 << gpio));
+	out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+	return 0;
+}
+
+/**
+ * xgpio_of_probe - Probe method for the GPIO device.
+ * @of_dev: pointer to OF device structure.
+ * @match:  pointer to the structure used for matching a device.
+ *
+ * This function probes the GPIO device in the device tree. It initializes the
+ * driver data structure. It returns 0, if the driver is bound to the GPIO
+ * device, or a negative value if there is an error.
+ */
+static int __devinit xgpio_of_probe(struct of_device *ofdev,
+				    const struct of_device_id *match)
+{
+	struct xgpio_instance *chip;
+	struct of_gpio_chip *ofchip;
+	int status = 0;
+	const u32 *tree_info;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+	ofchip = &chip->mmchip.of_gc;
+
+	/* Update GPIO state shadow register with default value */
+	tree_info = of_get_property(ofdev->node, "xlnx,dout-default", NULL);
+	if (tree_info)
+		chip->gpio_state = *tree_info;
+
+	/* Update GPIO direction shadow register with default value */
+	chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */
+	tree_info = of_get_property(ofdev->node, "xlnx,tri-default", NULL);
+	if (tree_info)
+		chip->gpio_dir = *tree_info;
+
+	/* Check device node and parent device node for device width */
+	ofchip->gc.ngpio = 32; /* By default assume full GPIO controller */
+	tree_info = of_get_property(ofdev->node, "xlnx,gpio-width", NULL);
+	if (!tree_info)
+		tree_info = of_get_property(ofdev->node->parent,
+					    "xlnx,gpio-width", NULL);
+	if (tree_info)
+		ofchip->gc.ngpio = *tree_info;
+
+	spin_lock_init(&chip->gpio_lock);
+
+	ofchip->gpio_cells = 2;
+	ofchip->gc.direction_input = xgpio_dir_in;
+	ofchip->gc.direction_output = xgpio_dir_out;
+	ofchip->gc.get = xgpio_get;
+	ofchip->gc.set = xgpio_set;
+
+	/* Call the OF gpio helper to setup and register the GPIO device */
+	status = of_mm_gpiochip_add(ofdev->node, &chip->mmchip);
+	if (status) {
+		kfree(chip);
+		dev_err(&ofdev->dev, "Error in probe function error value %d\n",
+			status);
+		return status;
+	}
+
+	/* Finally, write the initial state to the device */
+	out_be32(chip->mmchip.regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+	out_be32(chip->mmchip.regs + XGPIO_TRI_OFFSET, chip->gpio_dir);
+
+	dev_info(&ofdev->dev, "registered Xilinx GPIO controller\n");
+	return 0;
+}
+
+/**
+ * xgpio_of_remove - Remove method for the GPIO device.
+ * @of_dev: pointer to OF device structure.
+ *
+ * This function returns a negative error as we cannot unregister GPIO chips.
+ */
+static int __devexit xgpio_of_remove(struct of_device *ofdev)
+{
+	return -EBUSY;
+}
+
+static struct of_device_id xgpio_of_match[] __devinitdata = {
+	{ .compatible = "xlnx,xps-gpio-1.00.a", },
+	{ /* end of list */ },
+};
+
+MODULE_DEVICE_TABLE(of, xgpio_of_match);
+
+static struct of_platform_driver xgpio_of_driver = {
+	.name = "xilinx_gpio",
+	.match_table = xgpio_of_match,
+	.probe = xgpio_of_probe,
+	.remove = __devexit_p(xgpio_of_remove),
+};
+
+static int __init xgpio_init(void)
+{
+	return of_register_platform_driver(&xgpio_of_driver);
+}
+
+/* Make sure we get initialized before anyone else tries to use us */
+subsys_initcall(xgpio_init);
+/* No exit call at the moment as we cannot unregister of GPIO chips */
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx GPIO driver");
+MODULE_LICENSE("GPL");