@@ -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"
@@ -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
@@ -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 */
new file mode 100644
@@ -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);