summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-xilinx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-xilinx.c')
-rw-r--r--drivers/gpio/gpio-xilinx.c629
1 files changed, 465 insertions, 164 deletions
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index 8f24478cc18b..be4b4d730547 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -1,33 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Xilinx gpio driver for xps/axi_gpio IP.
*
* Copyright 2008 - 2013 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/bitmap.h>
#include <linux/bitops.h>
-#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/of_gpio.h>
-#include <linux/io.h>
#include <linux/gpio/driver.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/property.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_CHANNEL_OFFSET 0x8
+#define XGPIO_CHANNEL0_OFFSET 0x0
+#define XGPIO_CHANNEL1_OFFSET 0x8
+
+#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 */
/* Read/Write access to the GPIO registers */
#if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86)
@@ -40,42 +43,78 @@
/**
* struct xgpio_instance - Stores information about GPIO device
- * @mmchip: OF GPIO chip for memory mapped banks
- * @gpio_width: GPIO width for every channel
- * @gpio_state: GPIO state shadow register
- * @gpio_dir: GPIO direction shadow register
+ * @gc: GPIO chip
+ * @regs: register block
+ * @map: GPIO pin mapping on hardware side
+ * @state: GPIO write state shadow register
+ * @last_irq_read: GPIO read state register from last interrupt
+ * @dir: GPIO direction shadow register
* @gpio_lock: Lock used for synchronization
+ * @irq: IRQ used by GPIO device
+ * @enable: GPIO IRQ enable/disable bitfield
+ * @rising_edge: GPIO IRQ rising edge enable/disable bitfield
+ * @falling_edge: GPIO IRQ falling edge enable/disable bitfield
+ * @clk: clock resource for this driver
*/
struct xgpio_instance {
- struct of_mm_gpio_chip mmchip;
- unsigned int gpio_width[2];
- u32 gpio_state[2];
- u32 gpio_dir[2];
- spinlock_t gpio_lock[2];
+ struct gpio_chip gc;
+ void __iomem *regs;
+ DECLARE_BITMAP(map, 64);
+ DECLARE_BITMAP(state, 64);
+ DECLARE_BITMAP(last_irq_read, 64);
+ DECLARE_BITMAP(dir, 64);
+ raw_spinlock_t gpio_lock; /* For serializing operations */
+ int irq;
+ DECLARE_BITMAP(enable, 64);
+ DECLARE_BITMAP(rising_edge, 64);
+ DECLARE_BITMAP(falling_edge, 64);
+ struct clk *clk;
};
-static inline int xgpio_index(struct xgpio_instance *chip, int gpio)
+static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch)
{
- if (gpio >= chip->gpio_width[0])
- return 1;
+ switch (ch) {
+ case 0:
+ return XGPIO_CHANNEL0_OFFSET;
+ case 1:
+ return XGPIO_CHANNEL1_OFFSET;
+ default:
+ return -EINVAL;
+ }
+}
- return 0;
+static void xgpio_read_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a)
+{
+ void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32);
+ unsigned long value = xgpio_readreg(addr);
+
+ bitmap_write(a, value, round_down(bit, 32), 32);
}
-static inline int xgpio_regoffset(struct xgpio_instance *chip, int gpio)
+static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a)
{
- if (xgpio_index(chip, gpio))
- return XGPIO_CHANNEL_OFFSET;
+ void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32);
+ unsigned long value = bitmap_read(a, round_down(bit, 32), 32);
- return 0;
+ xgpio_writereg(addr, value);
}
-static inline int xgpio_offset(struct xgpio_instance *chip, int gpio)
+static void xgpio_read_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a)
{
- if (xgpio_index(chip, gpio))
- return gpio - chip->gpio_width[0];
+ unsigned long lastbit = find_nth_bit(chip->map, 64, chip->gc.ngpio - 1);
+ int bit;
- return gpio;
+ for (bit = 0; bit <= lastbit ; bit += 32)
+ xgpio_read_ch(chip, reg, bit, a);
+}
+
+static void xgpio_write_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a)
+{
+ unsigned long lastbit = find_nth_bit(chip->map, 64, chip->gc.ngpio - 1);
+ int bit;
+
+ for (bit = 0; bit <= lastbit ; bit += 32)
+ xgpio_write_ch(chip, reg, bit, a);
}
/**
@@ -91,14 +130,13 @@ static inline int xgpio_offset(struct xgpio_instance *chip, int gpio)
*/
static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)
{
- struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
struct xgpio_instance *chip = gpiochip_get_data(gc);
- u32 val;
+ unsigned long bit = find_nth_bit(chip->map, 64, gpio);
+ DECLARE_BITMAP(state, 64);
- val = xgpio_readreg(mm_gc->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, gpio));
+ xgpio_read_ch(chip, XGPIO_DATA_OFFSET, bit, state);
- return !!(val & BIT(xgpio_offset(chip, gpio)));
+ return test_bit(bit, state);
}
/**
@@ -110,26 +148,22 @@ static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)
* 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)
+static int 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 = gpiochip_get_data(gc);
- int index = xgpio_index(chip, gpio);
- int offset = xgpio_offset(chip, gpio);
+ unsigned long bit = find_nth_bit(chip->map, 64, gpio);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
+ raw_spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write to GPIO signal and set its direction to output */
- if (val)
- chip->gpio_state[index] |= BIT(offset);
- else
- chip->gpio_state[index] &= ~BIT(offset);
+ __assign_bit(bit, chip->state, val);
- xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
+ xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+ return 0;
}
/**
@@ -141,42 +175,29 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
* This function writes the specified values into the specified signals of the
* GPIO devices.
*/
-static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
- unsigned long *bits)
+static int xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
{
+ DECLARE_BITMAP(hw_mask, 64);
+ DECLARE_BITMAP(hw_bits, 64);
+ DECLARE_BITMAP(state, 64);
unsigned long flags;
- struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
struct xgpio_instance *chip = gpiochip_get_data(gc);
- int index = xgpio_index(chip, 0);
- int offset, i;
-
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
-
- /* Write to GPIO signals */
- for (i = 0; i < gc->ngpio; i++) {
- if (*mask == 0)
- break;
- if (index != xgpio_index(chip, i)) {
- xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, i),
- chip->gpio_state[index]);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
- index = xgpio_index(chip, i);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
- }
- if (__test_and_clear_bit(i, mask)) {
- offset = xgpio_offset(chip, i);
- if (test_bit(i, bits))
- chip->gpio_state[index] |= BIT(offset);
- else
- chip->gpio_state[index] &= ~BIT(offset);
- }
- }
- xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, i), chip->gpio_state[index]);
+ bitmap_scatter(hw_mask, mask, chip->map, 64);
+ bitmap_scatter(hw_bits, bits, chip->map, 64);
+
+ raw_spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ bitmap_replace(state, chip->state, hw_bits, hw_mask, 64);
+
+ xgpio_write_ch_all(chip, XGPIO_DATA_OFFSET, state);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ bitmap_copy(chip->state, state, 64);
+
+ raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+ return 0;
}
/**
@@ -191,19 +212,16 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
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 = gpiochip_get_data(gc);
- int index = xgpio_index(chip, gpio);
- int offset = xgpio_offset(chip, gpio);
+ unsigned long bit = find_nth_bit(chip->map, 64, gpio);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
+ raw_spin_lock_irqsave(&chip->gpio_lock, flags);
/* Set the GPIO bit in shadow register and set direction as input */
- chip->gpio_dir[index] |= BIT(offset);
- xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
+ __set_bit(bit, chip->dir);
+ xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0;
}
@@ -223,50 +241,65 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
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 = gpiochip_get_data(gc);
- int index = xgpio_index(chip, gpio);
- int offset = xgpio_offset(chip, gpio);
+ unsigned long bit = find_nth_bit(chip->map, 64, gpio);
- spin_lock_irqsave(&chip->gpio_lock[index], flags);
+ raw_spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write state of GPIO signal */
- if (val)
- chip->gpio_state[index] |= BIT(offset);
- else
- chip->gpio_state[index] &= ~BIT(offset);
- xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
+ __assign_bit(bit, chip->state, val);
+ xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state);
/* Clear the GPIO bit in shadow register and set direction as output */
- chip->gpio_dir[index] &= ~BIT(offset);
- xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET +
- xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
+ __clear_bit(bit, chip->dir);
+ xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir);
- spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0;
}
/**
* xgpio_save_regs - Set initial values of GPIO pins
- * @mm_gc: Pointer to memory mapped GPIO chip structure
+ * @chip: Pointer to GPIO instance
*/
-static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
+static void xgpio_save_regs(struct xgpio_instance *chip)
{
- struct xgpio_instance *chip =
- container_of(mm_gc, struct xgpio_instance, mmchip);
+ xgpio_write_ch_all(chip, XGPIO_DATA_OFFSET, chip->state);
+ xgpio_write_ch_all(chip, XGPIO_TRI_OFFSET, chip->dir);
+}
- xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state[0]);
- xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir[0]);
+static int xgpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ int ret;
- if (!chip->gpio_width[1])
- return;
+ ret = pm_runtime_get_sync(chip->parent);
+ /*
+ * 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;
+}
- xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET + XGPIO_CHANNEL_OFFSET,
- chip->gpio_state[1]);
- xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET + XGPIO_CHANNEL_OFFSET,
- chip->gpio_dir[1]);
+static void xgpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ pm_runtime_put(chip->parent);
+}
+
+static int xgpio_suspend(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+ struct irq_data *data = irq_get_irq_data(gpio->irq);
+
+ if (!data) {
+ dev_dbg(dev, "IRQ not connected\n");
+ return pm_runtime_force_suspend(dev);
+ }
+
+ if (!irqd_is_wakeup_set(data))
+ return pm_runtime_force_suspend(dev);
+
+ return 0;
}
/**
@@ -277,17 +310,227 @@ static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
*
* Return: 0 always
*/
-static int xgpio_remove(struct platform_device *pdev)
+static void xgpio_remove(struct platform_device *pdev)
+{
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+/**
+ * xgpio_irq_ack - Acknowledge a child GPIO interrupt.
+ * @irq_data: per IRQ and chip data passed down to chip functions
+ * This currently does nothing, but irq_ack is unconditionally called by
+ * handle_edge_irq and therefore must be defined.
+ */
+static void xgpio_irq_ack(struct irq_data *irq_data)
+{
+}
+
+static int xgpio_resume(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+ struct irq_data *data = irq_get_irq_data(gpio->irq);
+
+ if (!data) {
+ dev_dbg(dev, "IRQ not connected\n");
+ return pm_runtime_force_resume(dev);
+ }
+
+ if (!irqd_is_wakeup_set(data))
+ return pm_runtime_force_resume(dev);
+
+ return 0;
+}
+
+static int xgpio_runtime_suspend(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+
+ clk_disable(gpio->clk);
+
+ return 0;
+}
+
+static int xgpio_runtime_resume(struct device *dev)
+{
+ struct xgpio_instance *gpio = dev_get_drvdata(dev);
+
+ return clk_enable(gpio->clk);
+}
+
+static const struct dev_pm_ops xgpio_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
+ RUNTIME_PM_OPS(xgpio_runtime_suspend, xgpio_runtime_resume, NULL)
+};
+
+/**
+ * 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)
{
- struct xgpio_instance *chip = platform_get_drvdata(pdev);
+ unsigned long flags;
+ struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
+ int irq_offset = irqd_to_hwirq(irq_data);
+ unsigned long bit = find_nth_bit(chip->map, 64, irq_offset), enable;
+ u32 mask = BIT(bit / 32), temp;
+
+ raw_spin_lock_irqsave(&chip->gpio_lock, flags);
- of_mm_gpiochip_remove(&chip->mmchip);
+ __clear_bit(bit, chip->enable);
+
+ enable = bitmap_read(chip->enable, round_down(bit, 32), 32);
+ if (enable == 0) {
+ /* Disable per channel interrupt */
+ temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
+ temp &= ~mask;
+ xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, temp);
+ }
+ raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+ gpiochip_disable_irq(&chip->gc, irq_offset);
+}
+
+/**
+ * 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);
+ int irq_offset = irqd_to_hwirq(irq_data);
+ unsigned long bit = find_nth_bit(chip->map, 64, irq_offset), enable;
+ u32 mask = BIT(bit / 32), val;
+
+ gpiochip_enable_irq(&chip->gc, irq_offset);
+
+ raw_spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ enable = bitmap_read(chip->enable, round_down(bit, 32), 32);
+ if (enable == 0) {
+ /* Clear any existing per-channel interrupts */
+ val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
+ val &= mask;
+ xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, val);
+
+ /* Update GPIO IRQ read data before enabling interrupt*/
+ xgpio_read_ch(chip, XGPIO_DATA_OFFSET, bit, chip->last_irq_read);
+
+ /* Enable per channel interrupt */
+ val = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
+ val |= mask;
+ xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val);
+ }
+
+ __set_bit(bit, chip->enable);
+
+ raw_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 -EINVAL
+ */
+static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
+{
+ struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
+ int irq_offset = irqd_to_hwirq(irq_data);
+ unsigned long bit = find_nth_bit(chip->map, 64, irq_offset);
+ /*
+ * The Xilinx GPIO hardware provides a single interrupt status
+ * indication for any state change in a given GPIO channel (bank).
+ * Therefore, only rising edge or falling edge triggers are
+ * supported.
+ */
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_BOTH:
+ __set_bit(bit, chip->rising_edge);
+ __set_bit(bit, chip->falling_edge);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ __set_bit(bit, chip->rising_edge);
+ __clear_bit(bit, chip->falling_edge);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ __clear_bit(bit, chip->rising_edge);
+ __set_bit(bit, chip->falling_edge);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ irq_set_handler_locked(irq_data, handle_edge_irq);
return 0;
}
/**
- * xgpio_of_probe - Probe method for the GPIO device.
+ * xgpio_irqhandler - Gpio interrupt service routine
+ * @desc: Pointer to interrupt description
+ */
+static void xgpio_irqhandler(struct irq_desc *desc)
+{
+ struct xgpio_instance *chip = irq_desc_get_handler_data(desc);
+ struct gpio_chip *gc = &chip->gc;
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+ DECLARE_BITMAP(rising, 64);
+ DECLARE_BITMAP(falling, 64);
+ DECLARE_BITMAP(hw, 64);
+ DECLARE_BITMAP(sw, 64);
+ int irq_offset;
+ u32 status;
+
+ status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
+ xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, status);
+
+ chained_irq_enter(irqchip, desc);
+
+ raw_spin_lock(&chip->gpio_lock);
+
+ xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, hw);
+
+ bitmap_complement(rising, chip->last_irq_read, 64);
+ bitmap_and(rising, rising, hw, 64);
+ bitmap_and(rising, rising, chip->enable, 64);
+ bitmap_and(rising, rising, chip->rising_edge, 64);
+
+ bitmap_complement(falling, hw, 64);
+ bitmap_and(falling, falling, chip->last_irq_read, 64);
+ bitmap_and(falling, falling, chip->enable, 64);
+ bitmap_and(falling, falling, chip->falling_edge, 64);
+
+ bitmap_copy(chip->last_irq_read, hw, 64);
+ bitmap_or(hw, rising, falling, 64);
+
+ raw_spin_unlock(&chip->gpio_lock);
+
+ dev_dbg(gc->parent, "IRQ rising %*pb falling %*pb\n", 64, rising, 64, falling);
+
+ bitmap_gather(sw, hw, chip->map, 64);
+ for_each_set_bit(irq_offset, sw, 64)
+ generic_handle_domain_irq(gc->irq.domain, irq_offset);
+
+ chained_irq_exit(irqchip, desc);
+}
+
+static const struct irq_chip xgpio_irq_chip = {
+ .name = "gpio-xilinx",
+ .irq_ack = xgpio_irq_ack,
+ .irq_mask = xgpio_irq_mask,
+ .irq_unmask = xgpio_irq_unmask,
+ .irq_set_type = xgpio_set_irq_type,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+/**
+ * xgpio_probe - Probe method for the GPIO device.
* @pdev: pointer to the platform device
*
* Return:
@@ -296,76 +539,133 @@ static int xgpio_remove(struct platform_device *pdev)
*/
static int xgpio_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct xgpio_instance *chip;
int status = 0;
- struct device_node *np = pdev->dev.of_node;
- u32 is_dual;
-
- chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ u32 is_dual = 0;
+ u32 width[2];
+ u32 state[2];
+ u32 dir[2];
+ struct gpio_irq_chip *girq;
+ u32 temp;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
platform_set_drvdata(pdev, chip);
+ /* First, check if the device is dual-channel */
+ device_property_read_u32(dev, "xlnx,is-dual", &is_dual);
+
+ /* Setup defaults */
+ memset32(width, 0, ARRAY_SIZE(width));
+ memset32(state, 0, ARRAY_SIZE(state));
+ memset32(dir, 0xFFFFFFFF, ARRAY_SIZE(dir));
+
/* Update GPIO state shadow register with default value */
- of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state[0]);
+ device_property_read_u32(dev, "xlnx,dout-default", &state[0]);
+ device_property_read_u32(dev, "xlnx,dout-default-2", &state[1]);
+
+ bitmap_from_arr32(chip->state, state, 64);
/* Update GPIO direction shadow register with default value */
- if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0]))
- chip->gpio_dir[0] = 0xFFFFFFFF;
+ device_property_read_u32(dev, "xlnx,tri-default", &dir[0]);
+ device_property_read_u32(dev, "xlnx,tri-default-2", &dir[1]);
+
+ bitmap_from_arr32(chip->dir, dir, 64);
/*
* Check device node and parent device node for device width
* and assume default width of 32
*/
- if (of_property_read_u32(np, "xlnx,gpio-width", &chip->gpio_width[0]))
- chip->gpio_width[0] = 32;
+ if (device_property_read_u32(dev, "xlnx,gpio-width", &width[0]))
+ width[0] = 32;
- spin_lock_init(&chip->gpio_lock[0]);
+ if (width[0] > 32)
+ return -EINVAL;
- if (of_property_read_u32(np, "xlnx,is-dual", &is_dual))
- is_dual = 0;
+ if (is_dual && device_property_read_u32(dev, "xlnx,gpio2-width", &width[1]))
+ width[1] = 32;
- if (is_dual) {
- /* Update GPIO state shadow register with default value */
- of_property_read_u32(np, "xlnx,dout-default-2",
- &chip->gpio_state[1]);
+ if (width[1] > 32)
+ return -EINVAL;
- /* Update GPIO direction shadow register with default value */
- if (of_property_read_u32(np, "xlnx,tri-default-2",
- &chip->gpio_dir[1]))
- chip->gpio_dir[1] = 0xFFFFFFFF;
+ /* Setup hardware pin mapping */
+ bitmap_set(chip->map, 0, width[0]);
+ bitmap_set(chip->map, 32, width[1]);
- /*
- * Check device node and parent device node for device width
- * and assume default width of 32
- */
- if (of_property_read_u32(np, "xlnx,gpio2-width",
- &chip->gpio_width[1]))
- chip->gpio_width[1] = 32;
+ raw_spin_lock_init(&chip->gpio_lock);
- spin_lock_init(&chip->gpio_lock[1]);
- }
+ chip->gc.base = -1;
+ chip->gc.ngpio = bitmap_weight(chip->map, 64);
+ chip->gc.parent = dev;
+ chip->gc.direction_input = xgpio_dir_in;
+ chip->gc.direction_output = xgpio_dir_out;
+ chip->gc.get = xgpio_get;
+ chip->gc.set = xgpio_set;
+ chip->gc.request = xgpio_request;
+ chip->gc.free = xgpio_free;
+ chip->gc.set_multiple = xgpio_set_multiple;
- chip->mmchip.gc.ngpio = chip->gpio_width[0] + chip->gpio_width[1];
- chip->mmchip.gc.parent = &pdev->dev;
- chip->mmchip.gc.direction_input = xgpio_dir_in;
- chip->mmchip.gc.direction_output = xgpio_dir_out;
- chip->mmchip.gc.get = xgpio_get;
- chip->mmchip.gc.set = xgpio_set;
- chip->mmchip.gc.set_multiple = xgpio_set_multiple;
+ chip->gc.label = dev_name(dev);
- chip->mmchip.save_regs = xgpio_save_regs;
+ chip->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(chip->regs)) {
+ dev_err(dev, "failed to ioremap memory resource\n");
+ return PTR_ERR(chip->regs);
+ }
+
+ chip->clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(chip->clk))
+ return dev_err_probe(dev, PTR_ERR(chip->clk), "input clock not found.\n");
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ xgpio_save_regs(chip);
+
+ chip->irq = platform_get_irq_optional(pdev, 0);
+ if (chip->irq <= 0)
+ goto skip_irq;
+
+ /* Disable per-channel interrupts */
+ xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, 0);
+ /* Clear any existing per-channel interrupts */
+ temp = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
+ xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, temp);
+ /* Enable global interrupts */
+ xgpio_writereg(chip->regs + XGPIO_GIER_OFFSET, XGPIO_GIER_IE);
+
+ girq = &chip->gc.irq;
+ gpio_irq_chip_set_chip(girq, &xgpio_irq_chip);
+ girq->parent_handler = xgpio_irqhandler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents) {
+ status = -ENOMEM;
+ goto err_pm_put;
+ }
+ girq->parents[0] = chip->irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
- /* Call the OF gpio helper to setup and register the GPIO device */
- status = of_mm_gpiochip_add_data(np, &chip->mmchip, chip);
+skip_irq:
+ status = devm_gpiochip_add_data(dev, &chip->gc, chip);
if (status) {
- pr_err("%pOF: error in probe function with status %d\n",
- np, status);
- return status;
+ dev_err(dev, "failed to add GPIO chip\n");
+ goto err_pm_put;
}
+ pm_runtime_put(dev);
return 0;
+
+err_pm_put:
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ return status;
}
static const struct of_device_id xgpio_of_match[] = {
@@ -381,6 +681,7 @@ static struct platform_driver xgpio_plat_driver = {
.driver = {
.name = "gpio-xilinx",
.of_match_table = xgpio_of_match,
+ .pm = pm_ptr(&xgpio_dev_pm_ops),
},
};