diff mbox

[05/12] gpio: xilinx: merge Xilinx gpio support into LTSI 3.10.y

Message ID 1387865711-23124-6-git-send-email-daniel.sangorrin@toshiba.co.jp (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Sangorrin Dec. 24, 2013, 6:15 a.m. UTC
From: Soren Brinkmann <soren.brinkmann@xilinx.com>

This commits merges support for the GPIO bus from the Xilinx
master branch (commit efc27505715e64526653f35274717c0fc56491e3 in
master branch).

Signed-off-by: Daniel Sangorrin <daniel.sangorrin@toshiba.co.jp>
Signed-off-by: Yoshitake Kobayashi <yoshitake.kobayashi@toshiba.co.jp>
---
 drivers/gpio/Kconfig         |  12 +-
 drivers/gpio/Makefile        |   1 +
 drivers/gpio/gpio-xilinx.c   | 321 +++++++++++++++++--
 drivers/gpio/gpio-xilinxps.c | 722 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1026 insertions(+), 30 deletions(-)
 create mode 100644 drivers/gpio/gpio-xilinxps.c
diff mbox

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 573c449..97021aa 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -234,9 +234,17 @@  config GPIO_TS5500
 
 config GPIO_XILINX
 	bool "Xilinx GPIO support"
-	depends on PPC_OF || MICROBLAZE
+	depends on PPC_OF || MICROBLAZE || ARCH_ZYNQ
+	select GENERIC_IRQ_CHIP
+	help
+	  Say yes here to support the Xilinx AXI/XPS GPIO device
+
+config GPIO_XILINX_PS
+	tristate "Xilinx GPIO PS"
+	depends on ARCH_ZYNQ
+	select GENERIC_IRQ_CHIP
 	help
-	  Say yes here to support the Xilinx FPGA GPIO device
+	  Say yes here to support Xilinx GPIO PS controller
 
 config GPIO_VR41XX
 	tristate "NEC VR4100 series General-purpose I/O Uint support"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0cb2d65..2636985 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -87,3 +87,4 @@  obj-$(CONFIG_GPIO_WM831X)	+= gpio-wm831x.o
 obj-$(CONFIG_GPIO_WM8350)	+= gpio-wm8350.o
 obj-$(CONFIG_GPIO_WM8994)	+= gpio-wm8994.o
 obj-$(CONFIG_GPIO_XILINX)	+= gpio-xilinx.o
+obj-$(CONFIG_GPIO_XILINX_PS)	+= gpio-xilinxps.o
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index 792a05a..566a306 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -17,15 +17,25 @@ 
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
 #include <linux/gpio.h>
 #include <linux/slab.h>
 
 /* Register Offset Definitions */
-#define XGPIO_DATA_OFFSET   (0x0)	/* Data register  */
-#define XGPIO_TRI_OFFSET    (0x4)	/* I/O direction register  */
+#define XGPIO_DATA_OFFSET	0x0 /* Data register */
+#define XGPIO_TRI_OFFSET	0x4 /* I/O direction register */
+#define XGPIO_GIER_OFFSET	0x11c /* Global Interrupt Enable */
+#define XGPIO_GIER_IE		BIT(31)
+
+#define XGPIO_IPISR_OFFSET	0x120 /* IP Interrupt Status */
+#define XGPIO_IPIER_OFFSET	0x128 /* IP Interrupt Enable */
 
 #define XGPIO_CHANNEL_OFFSET	0x8
 
@@ -40,18 +50,24 @@ 
 
 /**
  * struct xgpio_instance - Stores information about GPIO device
- * struct of_mm_gpio_chip mmchip: OF GPIO chip for memory mapped banks
- * gpio_state: GPIO state shadow register
- * gpio_dir: GPIO direction shadow register
- * offset: GPIO channel offset
- * gpio_lock: Lock used for synchronization
+ * @mmchip: OF GPIO chip for memory mapped banks
+ * @gpio_state: GPIO state shadow register
+ * @gpio_dir: GPIO direction shadow register
+ * @offset: GPIO channel offset
+ * @irq_base: GPIO channel irq base address
+ * @irq_enable: GPIO irq enable/disable bitfield
+ * @gpio_lock: Lock used for synchronization
+ * @irq_domain: irq_domain of the controller
  */
 struct xgpio_instance {
 	struct of_mm_gpio_chip mmchip;
 	u32 gpio_state;
 	u32 gpio_dir;
 	u32 offset;
+	int irq_base;
+	u32 irq_enable;
 	spinlock_t gpio_lock;
+	struct irq_domain *irq_domain;
 };
 
 /**
@@ -59,8 +75,11 @@  struct xgpio_instance {
  * @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.
+ * This function reads the specified signal of the GPIO device.
+ *
+ * Return:
+ * 0 if direction of GPIO signals is set as input otherwise it
+ * returns negative error value.
  */
 static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)
 {
@@ -110,8 +129,10 @@  static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
  * @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.
+ *
+ * Return:
+ * 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)
 {
@@ -138,8 +159,10 @@  static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
  * @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
+ * This function sets the direction of specified GPIO signal as output.
+ *
+ * Return:
+ * 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)
@@ -171,7 +194,7 @@  static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
 
 /**
  * xgpio_save_regs - Set initial values of GPIO pins
- * @mm_gc: pointer to memory mapped GPIO chip structure
+ * @mm_gc: Pointer to memory mapped GPIO chip structure
  */
 static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
 {
@@ -185,20 +208,245 @@  static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
 }
 
 /**
+ * xgpio_xlate - Set initial values of GPIO pins
+ * @gc: Pointer to gpio_chip device structure.
+ * @gpiospec:  gpio specifier as found in the device tree
+ * @flags: A flags pointer based on binding
+ *
+ * Return:
+ * irq number otherwise -EINVAL
+ */
+static int xgpio_xlate(struct gpio_chip *gc,
+		       const struct of_phandle_args *gpiospec, u32 *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);
+
+	if (gpiospec->args[1] == chip->offset)
+		return gpiospec->args[0];
+
+	return -EINVAL;
+}
+
+/**
+ * xgpio_irq_mask - Write the specified signal of the GPIO device.
+ * @irq_data: per irq and chip data passed down to chip functions
+ */
+static void xgpio_irq_mask(struct irq_data *irq_data)
+{
+	unsigned long flags;
+	struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
+	struct of_mm_gpio_chip *mm_gc = &chip->mmchip;
+	u32 offset = irq_data->irq - chip->irq_base;
+	u32 temp;
+
+	pr_debug("%s: Disable %d irq, irq_enable_mask 0x%x\n",
+		__func__, offset, chip->irq_enable);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->irq_enable &= ~BIT(offset);
+
+	if (!chip->irq_enable) {
+		/* Enable per channel interrupt */
+		temp = xgpio_readreg(mm_gc->regs + XGPIO_IPIER_OFFSET);
+		temp &= chip->offset / XGPIO_CHANNEL_OFFSET + 1;
+		xgpio_writereg(mm_gc->regs + XGPIO_IPIER_OFFSET, temp);
+
+		/* Disable global interrupt if channel interrupts are unused */
+		temp = xgpio_readreg(mm_gc->regs + XGPIO_IPIER_OFFSET);
+		if (!temp)
+			xgpio_writereg(mm_gc->regs + XGPIO_GIER_OFFSET,
+				       ~XGPIO_GIER_IE);
+
+	}
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
+ * xgpio_irq_unmask - Write the specified signal of the GPIO device.
+ * @irq_data: per irq and chip data passed down to chip functions
+ */
+static void xgpio_irq_unmask(struct irq_data *irq_data)
+{
+	unsigned long flags;
+	struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
+	struct of_mm_gpio_chip *mm_gc = &chip->mmchip;
+	u32 offset = irq_data->irq - chip->irq_base;
+	u32 temp;
+
+	pr_debug("%s: Enable %d irq, irq_enable_mask 0x%x\n",
+		__func__, offset, chip->irq_enable);
+
+	/* Setup pin as input */
+	xgpio_dir_in(&mm_gc->gc, offset);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->irq_enable |= BIT(offset);
+
+	if (chip->irq_enable) {
+
+		/* Enable per channel interrupt */
+		temp = xgpio_readreg(mm_gc->regs + XGPIO_IPIER_OFFSET);
+		temp |= chip->offset / XGPIO_CHANNEL_OFFSET + 1;
+		xgpio_writereg(mm_gc->regs + XGPIO_IPIER_OFFSET, temp);
+
+		/* Enable global interrupts */
+		xgpio_writereg(mm_gc->regs + XGPIO_GIER_OFFSET, XGPIO_GIER_IE);
+	}
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
+ * xgpio_set_irq_type - Write the specified signal of the GPIO device.
+ * @irq_data: Per irq and chip data passed down to chip functions
+ * @type: Interrupt type that is to be set for the gpio pin
+ *
+ * Return:
+ * 0 if interrupt type is supported otherwise otherwise -EINVAL
+ */
+static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
+{
+	/* Only rising edge case is supported now */
+	if (type == IRQ_TYPE_EDGE_RISING)
+		return 0;
+
+	return -EINVAL;
+}
+
+/* irq chip descriptor */
+static struct irq_chip xgpio_irqchip = {
+	.name		= "xgpio",
+	.irq_mask	= xgpio_irq_mask,
+	.irq_unmask	= xgpio_irq_unmask,
+	.irq_set_type	= xgpio_set_irq_type,
+};
+
+/**
+ * xgpio_to_irq - Find out gpio to Linux irq mapping
+ * @gc: Pointer to gpio_chip device structure.
+ * @offset: Gpio pin offset
+ *
+ * Return:
+ * irq number otherwise -EINVAL
+ */
+static int xgpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+	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);
+
+	return irq_find_mapping(chip->irq_domain, offset);
+}
+
+/**
+ * xgpio_irqhandler - Gpio interrupt service routine
+ * @irq: gpio irq number
+ * @desc: Pointer to interrupt description
+ */
+static void xgpio_irqhandler(unsigned int irq, struct irq_desc *desc)
+{
+	struct xgpio_instance *chip = (struct xgpio_instance *)
+						irq_get_handler_data(irq);
+	struct of_mm_gpio_chip *mm_gc = &chip->mmchip;
+	struct irq_chip *irqchip = irq_desc_get_chip(desc);
+	int offset;
+	unsigned long val;
+
+	chained_irq_enter(irqchip, desc);
+
+	val = xgpio_readreg(mm_gc->regs + chip->offset);
+	/* Only rising edge is supported */
+	val &= chip->irq_enable;
+
+	for_each_set_bit(offset, &val, chip->mmchip.gc.ngpio) {
+		generic_handle_irq(chip->irq_base + offset);
+	}
+
+	xgpio_writereg(mm_gc->regs + XGPIO_IPISR_OFFSET,
+		       chip->offset / XGPIO_CHANNEL_OFFSET + 1);
+
+	chained_irq_exit(irqchip, desc);
+}
+
+static struct lock_class_key gpio_lock_class;
+
+/**
+ * xgpio_irq_setup - Allocate irq for gpio and setup appropriate functions
+ * @np: Device node of the GPIO chip
+ * @chip: Pointer to private gpio channel structure
+ *
+ * Return:
+ * 0 if success, otherwise -1
+ */
+static int xgpio_irq_setup(struct device_node *np, struct xgpio_instance *chip)
+{
+	u32 pin_num;
+	struct resource res;
+
+	int ret = of_irq_to_resource(np, 0, &res);
+	if (!ret) {
+		pr_info("GPIO IRQ not connected\n");
+		return 0;
+	}
+
+	chip->mmchip.gc.of_xlate = xgpio_xlate;
+	chip->mmchip.gc.of_gpio_n_cells = 2;
+	chip->mmchip.gc.to_irq = xgpio_to_irq;
+
+	chip->irq_base = irq_alloc_descs(-1, 0, chip->mmchip.gc.ngpio, 0);
+	if (chip->irq_base < 0) {
+		pr_err("Couldn't allocate IRQ numbers\n");
+		return -1;
+	}
+	chip->irq_domain = irq_domain_add_legacy(np, chip->mmchip.gc.ngpio,
+						 chip->irq_base, 0,
+						 &irq_domain_simple_ops, NULL);
+
+	/*
+	 * set the irq chip, handler and irq chip data for callbacks for
+	 * each pin
+	 */
+	for (pin_num = 0; pin_num < chip->mmchip.gc.ngpio; pin_num++) {
+		u32 gpio_irq = irq_find_mapping(chip->irq_domain, pin_num);
+		irq_set_lockdep_class(gpio_irq, &gpio_lock_class);
+		pr_debug("IRQ Base: %d, Pin %d = IRQ %d\n",
+			chip->irq_base,	pin_num, gpio_irq);
+		irq_set_chip_and_handler(gpio_irq, &xgpio_irqchip,
+					 handle_simple_irq);
+		irq_set_chip_data(gpio_irq, (void *)chip);
+#ifdef CONFIG_ARCH_ZYNQ
+		set_irq_flags(gpio_irq, IRQF_VALID);
+#endif
+	}
+	irq_set_handler_data(res.start, (void *)chip);
+	irq_set_chained_handler(res.start, xgpio_irqhandler);
+
+	return 0;
+}
+
+/**
  * xgpio_of_probe - Probe method for the GPIO device.
  * @np: pointer to device tree node
  *
  * 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.
+ * driver data structure.
+ *
+ * Return:
+ * It returns 0, if the driver is bound to the GPIO device, or
+ * a negative value if there is an error.
  */
-static int xgpio_of_probe(struct device_node *np)
+static int xgpio_of_probe(struct platform_device *pdev)
 {
+	struct device_node *np = pdev->dev.of_node;
 	struct xgpio_instance *chip;
 	int status = 0;
 	const u32 *tree_info;
 
-	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
 		return -ENOMEM;
 
@@ -230,18 +478,24 @@  static int xgpio_of_probe(struct device_node *np)
 	/* Call the OF gpio helper to setup and register the GPIO device */
 	status = of_mm_gpiochip_add(np, &chip->mmchip);
 	if (status) {
-		kfree(chip);
 		pr_err("%s: error in probe function with status %d\n",
 		       np->full_name, status);
 		return status;
 	}
 
+	status = xgpio_irq_setup(np, chip);
+	if (status) {
+		pr_err("%s: GPIO IRQ initialization failed %d\n",
+		       np->full_name, status);
+		return status;
+	}
+
 	pr_info("XGpio: %s: registered, base is %d\n", np->full_name,
 							chip->mmchip.gc.base);
 
 	tree_info = of_get_property(np, "xlnx,is-dual", NULL);
 	if (tree_info && be32_to_cpup(tree_info)) {
-		chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+		chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
 		if (!chip)
 			return -ENOMEM;
 
@@ -274,12 +528,18 @@  static int xgpio_of_probe(struct device_node *np)
 
 		chip->mmchip.save_regs = xgpio_save_regs;
 
+		status = xgpio_irq_setup(np, chip);
+		if (status) {
+			pr_err("%s: GPIO IRQ initialization failed %d\n",
+			      np->full_name, status);
+			return status;
+		}
+
 		/* Call the OF gpio helper to setup and register the GPIO dev */
 		status = of_mm_gpiochip_add(np, &chip->mmchip);
 		if (status) {
-			kfree(chip);
 			pr_err("%s: error in probe function with status %d\n",
-			np->full_name, status);
+			       np->full_name, status);
 			return status;
 		}
 		pr_info("XGpio: %s: dual channel registered, base is %d\n",
@@ -293,15 +553,20 @@  static struct of_device_id xgpio_of_match[] = {
 	{ .compatible = "xlnx,xps-gpio-1.00.a", },
 	{ /* end of list */ },
 };
+MODULE_DEVICE_TABLE(of, xgpio_of_match);
+
+static struct platform_driver xilinx_gpio_driver = {
+	.probe = xgpio_of_probe,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "xilinx-gpio",
+		.of_match_table = xgpio_of_match,
+	},
+};
 
 static int __init xgpio_init(void)
 {
-	struct device_node *np;
-
-	for_each_matching_node(np, xgpio_of_match)
-		xgpio_of_probe(np);
-
-	return 0;
+	return platform_driver_register(&xilinx_gpio_driver);
 }
 
 /* Make sure we get initialized before anyone else tries to use us */
diff --git a/drivers/gpio/gpio-xilinxps.c b/drivers/gpio/gpio-xilinxps.c
new file mode 100644
index 0000000..d6b681b
--- /dev/null
+++ b/drivers/gpio/gpio-xilinxps.c
@@ -0,0 +1,722 @@ 
+/*
+ * Xilinx PS GPIO device driver
+ *
+ * 2009-2011 (c) Xilinx, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * 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., 675 Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeup.h>
+#include <linux/slab.h>
+#include <asm/mach/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define DRIVER_NAME "xgpiops"
+#define XGPIOPS_NR_GPIOS	118
+
+static struct irq_domain *irq_domain;
+
+/* Register offsets for the GPIO device */
+
+#define XGPIOPS_DATA_LSW_OFFSET(BANK)	(0x000 + (8 * BANK)) /* LSW Mask &
+								Data -WO */
+#define XGPIOPS_DATA_MSW_OFFSET(BANK)	(0x004 + (8 * BANK)) /* MSW Mask &
+								Data -WO */
+#define XGPIOPS_DATA_OFFSET(BANK)	(0x040 + (4 * BANK)) /* Data Register
+								-RW */
+#define XGPIOPS_DIRM_OFFSET(BANK)	(0x204 + (0x40 * BANK)) /* Direction
+								mode reg-RW */
+#define XGPIOPS_OUTEN_OFFSET(BANK)	(0x208 + (0x40 * BANK)) /* Output
+								enable reg-RW
+								 */
+#define XGPIOPS_INTMASK_OFFSET(BANK)	(0x20C + (0x40 * BANK)) /* Interrupt
+								mask reg-RO */
+#define XGPIOPS_INTEN_OFFSET(BANK)	(0x210 + (0x40 * BANK)) /* Interrupt
+								enable reg-WO
+								 */
+#define XGPIOPS_INTDIS_OFFSET(BANK)	(0x214 + (0x40 * BANK)) /* Interrupt
+								disable reg-WO
+								 */
+#define XGPIOPS_INTSTS_OFFSET(BANK)	(0x218 + (0x40 * BANK)) /* Interrupt
+								status reg-RO
+								 */
+#define XGPIOPS_INTTYPE_OFFSET(BANK)	(0x21C + (0x40 * BANK)) /* Interrupt
+								type reg-RW
+								 */
+#define XGPIOPS_INTPOL_OFFSET(BANK)	(0x220 + (0x40 * BANK)) /* Interrupt
+								polarity reg
+								-RW */
+#define XGPIOPS_INTANY_OFFSET(BANK)	(0x224 + (0x40 * BANK)) /* Interrupt on
+								any, reg-RW */
+
+/* Read/Write access to the GPIO PS registers */
+#define xgpiops_readreg(offset)		__raw_readl(offset)
+#define xgpiops_writereg(val, offset)	__raw_writel(val, offset)
+
+static unsigned int xgpiops_pin_table[] = {
+	31, /* 0 - 31 */
+	53, /* 32 - 53 */
+	85, /* 54 - 85 */
+	117 /* 86 - 117 */
+};
+
+/**
+ * struct xgpiops - gpio device private data structure
+ * @chip:	instance of the gpio_chip
+ * @base_addr:	base address of the GPIO device
+ * @gpio_lock:	lock used for synchronization
+ */
+struct xgpiops {
+	struct gpio_chip chip;
+	void __iomem *base_addr;
+	unsigned int irq;
+	unsigned int irq_base;
+	struct clk *clk;
+	spinlock_t gpio_lock;
+};
+
+/**
+ * xgpiops_get_bank_pin - Get the bank number and pin number within that bank
+ * for a given pin in the GPIO device
+ * @pin_num:	gpio pin number within the device
+ * @bank_num:	an output parameter used to return the bank number of the gpio
+ *		pin
+ * @bank_pin_num: an output parameter used to return pin number within a bank
+ *		  for the given gpio pin
+ *
+ * Returns the bank number.
+ */
+static inline void xgpiops_get_bank_pin(unsigned int pin_num,
+					 unsigned int *bank_num,
+					 unsigned int *bank_pin_num)
+{
+	for (*bank_num = 0; *bank_num < 4; (*bank_num)++)
+		if (pin_num <= xgpiops_pin_table[*bank_num])
+			break;
+
+	if (*bank_num == 0)
+		*bank_pin_num = pin_num;
+	else
+		*bank_pin_num = pin_num %
+					(xgpiops_pin_table[*bank_num - 1] + 1);
+}
+
+/**
+ * xgpiops_get_value - Get the state of the specified pin of GPIO device
+ * @chip:	gpio_chip instance to be worked on
+ * @pin:	gpio pin number within the device
+ *
+ * This function reads the state of the specified pin of the GPIO device.
+ * It returns 0 if the pin is low, 1 if pin is high.
+ */
+static int xgpiops_get_value(struct gpio_chip *chip, unsigned int pin)
+{
+	unsigned int bank_num, bank_pin_num;
+	struct xgpiops *gpio = container_of(chip, struct xgpiops, chip);
+
+	xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num);
+
+	return (xgpiops_readreg(gpio->base_addr +
+				 XGPIOPS_DATA_OFFSET(bank_num)) >>
+		bank_pin_num) & 1;
+}
+
+/**
+ * xgpiops_set_value - Modify the state of the pin with specified value
+ * @chip:	gpio_chip instance to be worked on
+ * @pin:	gpio pin number within the device
+ * @state:	value used to modify the state of the specified pin
+ *
+ * This function calculates the register offset (i.e to lower 16 bits or
+ * upper 16 bits) based on the given pin number and sets the state of a
+ * gpio pin to the specified value. The state is either 0 or non-zero.
+ */
+static void xgpiops_set_value(struct gpio_chip *chip, unsigned int pin,
+			       int state)
+{
+	unsigned long flags;
+	unsigned int reg_offset;
+	unsigned int bank_num, bank_pin_num;
+	struct xgpiops *gpio = container_of(chip, struct xgpiops, chip);
+
+	xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num);
+
+	if (bank_pin_num >= 16) {
+		bank_pin_num -= 16; /* only 16 data bits in bit maskable reg */
+		reg_offset = XGPIOPS_DATA_MSW_OFFSET(bank_num);
+	} else {
+		reg_offset = XGPIOPS_DATA_LSW_OFFSET(bank_num);
+	}
+
+	/*
+	 * get the 32 bit value to be written to the mask/data register where
+	 * the upper 16 bits is the mask and lower 16 bits is the data
+	 */
+	if (state)
+		state = 1;
+	state = ~(1 << (bank_pin_num + 16)) & ((state << bank_pin_num) |
+					       0xFFFF0000);
+
+	spin_lock_irqsave(&gpio->gpio_lock, flags);
+	xgpiops_writereg(state, gpio->base_addr + reg_offset);
+	spin_unlock_irqrestore(&gpio->gpio_lock, flags);
+}
+
+/**
+ * xgpiops_dir_in - Set the direction of the specified GPIO pin as input
+ * @chip:	gpio_chip instance to be worked on
+ * @pin:	gpio pin number within the device
+ *
+ * This function uses the read-modify-write sequence to set the direction of
+ * the gpio pin as input. Returns 0 always.
+ */
+static int xgpiops_dir_in(struct gpio_chip *chip, unsigned int pin)
+{
+	unsigned int reg, bank_num, bank_pin_num;
+	struct xgpiops *gpio = container_of(chip, struct xgpiops, chip);
+
+	xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num);
+	/* clear the bit in direction mode reg to set the pin as input */
+	reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));
+	reg &= ~(1 << bank_pin_num);
+	xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));
+
+	return 0;
+}
+
+/**
+ * xgpiops_dir_out - Set the direction of the specified GPIO pin as output
+ * @chip:	gpio_chip instance to be worked on
+ * @pin:	gpio pin number within the device
+ * @state:	value to be written to specified pin
+ *
+ * This function sets the direction of specified GPIO pin as output, configures
+ * the Output Enable register for the pin and uses xgpiops_set to set the state
+ * of the pin to the value specified. Returns 0 always.
+ */
+static int xgpiops_dir_out(struct gpio_chip *chip, unsigned int pin, int state)
+{
+	struct xgpiops *gpio = container_of(chip, struct xgpiops, chip);
+	unsigned int reg, bank_num, bank_pin_num;
+
+	xgpiops_get_bank_pin(pin, &bank_num, &bank_pin_num);
+
+	/* set the GPIO pin as output */
+	reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));
+	reg |= 1 << bank_pin_num;
+	xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_DIRM_OFFSET(bank_num));
+
+	/* configure the output enable reg for the pin */
+	reg = xgpiops_readreg(gpio->base_addr + XGPIOPS_OUTEN_OFFSET(bank_num));
+	reg |= 1 << bank_pin_num;
+	xgpiops_writereg(reg, gpio->base_addr + XGPIOPS_OUTEN_OFFSET(bank_num));
+
+	/* set the state of the pin */
+	xgpiops_set_value(chip, pin, state);
+	return 0;
+}
+
+static int xgpiops_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	return irq_find_mapping(irq_domain, offset);
+}
+
+/**
+ * xgpiops_irq_ack - Acknowledge the interrupt of a gpio pin
+ * @irq_data: irq data containing irq number of gpio pin for the irq to ack
+ *
+ * This function calculates gpio pin number from irq number and sets the bit
+ * in the Interrupt Status Register of the corresponding bank, to ACK the irq.
+ */
+static void xgpiops_irq_ack(struct irq_data *irq_data)
+{
+	struct xgpiops *gpio = (struct xgpiops *)
+				irq_data_get_irq_chip_data(irq_data);
+	unsigned int device_pin_num, bank_num, bank_pin_num;
+
+	device_pin_num = irq_data->hwirq;
+	xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+	xgpiops_writereg(1 << bank_pin_num, gpio->base_addr +
+			(XGPIOPS_INTSTS_OFFSET(bank_num)));
+}
+
+/**
+ * xgpiops_irq_mask - Disable the interrupts for a gpio pin
+ * @irq:	irq number of gpio pin for which interrupt is to be disabled
+ *
+ * This function calculates gpio pin number from irq number and sets the
+ * bit in the Interrupt Disable register of the corresponding bank to disable
+ * interrupts for that pin.
+ */
+static void xgpiops_irq_mask(struct irq_data *irq_data)
+{
+	struct xgpiops *gpio = (struct xgpiops *)
+				irq_data_get_irq_chip_data(irq_data);
+	unsigned int device_pin_num, bank_num, bank_pin_num;
+
+	device_pin_num = irq_data->hwirq;
+	xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+	xgpiops_writereg(1 << bank_pin_num,
+			  gpio->base_addr + XGPIOPS_INTDIS_OFFSET(bank_num));
+}
+
+/**
+ * xgpiops_irq_unmask - Enable the interrupts for a gpio pin
+ * @irq_data: irq data containing irq number of gpio pin for the irq to enable
+ *
+ * This function calculates the gpio pin number from irq number and sets the
+ * bit in the Interrupt Enable register of the corresponding bank to enable
+ * interrupts for that pin.
+ */
+static void xgpiops_irq_unmask(struct irq_data *irq_data)
+{
+	struct xgpiops *gpio = irq_data_get_irq_chip_data(irq_data);
+	unsigned int device_pin_num, bank_num, bank_pin_num;
+
+	device_pin_num = irq_data->hwirq;
+	xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+	xgpiops_writereg(1 << bank_pin_num,
+			  gpio->base_addr + XGPIOPS_INTEN_OFFSET(bank_num));
+}
+
+/**
+ * xgpiops_set_irq_type - Set the irq type for a gpio pin
+ * @irq_data:	irq data containing irq number of gpio pin
+ * @type:	interrupt type that is to be set for the gpio pin
+ *
+ * This function gets the gpio pin number and its bank from the gpio pin number
+ * and configures the INT_TYPE, INT_POLARITY and INT_ANY registers. Returns 0,
+ * negative error otherwise.
+ * TYPE-EDGE_RISING,  INT_TYPE - 1, INT_POLARITY - 1,  INT_ANY - 0;
+ * TYPE-EDGE_FALLING, INT_TYPE - 1, INT_POLARITY - 0,  INT_ANY - 0;
+ * TYPE-EDGE_BOTH,    INT_TYPE - 1, INT_POLARITY - NA, INT_ANY - 1;
+ * TYPE-LEVEL_HIGH,   INT_TYPE - 0, INT_POLARITY - 1,  INT_ANY - NA;
+ * TYPE-LEVEL_LOW,    INT_TYPE - 0, INT_POLARITY - 0,  INT_ANY - NA
+ */
+static int xgpiops_set_irq_type(struct irq_data *irq_data, unsigned int type)
+{
+	struct xgpiops *gpio = irq_data_get_irq_chip_data(irq_data);
+	unsigned int device_pin_num, bank_num, bank_pin_num;
+	unsigned int int_type, int_pol, int_any;
+
+	device_pin_num = irq_data->hwirq;
+	xgpiops_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+
+	int_type = xgpiops_readreg(gpio->base_addr +
+				    XGPIOPS_INTTYPE_OFFSET(bank_num));
+	int_pol = xgpiops_readreg(gpio->base_addr +
+				   XGPIOPS_INTPOL_OFFSET(bank_num));
+	int_any = xgpiops_readreg(gpio->base_addr +
+				   XGPIOPS_INTANY_OFFSET(bank_num));
+
+	/*
+	 * based on the type requested, configure the INT_TYPE, INT_POLARITY
+	 * and INT_ANY registers
+	 */
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		int_type |= (1 << bank_pin_num);
+		int_pol |= (1 << bank_pin_num);
+		int_any &= ~(1 << bank_pin_num);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		int_type |= (1 << bank_pin_num);
+		int_pol &= ~(1 << bank_pin_num);
+		int_any &= ~(1 << bank_pin_num);
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		int_type |= (1 << bank_pin_num);
+		int_any |= (1 << bank_pin_num);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		int_type &= ~(1 << bank_pin_num);
+		int_pol |= (1 << bank_pin_num);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		int_type &= ~(1 << bank_pin_num);
+		int_pol &= ~(1 << bank_pin_num);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	xgpiops_writereg(int_type,
+			  gpio->base_addr + XGPIOPS_INTTYPE_OFFSET(bank_num));
+	xgpiops_writereg(int_pol,
+			  gpio->base_addr + XGPIOPS_INTPOL_OFFSET(bank_num));
+	xgpiops_writereg(int_any,
+			  gpio->base_addr + XGPIOPS_INTANY_OFFSET(bank_num));
+	return 0;
+}
+
+static int xgpiops_set_wake(struct irq_data *data, unsigned int on)
+{
+	if (on)
+		xgpiops_irq_unmask(data);
+	else
+		xgpiops_irq_mask(data);
+
+	return 0;
+}
+
+/* irq chip descriptor */
+static struct irq_chip xgpiops_irqchip = {
+	.name		= DRIVER_NAME,
+	.irq_ack	= xgpiops_irq_ack,
+	.irq_mask	= xgpiops_irq_mask,
+	.irq_unmask	= xgpiops_irq_unmask,
+	.irq_set_type	= xgpiops_set_irq_type,
+	.irq_set_wake	= xgpiops_set_wake,
+};
+
+/**
+ * xgpiops_irqhandler - IRQ handler for the gpio banks of a gpio device
+ * @irq:	irq number of the gpio bank where interrupt has occurred
+ * @desc:	irq descriptor instance of the 'irq'
+ *
+ * This function reads the Interrupt Status Register of each bank to get the
+ * gpio pin number which has triggered an interrupt. It then acks the triggered
+ * interrupt and calls the pin specific handler set by the higher layer
+ * application for that pin.
+ * Note: A bug is reported if no handler is set for the gpio pin.
+ */
+static void xgpiops_irqhandler(unsigned int irq, struct irq_desc *desc)
+{
+	struct xgpiops *gpio = (struct xgpiops *)irq_get_handler_data(irq);
+	int gpio_irq = gpio->irq_base;
+	unsigned int int_sts, int_enb, bank_num;
+	struct irq_desc *gpio_irq_desc;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+
+	chained_irq_enter(chip, desc);
+
+	for (bank_num = 0; bank_num < 4; bank_num++) {
+		int_sts = xgpiops_readreg(gpio->base_addr +
+					   XGPIOPS_INTSTS_OFFSET(bank_num));
+		int_enb = xgpiops_readreg(gpio->base_addr +
+					   XGPIOPS_INTMASK_OFFSET(bank_num));
+		int_sts &= ~int_enb;
+
+		for (; int_sts != 0; int_sts >>= 1, gpio_irq++) {
+			if ((int_sts & 1) == 0)
+				continue;
+			gpio_irq_desc = irq_to_desc(gpio_irq);
+			BUG_ON(!gpio_irq_desc);
+			chip = irq_desc_get_chip(gpio_irq_desc);
+			BUG_ON(!chip);
+			chip->irq_ack(&gpio_irq_desc->irq_data);
+
+			/* call the pin specific handler */
+			generic_handle_irq(gpio_irq);
+		}
+		/* shift to first virtual irq of next bank */
+		gpio_irq = gpio->irq_base + xgpiops_pin_table[bank_num] + 1;
+	}
+
+	chip = irq_desc_get_chip(desc);
+	chained_irq_exit(chip, desc);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int xgpiops_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct xgpiops *gpio = platform_get_drvdata(pdev);
+
+	if (!device_may_wakeup(dev)) {
+		if (!pm_runtime_suspended(dev))
+			clk_disable(gpio->clk);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int xgpiops_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct xgpiops *gpio = platform_get_drvdata(pdev);
+
+	if (!device_may_wakeup(dev)) {
+		if (!pm_runtime_suspended(dev))
+			return clk_enable(gpio->clk);
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int xgpiops_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct xgpiops *gpio = platform_get_drvdata(pdev);
+
+	clk_disable(gpio->clk);
+
+	return 0;
+}
+
+static int xgpiops_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct xgpiops *gpio = platform_get_drvdata(pdev);
+
+	return clk_enable(gpio->clk);
+}
+
+static int xgpiops_idle(struct device *dev)
+{
+	return pm_schedule_suspend(dev, 1);
+}
+
+static int xgpiops_request(struct gpio_chip *chip, unsigned offset)
+{
+	int ret;
+
+	ret = pm_runtime_get_sync(chip->dev);
+
+	/*
+	 * If the device is already active pm_runtime_get() will return 1 on
+	 * success, but gpio_request still needs to return 0.
+	 */
+	return ret < 0 ? ret : 0;
+}
+
+static void xgpiops_free(struct gpio_chip *chip, unsigned offset)
+{
+	pm_runtime_put_sync(chip->dev);
+}
+
+static void xgpiops_pm_runtime_init(struct platform_device *pdev)
+{
+	struct xgpiops *gpio = platform_get_drvdata(pdev);
+
+	clk_disable(gpio->clk);
+	pm_runtime_enable(&pdev->dev);
+}
+
+#else /* ! CONFIG_PM_RUNTIME */
+#define xgpiops_request	NULL
+#define xgpiops_free	NULL
+static void xgpiops_pm_runtime_init(struct platform_device *pdev) {}
+#endif /* ! CONFIG_PM_RUNTIME */
+
+#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_PM_SLEEP)
+static const struct dev_pm_ops xgpiops_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(xgpiops_suspend, xgpiops_resume)
+	SET_RUNTIME_PM_OPS(xgpiops_runtime_suspend, xgpiops_runtime_resume,
+			xgpiops_idle)
+};
+#define XGPIOPS_PM	(&xgpiops_dev_pm_ops)
+
+#else /*! CONFIG_PM_RUNTIME || ! CONFIG_PM_SLEEP */
+#define XGPIOPS_PM	NULL
+#endif /*! CONFIG_PM_RUNTIME */
+
+/**
+ * xgpiops_probe - Initialization method for a xgpiops device
+ * @pdev:	platform device instance
+ *
+ * This function allocates memory resources for the gpio device and registers
+ * all the banks of the device. It will also set up interrupts for the gpio
+ * pins.
+ * Note: Interrupts are disabled for all the banks during initialization.
+ * Returns 0 on success, negative error otherwise.
+ */
+static int xgpiops_probe(struct platform_device *pdev)
+{
+	int ret;
+	unsigned int irq_num;
+	struct xgpiops *gpio;
+	struct gpio_chip *chip;
+	resource_size_t remap_size;
+	struct resource *mem_res = NULL;
+	int pin_num, bank_num, gpio_irq;
+
+	gpio = kzalloc(sizeof(struct xgpiops), GFP_KERNEL);
+	if (!gpio) {
+		dev_err(&pdev->dev,
+			"couldn't allocate memory for gpio private data\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&gpio->gpio_lock);
+
+	platform_set_drvdata(pdev, gpio);
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "No memory resource\n");
+		ret = -ENODEV;
+		goto err_free_gpio;
+	}
+
+	remap_size = mem_res->end - mem_res->start + 1;
+	if (!request_mem_region(mem_res->start, remap_size, pdev->name)) {
+		dev_err(&pdev->dev, "Cannot request IO\n");
+		ret = -ENXIO;
+		goto err_free_gpio;
+	}
+
+	gpio->base_addr = ioremap(mem_res->start, remap_size);
+	if (gpio->base_addr == NULL) {
+		dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n",
+			(unsigned long)mem_res->start);
+		ret = -ENOMEM;
+		goto err_release_region;
+	}
+
+	irq_num = platform_get_irq(pdev, 0);
+	gpio->irq = irq_num;
+
+	/* configure the gpio chip */
+	chip = &gpio->chip;
+	chip->label = "xgpiops";
+	chip->owner = THIS_MODULE;
+	chip->dev = &pdev->dev;
+	chip->get = xgpiops_get_value;
+	chip->set = xgpiops_set_value;
+	chip->request = xgpiops_request;
+	chip->free = xgpiops_free;
+	chip->direction_input = xgpiops_dir_in;
+	chip->direction_output = xgpiops_dir_out;
+	chip->to_irq = xgpiops_to_irq;
+	chip->dbg_show = NULL;
+	chip->base = 0;		/* default pin base */
+	chip->ngpio = XGPIOPS_NR_GPIOS;
+	chip->can_sleep = 0;
+
+	gpio->irq_base = irq_alloc_descs(-1, 0, chip->ngpio, 0);
+	if (gpio->irq_base < 0) {
+		dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n");
+		ret = -ENODEV;
+		goto err_iounmap;
+	}
+
+	irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
+					   chip->ngpio, gpio->irq_base, 0,
+					   &irq_domain_simple_ops, NULL);
+
+	/* report a bug if gpio chip registration fails */
+	ret = gpiochip_add(chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "gpio chip registration failed\n");
+		goto err_iounmap;
+	} else {
+		dev_info(&pdev->dev, "gpio at 0x%08lx mapped to 0x%08lx\n",
+			 (unsigned long)mem_res->start,
+			 (unsigned long)gpio->base_addr);
+	}
+
+	/* Enable GPIO clock */
+	gpio->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(gpio->clk)) {
+		dev_err(&pdev->dev, "input clock not found.\n");
+		ret = PTR_ERR(gpio->clk);
+		goto err_chip_remove;
+	}
+	ret = clk_prepare_enable(gpio->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to enable clock.\n");
+		goto err_clk_put;
+	}
+
+	/* disable interrupts for all banks */
+	for (bank_num = 0; bank_num < 4; bank_num++) {
+		xgpiops_writereg(0xffffffff, gpio->base_addr +
+				  XGPIOPS_INTDIS_OFFSET(bank_num));
+	}
+
+	/*
+	 * set the irq chip, handler and irq chip data for callbacks for
+	 * each pin
+	 */
+	for (pin_num = 0; pin_num < min_t(int, XGPIOPS_NR_GPIOS,
+						(int)chip->ngpio); pin_num++) {
+		gpio_irq = irq_find_mapping(irq_domain, pin_num);
+		irq_set_chip_and_handler(gpio_irq, &xgpiops_irqchip,
+							handle_simple_irq);
+		irq_set_chip_data(gpio_irq, (void *)gpio);
+		set_irq_flags(gpio_irq, IRQF_VALID);
+	}
+
+	irq_set_handler_data(irq_num, (void *)gpio);
+	irq_set_chained_handler(irq_num, xgpiops_irqhandler);
+
+	xgpiops_pm_runtime_init(pdev);
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+
+	return 0;
+
+err_clk_put:
+	clk_put(gpio->clk);
+err_chip_remove:
+	gpiochip_remove(chip);
+err_iounmap:
+	iounmap(gpio->base_addr);
+err_release_region:
+	release_mem_region(mem_res->start, remap_size);
+err_free_gpio:
+	platform_set_drvdata(pdev, NULL);
+	kfree(gpio);
+
+	return ret;
+}
+
+static int xgpiops_remove(struct platform_device *pdev)
+{
+	struct xgpiops *gpio = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(gpio->clk);
+	clk_put(gpio->clk);
+	device_set_wakeup_capable(&pdev->dev, 0);
+	return 0;
+}
+
+static struct of_device_id xgpiops_of_match[] = {
+	{ .compatible = "xlnx,ps7-gpio-1.00.a", },
+	{ /* end of table */}
+};
+MODULE_DEVICE_TABLE(of, xgpiops_of_match);
+
+static struct platform_driver xgpiops_driver = {
+	.driver	= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= XGPIOPS_PM,
+		.of_match_table = xgpiops_of_match,
+	},
+	.probe		= xgpiops_probe,
+	.remove		= xgpiops_remove,
+};
+
+/**
+ * xgpiops_init - Initial driver registration call
+ */
+static int __init xgpiops_init(void)
+{
+	return platform_driver_register(&xgpiops_driver);
+}
+
+postcore_initcall(xgpiops_init);