[WIP] Port xilinx gpio to GPIO_GENERIC

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

 



Hi,

This is my first try switching gpio-xilinx to GPIO_GENERIC. Most of the work
here is based on the EP93xx GPIO which is the only example I found using
multiple banks and bgpio_init.

The way this sets the gpio up is that with respect to the device tree, both
banks can be addressed in the same &gpio (I hope, I havent tested addressing the
second bank yet, mainly because no leds are mapped to it yet). But in the sysfs
(in /sys/class/gpio/), there are two entries, one for each bank.

Something that still needs to be fixed in this implementation is that the pin
numbers are fixed. The first pin from the first bank is automatically assigned
the number 0 and the second bank is adjacent to that. In general setting the pin
number to a fixed value prevents also using other gpio controllers. Is there a
way to find out which start pin (gc->base) a gpio_chip has been assigned when
gc->base has been set to -1?

The other changes are removing all the functions that are already implemented by
gpio-mmio.c.

Once this is implemented. My plan is to handle IRQ support in another commit and
then cherry-pick some of the commits from the linux-xlnx codebase to ensure
feature parity.

Regards,
Alexander

---
 drivers/gpio/Kconfig       |   1 +
 drivers/gpio/gpio-xilinx.c | 357 +++++++------------------------------
 2 files changed, 69 insertions(+), 289 deletions(-)

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 71c0ab46f216..5101919937b3 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -564,6 +564,7 @@ config GPIO_XGENE_SB
 config GPIO_XILINX
 	tristate "Xilinx GPIO support"
 	depends on OF_GPIO
+	select GPIO_GENERIC
 	help
 	  Say yes here to support the Xilinx FPGA GPIO device

diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index e8ec0e33a0a9..161b41d613e0 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -29,246 +29,17 @@

 #define XGPIO_CHANNEL_OFFSET	0x8

-/* Read/Write access to the GPIO registers */
-#if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86)
-# define xgpio_readreg(offset)		readl(offset)
-# define xgpio_writereg(offset, val)	writel(val, offset)
-#else
-# define xgpio_readreg(offset)		__raw_readl(offset)
-# define xgpio_writereg(offset, val)	__raw_writel(val, offset)
-#endif
-
 /**
  * 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
- * @gpio_lock: Lock used for synchronization
+ * @gc: GPIO chips for memory mapped banks
+ * @is_dual: True if the second bank is used
  */
 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[2];
+	bool is_dual;
+	void __iomem *base;
 };

-static inline int xgpio_index(struct xgpio_instance *chip, int gpio)
-{
-	if (gpio >= chip->gpio_width[0])
-		return 1;
-
-	return 0;
-}
-
-static inline int xgpio_regoffset(struct xgpio_instance *chip, int gpio)
-{
-	if (xgpio_index(chip, gpio))
-		return XGPIO_CHANNEL_OFFSET;
-
-	return 0;
-}
-
-static inline int xgpio_offset(struct xgpio_instance *chip, int gpio)
-{
-	if (xgpio_index(chip, gpio))
-		return gpio - chip->gpio_width[0];
-
-	return gpio;
-}
-
-/**
- * xgpio_get - Read the specified signal of the GPIO device.
- * @gc:     Pointer to gpio_chip device structure.
- * @gpio:   GPIO signal number.
- *
- * This function reads the specified signal of the GPIO device.
- *
- * 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)
-{
-	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
-	struct xgpio_instance *chip = gpiochip_get_data(gc);
-	u32 val;
-
-	val = xgpio_readreg(mm_gc->regs + XGPIO_DATA_OFFSET +
-			    xgpio_regoffset(chip, gpio));
-
-	return !!(val & BIT(xgpio_offset(chip, gpio)));
-}
-
-/**
- * xgpio_set - Write the specified signal of the GPIO device.
- * @gc:     Pointer to gpio_chip device structure.
- * @gpio:   GPIO signal number.
- * @val:    Value to be written to specified signal.
- *
- * This function writes the specified value in to the specified signal of the
- * GPIO device.
- */
-static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
-{
-	unsigned long flags;
-	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
-	struct xgpio_instance *chip = gpiochip_get_data(gc);
-	int index =  xgpio_index(chip, gpio);
-	int offset =  xgpio_offset(chip, gpio);
-
-	spin_lock_irqsave(&chip->gpio_lock[index], 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);
-
-	xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
-		       xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
-
-	spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
-}
-
-/**
- * xgpio_set_multiple - Write the specified signals of the GPIO device.
- * @gc:     Pointer to gpio_chip device structure.
- * @mask:   Mask of the GPIOS to modify.
- * @bits:   Value to be wrote on each GPIO
- *
- * 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)
-{
-	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]);
-
-	spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
-}
-
-/**
- * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
- * @gc:     Pointer to gpio_chip device structure.
- * @gpio:   GPIO signal number.
- *
- * 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)
-{
-	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);
-
-	spin_lock_irqsave(&chip->gpio_lock[index], 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]);
-
-	spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
-
-	return 0;
-}
-
-/**
- * xgpio_dir_out - Set the direction of the specified GPIO signal as output.
- * @gc:     Pointer to gpio_chip device structure.
- * @gpio:   GPIO signal number.
- * @val:    Value to be written to specified signal.
- *
- * This function sets the direction of specified GPIO signal as output.
- *
- * 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)
-{
-	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);
-
-	spin_lock_irqsave(&chip->gpio_lock[index], 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]);
-
-	/* 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]);
-
-	spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
-
-	return 0;
-}
-
-/**
- * xgpio_save_regs - Set initial values of GPIO pins
- * @mm_gc: Pointer to memory mapped GPIO chip structure
- */
-static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
-{
-	struct xgpio_instance *chip =
-		container_of(mm_gc, struct xgpio_instance, mmchip);
-
-	xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET,	chip->gpio_state[0]);
-	xgpio_writereg(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir[0]);
-
-	if (!chip->gpio_width[1])
-		return;
-
-	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]);
-}
-
 /**
  * xgpio_remove - Remove method for the GPIO device.
  * @pdev: pointer to the platform device
@@ -281,11 +52,58 @@ static int xgpio_remove(struct platform_device *pdev)
 {
 	struct xgpio_instance *chip = platform_get_drvdata(pdev);

-	of_mm_gpiochip_remove(&chip->mmchip);
+	gpiochip_remove(&chip->gc[0]);
+	if (&chip->is_dual) {
+		gpiochip_remove(&chip->gc[1]);
+	}

 	return 0;
 }

+static int xgpio_add_bank(struct gpio_chip *gc, struct device *dev,
+	void __iomem *mmio_base, int bank, u16 base)
+{
+	int status = 0;
+	u32 gpio_state;
+	u32 gpio_dir;
+	u32 gpio_width;
+	const char *dout_name[2] = {"xlnx,dout-default", "xlnx,dout-default-2"};
+	const char *tri_name[2] = {"xlnx,tri-default", "xlnx,tri-default-2"};
+	const char *width_name[2] = {"xlnx,gpio-width", "xlnx,gpio2-width"};
+	const char *bank_name[2] = {"GPIO1", "GPIO2"};
+	struct device_node *np = dev->of_node;
+
+	/* It only supports two banks */
+	if (bank >> 1)
+		return -1;
+
+	of_property_read_u32(np, dout_name[bank], &gpio_state);
+
+	if (of_property_read_u32(np, tri_name[bank], &gpio_dir))
+		gpio_dir = 0xFFFFFFFF;
+
+	if (of_property_read_u32(np, width_name[bank], &gpio_width))
+		gpio_width = 32;
+
+	status = bgpio_init(gc, dev, 4,
+			    mmio_base + XGPIO_DATA_OFFSET,
+			    NULL,
+			    NULL,
+			    mmio_base + XGPIO_TRI_OFFSET,
+			    NULL,
+			    0);
+	if (status) {
+		return status;
+	}
+	gc->label = bank_name[bank];
+	gc->ngpio = gpio_width;
+	gc->base = base;
+	gc->bgpio_data = gpio_state;
+	gc->bgpio_dir = gpio_dir;
+
+	return devm_gpiochip_add_data(dev, gc, NULL);
+}
+
 /**
  * xgpio_of_probe - Probe method for the GPIO device.
  * @pdev: pointer to the platform device
@@ -297,72 +115,33 @@ static int xgpio_remove(struct platform_device *pdev)
 static int xgpio_probe(struct platform_device *pdev)
 {
 	struct xgpio_instance *chip;
-	int status = 0;
+	struct device *dev = &pdev->dev;
 	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
 	u32 is_dual;

-	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
 		return -ENOMEM;

-	platform_set_drvdata(pdev, chip);
-
-	/* Update GPIO state shadow register with default value */
-	of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state[0]);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	chip->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(chip->base))
+		return PTR_ERR(chip->base);

-	/* 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;
-
-	/*
-	 * 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;
-
-	spin_lock_init(&chip->gpio_lock[0]);
+	if (xgpio_add_bank(&chip->gc[0], dev, chip->base, 0, 0)) {
+		dev_err(dev, "unable to init generic GPIO bank 1\n");
+	}

 	if (of_property_read_u32(np, "xlnx,is-dual", &is_dual))
 		is_dual = 0;
+	chip->is_dual = is_dual;

 	if (is_dual) {
-		/* Update GPIO state shadow register with default value */
-		of_property_read_u32(np, "xlnx,dout-default-2",
-				     &chip->gpio_state[1]);
-
-		/* 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;
-
-		/*
-		 * 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;
-
-		spin_lock_init(&chip->gpio_lock[1]);
-	}
-
-	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->mmchip.save_regs = xgpio_save_regs;
-
-	/* Call the OF gpio helper to setup and register the GPIO device */
-	status = of_mm_gpiochip_add_data(np, &chip->mmchip, chip);
-	if (status) {
-		pr_err("%pOF: error in probe function with status %d\n",
-		       np, status);
-		return status;
+		if (xgpio_add_bank(&chip->gc[1], dev,
+				   chip->base + XGPIO_CHANNEL_OFFSET, 1,
+				   chip->gc[0].ngpio))
+			dev_err(dev, "unable to init generic GPIO bank 2\n");
 	}

 	return 0;
--
2.17.1



[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux