mbox

[v2] gpio: mvebu: new gpio driver for mvebu platforms

Message ID 1344705034-18173-1-git-send-email-thomas.petazzoni@free-electrons.com
State New
Headers show

Pull-request

git://github.com/MISL-EBU-System-SW/mainline-public.git mvebu-gpio

Message

Thomas Petazzoni Aug. 11, 2012, 5:10 p.m. UTC
Hello,

This patch set adds a new GPIO driver for Marvell EBU SoCs, which aims
at replacing the existing GPIO driver from plat-orion/gpio.c. The
driver comes with the necessary DT changes to make it work on Armada
370 and Armada XP, and it has been tested successfully by Sebastian
Hesselbarth on Dove. It should also work flawlessly on other Marvell
EBU SoCs (Kirkwood, Orion, Discovery), but this remains to be tested
by the relevant maintainers.

This new driver reuses a lot of code from the existing
plat-orion/gpio.c driver, but has a number of advantages:

 *) Support for Armada 370 and Armada XP
 *) It is integrated with the mvebu pinctrl driver so that GPIO pins
    are properly muxed, and the GPIO driver knows which GPIO pins are
    output-only or input-only. This driver has been submitted by
    Sebastian Hesselbarth today on the LAKML.
 *) Properly placed in drivers/gpio
 *) More extensible mechanism to support platform differences. The
    plat-orion driver uses a simple mask-offset DT property, which
    works fine for Discovery MV78200 but not for Armada XP. The new
    driver uses different compatible strings to identify the different
    variants of the GPIO controllers.

Changes since v1:
 * Added fixes for Dove from Sebastian Hesselbarth, and his Tested-by
   on the driver patch.

Thanks,

Thomas

The following changes since commit 872b6be95abf9d57e0639d0345fa2b5a0e4fa0f4:

  arm: mvebu: add pinctrl support in defconfig (2012-08-11 11:27:33 +0200)

are available in the git repository at:

  git://github.com/MISL-EBU-System-SW/mainline-public.git mvebu-gpio

for you to fetch changes up to c6347492639521d79e541a90fe2dfeec07d9b1a2:

  arm: mvebu: add gpio support in defconfig (2012-08-11 19:06:40 +0200)

----------------------------------------------------------------
Thomas Petazzoni (5):
      gpio: introduce gpio-mvebu driver for Marvell SoCs
      Documentation: add description of DT binding for the gpio-mvebu driver
      arm: mvebu: use GPIO support now that a driver is available
      arm: mvebu: add DT information for GPIO banks on Armada 370 and XP
      arm: mvebu: add gpio support in defconfig

 .../devicetree/bindings/gpio/gpio-mvebu.txt        |   44 ++
 arch/arm/Kconfig                                   |    1 +
 arch/arm/boot/dts/armada-370.dtsi                  |   30 +
 arch/arm/boot/dts/armada-xp-mv78230.dtsi           |   22 +
 arch/arm/boot/dts/armada-xp-mv78260.dtsi           |   33 +
 arch/arm/boot/dts/armada-xp-mv78460.dtsi           |   33 +
 arch/arm/configs/mvebu_defconfig                   |    2 +
 arch/arm/mach-mvebu/include/mach/gpio.h            |    1 +
 drivers/gpio/Kconfig                               |    6 +
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-mvebu.c                          |  656 ++++++++++++++++++++
 11 files changed, 829 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
 create mode 100644 arch/arm/mach-mvebu/include/mach/gpio.h
 create mode 100644 drivers/gpio/gpio-mvebu.c

Comments

Jean-Christophe PLAGNIOL-VILLARD Aug. 13, 2012, 5:18 a.m. UTC | #1
On 19:10 Sat 11 Aug     , Thomas Petazzoni wrote:
> This driver aims at replacing the arch/arm/plat-orion/gpio.c driver,
> and is designed to be compatible with all Marvell EBU SoCs: Orion,
> Kirkwood, Dove, Armada 370/XP and Discovery.
> 
> It has been successfully tested on Dove and Armada XP at the moment.
> 
> Compared to the plat-orion driver, this new driver has the following
> added benefits:
> 
>  *) Support for Armada 370 and Armada XP
>  *) It is integrated with the mvebu pinctrl driver so that GPIO pins
>     are properly muxed, and the GPIO driver knows which GPIO pins are
>     output-only or input-only.
>  *) Properly placed in drivers/gpio
>  *) More extensible mechanism to support platform differences. The
>     plat-orion driver uses a simple mask-offset DT property, which
>     works fine for Discovery MV78200 but not for Armada XP. The new
>     driver uses different compatible strings to identify the different
>     variants of the GPIO controllers.
> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Andrew Lunn <andrew@lunn.ch>
> Cc: Jason Cooper <jason@lakedaemon.net>
> Cc: Gregory Clement <gregory.clement@free-electrons.com>
> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
>  drivers/gpio/Kconfig      |    6 +
>  drivers/gpio/Makefile     |    1 +
>  drivers/gpio/gpio-mvebu.c |  656 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 663 insertions(+)
>  create mode 100644 drivers/gpio/gpio-mvebu.c
> 
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index b16c8a7..993b0c4 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -150,6 +150,12 @@ config GPIO_MSM_V2
>  	  Qualcomm MSM chips.  Most of the pins on the MSM can be
>  	  selected for GPIO, and are controlled by this driver.
>  
> +config GPIO_MVEBU
> +	def_bool y
> +	depends on ARCH_MVEBU
> +	select GPIO_GENERIC
> +	select GENERIC_IRQ_CHIP
> +
>  config GPIO_MXC
>  	def_bool y
>  	depends on ARCH_MXC
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 153cace..1a33e67 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -41,6 +41,7 @@ obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
>  obj-$(CONFIG_GPIO_MSIC)		+= gpio-msic.o
>  obj-$(CONFIG_GPIO_MSM_V1)	+= gpio-msm-v1.o
>  obj-$(CONFIG_GPIO_MSM_V2)	+= gpio-msm-v2.o
> +obj-$(CONFIG_GPIO_MVEBU)        += gpio-mvebu.o
>  obj-$(CONFIG_GPIO_MXC)		+= gpio-mxc.o
>  obj-$(CONFIG_GPIO_MXS)		+= gpio-mxs.o
>  obj-$(CONFIG_ARCH_OMAP)		+= gpio-omap.o
> diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
> new file mode 100644
> index 0000000..892e3f1
> --- /dev/null
> +++ b/drivers/gpio/gpio-mvebu.c
> @@ -0,0 +1,656 @@
> +/*
> + * GPIO driver for Marvell SoCs
> + *
> + * Copyright (C) 2012 Marvell
> + *
> + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> + * Andrew Lunn <andrew@lunn.ch>
> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2.  This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + *
> + * This driver is a fairly straightforward GPIO driver for the
> + * complete family of Marvell EBU SoC platforms (Orion, Dove,
> + * Kirkwood, Discovery, Armada 370/XP). The only complexity of this
> + * driver is the different register layout that exists between the
> + * non-SMP platforms (Orion, Dove, Kirkwood, Armada 370) and the SMP
> + * platforms (MV78200 from the Discovery family and the Armada
> + * XP). Therefore, this driver handles three variants of the GPIO
> + * block:
> + * - the basic variant, called "orion-gpio", with the simplest
> + *   register set. Used on Orion, Dove, Kirkwoord, Armada 370 and
> + *   non-SMP Discovery systems
> + * - the mv78200 variant for MV78200 Discovery systems. This variant
> + *   turns the edge mask and level mask registers into CPU0 edge
> + *   mask/level mask registers, and adds CPU1 edge mask/level mask
> + *   registers.
> + * - the armadaxp variant for Armada XP systems. This variant keeps
> + *   the normal cause/edge mask/level mask registers when the global
> + *   interrupts are used, but adds per-CPU cause/edge mask/level mask
> + *   registers n a separate memory area for the per-CPU GPIO
> + *   interrupts.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/slab.h>
> +#include <linux/irqdomain.h>
> +#include <linux/io.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pinctrl/consumer.h>
> +
> +/*
> + * GPIO unit register offsets.
> + */
> +#define GPIO_OUT_OFF		0x0000
> +#define GPIO_IO_CONF_OFF	0x0004
> +#define GPIO_BLINK_EN_OFF	0x0008
> +#define GPIO_IN_POL_OFF		0x000c
> +#define GPIO_DATA_IN_OFF	0x0010
> +#define GPIO_EDGE_CAUSE_OFF	0x0014
> +#define GPIO_EDGE_MASK_OFF	0x0018
> +#define GPIO_LEVEL_MASK_OFF	0x001c
> +
> +/* The MV78200 has per-CPU registers for edge mask and level mask */
> +#define GPIO_EDGE_MASK_MV78200_OFF(cpu)   ((cpu) ? 0x30 : 0x18)
> +#define GPIO_LEVEL_MASK_MV78200_OFF(cpu)  ((cpu) ? 0x34 : 0x1C)
> +
> +/* The Armada XP has per-CPU registers for interrupt cause, interrupt
> + * mask and interrupt level mask. Those are relative to the
> + * percpu_membase. */
> +#define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4)
> +#define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu)  (0x10 + (cpu) * 0x4)
> +#define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4)
> +
> +#define MVEBU_GPIO_SOC_VARIANT_ORION    0x1
> +#define MVEBU_GPIO_SOC_VARIANT_MV78200  0x2
> +#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
> +
> +struct mvebu_gpio_chip {
> +	struct gpio_chip   chip;
> +	spinlock_t	   lock;
> +	void __iomem	  *membase;
> +	void __iomem	  *percpu_membase;
> +	unsigned int       irqbase;
> +	struct irq_domain *domain;
> +	int                soc_variant;
> +};
> +
> +/*
> + * Common functions to all SoCs
> + */
> +static void __iomem *GPIO_OUT(struct mvebu_gpio_chip *mvchip)
please use inline and do not use upper case for function

in the code pelease use relaxed readl/writel
> +{
> +	return mvchip->membase + GPIO_OUT_OFF;
> +}
> +
> +static void __iomem *GPIO_IO_CONF(struct mvebu_gpio_chip *mvchip)
> +{
> +	return mvchip->membase + GPIO_IO_CONF_OFF;
> +}
> +
> +static void __iomem *GPIO_IN_POL(struct mvebu_gpio_chip *mvchip)
> +{
> +	return mvchip->membase + GPIO_IN_POL_OFF;
> +}
> +
> +static void __iomem *GPIO_DATA_IN(struct mvebu_gpio_chip *mvchip)
> +{
> +	return mvchip->membase + GPIO_DATA_IN_OFF;
> +}
> +
> +static void __iomem *GPIO_EDGE_CAUSE(struct mvebu_gpio_chip *mvchip)
> +{
> +	int cpu;
> +
> +	switch(mvchip->soc_variant) {
> +	case MVEBU_GPIO_SOC_VARIANT_ORION:
> +	case MVEBU_GPIO_SOC_VARIANT_MV78200:
> +		return mvchip->membase + GPIO_EDGE_CAUSE_OFF;
> +	case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
> +		cpu = smp_processor_id();
> +		return mvchip->percpu_membase + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
> +	default:
> +		BUG();
> +	}
> +}
> +
> +
> +static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
> +{
> +	struct mvebu_gpio_chip *mvchip =
> +		container_of(chip, struct mvebu_gpio_chip, chip);
> +	unsigned long flags;
> +	int ret;
> +	u32 u;
> +
> +	/* Check with the pinctrl driver whether this pin is usable as
> +	 * an input GPIO */
> +	ret = pinctrl_gpio_direction_input(chip->base + pin);
this will de the configuration
> +	if (ret)
> +		return ret;
> +
> +	spin_lock_irqsave(&mvchip->lock, flags);
> +	u = readl(GPIO_IO_CONF(mvchip));
> +	u |= 1 << pin;
> +	writel(u, GPIO_IO_CONF(mvchip));
why this?
> +	spin_unlock_irqrestore(&mvchip->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin,
> +				       int value)
> +{
> +	struct mvebu_gpio_chip *mvchip =
> +		container_of(chip, struct mvebu_gpio_chip, chip);
> +	unsigned long flags;
> +	int ret;
> +	u32 u;
> +
> +	/* Check with the pinctrl driver whether this pin is usable as
> +	 * an output GPIO */
> +	ret = pinctrl_gpio_direction_output(chip->base + pin);
> +	if (ret)
> +		return ret;
> +
> +	spin_lock_irqsave(&mvchip->lock, flags);
> +	u = readl(GPIO_IO_CONF(mvchip));
> +	u &= ~(1 << pin);
> +	writel(u, GPIO_IO_CONF(mvchip));
> +	spin_unlock_irqrestore(&mvchip->lock, flags);
> +
> +	return 0;
> +}
> +

 +
> +	/* Check if we need to change chip and handler */
> +	if (!(ct->type & type))
> +		if (irq_setup_alt_chip(d, type))
> +			return -EINVAL;
> +
> +	/*
> +	 * Configure interrupt polarity.
> +	 */
> +	if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) {
> +		u = readl(GPIO_IN_POL(mvchip));
> +		u &= ~(1 << pin);
> +		writel(u, GPIO_IN_POL(mvchip));
> +	} else if (type == IRQ_TYPE_EDGE_FALLING || type == IRQ_TYPE_LEVEL_LOW) {
> +		u = readl(GPIO_IN_POL(mvchip));
> +		u |= 1 << pin;
> +		writel(u, GPIO_IN_POL(mvchip));
> +	} else if (type == IRQ_TYPE_EDGE_BOTH) {
> +		u32 v;
switch will make the code cleaner
> +
> +		v = readl(GPIO_IN_POL(mvchip)) ^ readl(GPIO_DATA_IN(mvchip));
> +
> +		/*
> +		 * set initial polarity based on current input level
> +		 */
> +		u = readl(GPIO_IN_POL(mvchip));
> +		if (v & (1 << pin))
> +			u |= 1 << pin;		/* falling */
> +		else
> +			u &= ~(1 << pin);	/* rising */
> +		writel(u, GPIO_IN_POL(mvchip));
> +	}
> +	return 0;
> +}
> +
> +static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
> +{
> +	struct mvebu_gpio_chip *mvchip = irq_get_handler_data(irq);
> +	u32 cause, type;
> +	int i;
> +
> +	if (mvchip == NULL)
> +		return;
> +
> +	cause = readl(GPIO_DATA_IN(mvchip)) & readl(GPIO_LEVEL_MASK(mvchip));
> +	cause |= readl(GPIO_EDGE_CAUSE(mvchip)) & readl(GPIO_EDGE_MASK(mvchip));
> +
> +	for (i = 0; i < mvchip->chip.ngpio; i++) {
> +		int irq;
> +
> +		irq = mvchip->irqbase + i;
> +
> +		if (!(cause & (1 << i)))
> +			continue;
> +
> +		type = irqd_get_trigger_type(irq_get_irq_data(irq));
> +		if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
> +			/* Swap polarity (race with GPIO line) */
> +			u32 polarity;
> +
> +			polarity = readl(GPIO_IN_POL(mvchip));
> +			polarity ^= 1 << i;
> +			writel(polarity, GPIO_IN_POL(mvchip));
> +		}
> +		generic_handle_irq(irq);
> +	}
> +}
> +
> +static struct platform_device_id mvebu_gpio_ids[] = {
> +	{
> +		.name = "orion-gpio",
> +	}, {
> +		.name = "mv78200-gpio",
> +	}, {
> +		.name = "armadaxp-gpio",
why here to0?
you already detect it via DT
> +	}, {
> +		/* sentinel */
> +	},
> +};
> +MODULE_DEVICE_TABLE(platform, mvebu_gpio_ids);
> +
> +static struct of_device_id mvebu_gpio_of_match[] __devinitdata = {
> +	{
> +		.compatible = "marvell,orion-gpio",
> +		.data       = (void*) MVEBU_GPIO_SOC_VARIANT_ORION,
> +	},
> +	{
> +		.compatible = "marvell,mv78200-gpio",
> +		.data       = (void*) MVEBU_GPIO_SOC_VARIANT_MV78200,
> +	},
> +	{
> +		.compatible = "marvell,armadaxp-gpio",
> +		.data       = (void*) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
> +	},
> +	{
> +		/* sentinel */
> +	},
> +};
> +MODULE_DEVICE_TABLE(of, mvebu_gpio_of_match);
> +
> +static unsigned int mvebu_gpio_chip_count;
> +
> +static int __devinit mvebu_gpio_probe(struct platform_device *pdev)
> +{
> +	struct mvebu_gpio_chip *mvchip;
> +	const struct of_device_id *match;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct resource *res;
> +	struct irq_chip_generic *gc;
> +	struct irq_chip_type *ct;
> +	unsigned int ngpios;
> +	int soc_variant;
> +	int i, cpu;
> +
> +	match = of_match_device(mvebu_gpio_of_match, &pdev->dev);
> +	if (match)
> +		soc_variant = (int) match->data;
> +	else
> +		soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (! res) {
> +		dev_err(&pdev->dev, "Cannot get memory resource\n");
> +		return -ENODEV;
> +	}
> +
> +	mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL);
> +	if (! mvchip){
> +		dev_err(&pdev->dev, "Cannot allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) {
> +		dev_err(&pdev->dev, "Missing ngpios OF property\n");
> +		return -ENODEV;
> +	}
> +
> +	mvchip->soc_variant = soc_variant;
> +	mvchip->chip.label = kasprintf(GFP_KERNEL, "mvebu-gpio%d", mvebu_gpio_chip_count);
> +	mvchip->chip.dev = &pdev->dev;
> +	mvchip->chip.request = mvebu_gpio_request;
> +	mvchip->chip.direction_input = mvebu_gpio_direction_input;
> +	mvchip->chip.get = mvebu_gpio_get;
> +	mvchip->chip.direction_output = mvebu_gpio_direction_output;
> +	mvchip->chip.set = mvebu_gpio_set;
> +	mvchip->chip.to_irq = mvebu_gpio_to_irq;
> +	mvchip->chip.base = mvebu_gpio_chip_count * 32;
why you don't use the auto allocated gpio number?
> +	mvchip->chip.ngpio = ngpios;
> +	mvchip->chip.can_sleep = 0;
> +#ifdef CONFIG_OF
> +	mvchip->chip.of_node = np;
> +#endif
> +
> +	spin_lock_init(&mvchip->lock);
> +	mvchip->membase = devm_request_and_ioremap(&pdev->dev, res);
> +	if (! mvchip->membase) {
> +		dev_err(&pdev->dev, "Cannot ioremap\n");
> +		kfree(mvchip->chip.label);
> +		return -ENOMEM;
> +	}
> +
> +	/* The Armada XP has a second range of registers for the
> +	 * per-CPU registers */
> +	if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +		if (! res) {
> +			dev_err(&pdev->dev, "Cannot get memory resource\n");
> +			kfree(mvchip->chip.label);
> +			return -ENODEV;
> +		}
> +
> +		mvchip->percpu_membase = devm_request_and_ioremap(&pdev->dev, res);
> +		if (! mvchip->percpu_membase) {
> +			dev_err(&pdev->dev, "Cannot ioremap\n");
> +			kfree(mvchip->chip.label);
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	/*
> +	 * Mask and clear GPIO interrupts.
> +	 */
> +	switch(soc_variant) {
> +	case MVEBU_GPIO_SOC_VARIANT_ORION:
> +		writel(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
> +		writel(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
> +		writel(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
> +		break;
> +	case MVEBU_GPIO_SOC_VARIANT_MV78200:
> +		writel(0, GPIO_EDGE_CAUSE(mvchip));
> +		for (cpu = 0; cpu < 2; cpu++) {
> +			writel(0, mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu));
> +			writel(0, mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu));
> +		}
> +		break;
> +	case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
> +		writel(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
> +		writel(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
> +		writel(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
> +		for (cpu = 0; cpu < 4; cpu++) {
> +			writel(0, mvchip->percpu_membase + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu));
> +			writel(0, mvchip->percpu_membase + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu));
> +			writel(0, mvchip->percpu_membase + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu));
> +		}
> +		break;
> +	default:
> +		BUG();
> +	}
> +
> +	gpiochip_add(&mvchip->chip);
> +
> +	/* Some gpio controllers do not provide irq support */
> +	if (!of_irq_count(np)) {
> +		mvebu_gpio_chip_count++;
> +		return 0;
> +	}
> +
> +	/* Setup the interrupt handlers. Each chip can have up to 4
> +	 * interrupt handlers, with each handler dealing with 8 GPIO
> +	 * pins. */
> +	for (i = 0; i < 4; i++) {
> +		int irq;
> +		irq = platform_get_irq(pdev, i);
> +		if (irq < 0)
> +			continue;
> +		irq_set_handler_data(irq, mvchip);
> +		irq_set_chained_handler(irq, mvebu_gpio_irq_handler);
> +	}
> +
> +	mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
> +	if (mvchip->irqbase < 0) {
> +		dev_err(&pdev->dev, "no irqs\n");
> +		kfree(mvchip->chip.label);
> +		return -ENOMEM;
> +	}
> +
> +	gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
> +				    mvchip->membase, handle_level_irq);
> +	if (! gc) {
> +		dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
> +		kfree(mvchip->chip.label);
> +		return -ENOMEM;
> +	}
> +
> +	gc->private = mvchip;
> +	ct = &gc->chip_types[0];
> +	ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
> +	ct->chip.irq_mask = mvebu_gpio_level_irq_mask;
> +	ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask;
> +	ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
> +	ct->chip.name = mvchip->chip.label;
> +
> +	ct = &gc->chip_types[1];
> +	ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
> +	ct->chip.irq_ack = mvebu_gpio_irq_ack;
> +	ct->chip.irq_mask = mvebu_gpio_edge_irq_mask;
> +	ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask;
> +	ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
> +	ct->handler = handle_edge_irq;
> +	ct->chip.name = mvchip->chip.label;
> +
> +	irq_setup_generic_chip(gc, IRQ_MSK(ngpios), IRQ_GC_INIT_MASK_CACHE,
> +			       IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
> +
> +	/* Setup irq domain on top of the generic chip. */
> +	mvchip->domain = irq_domain_add_legacy(np, mvchip->chip.ngpio,
> +					       mvchip->irqbase, 0,
> +					       &irq_domain_simple_ops,
> +					       mvchip);
> +	if (!mvchip->domain) {
> +		dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
> +			mvchip->chip.label);
> +		irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
> +					IRQ_LEVEL | IRQ_NOPROBE);
> +		kfree(gc);
> +		kfree(mvchip->chip.label);
> +		return -ENODEV;
> +	}
> +
> +	mvebu_gpio_chip_count++;
> +
> +	return 0;
> +}
> +
> +static struct platform_driver mvebu_gpio_driver = {
> +	.driver		= {
> +		.name	        = "mvebu-gpio",
> +		.owner	        = THIS_MODULE,
> +		.of_match_table = mvebu_gpio_of_match,
> +	},
> +	.probe		= mvebu_gpio_probe,
> +	.id_table	= mvebu_gpio_ids,
> +};
> +
> +static int __init mvebu_gpio_init(void)
> +{
> +	return platform_driver_register(&mvebu_gpio_driver);
> +}
> +postcore_initcall(mvebu_gpio_init);
> -- 
> 1.7.9.5
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Thomas Petazzoni Aug. 13, 2012, 5:34 p.m. UTC | #2
Hello,

Thanks Jean-Christophe for this review!

Le Mon, 13 Aug 2012 07:18:09 +0200,
Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> a écrit :

> > +/*
> > + * Common functions to all SoCs
> > + */
> > +static void __iomem *GPIO_OUT(struct mvebu_gpio_chip *mvchip)
> please use inline and do not use upper case for function

Yes agreed. Those upper-case functions come directly from the previous
driver in plat-orion/gpio.c, but I was also not really happy with them.
Will change it for the next version.

> in the code pelease use relaxed readl/writel

Ok.

> > +	/* Check with the pinctrl driver whether this pin is usable as
> > +	 * an input GPIO */
> > +	ret = pinctrl_gpio_direction_input(chip->base + pin);
> this will de the configuration

No, it will not do the configuration. On Marvell SoCs:

 * The MPP unit allows to configure the pin muxing only. This unit is
   managed by the pinctrl driver.

 * The GPIO unit allows to configure whether a GPIO is input/output,
   set the level, configure GPIO interrupts. This unit is managed by
   the GPIO driver.

Those two units have distinct register ranges, so we cannot easily
migrate the GPIO direction configuration to the pinctrl driver without
doing some ugly things.

So, in terms of GPIO direction, the pinctrl driver is only responsible
for telling the GPIO driver whether a particular pin *can* be
configured as input or output (because this is something we encode in
the pinctrl driver in the pins list). However, *configuring* the
direction must be done by the GPIO driver.

> > +	if (ret)
> > +		return ret;
> > +
> > +	spin_lock_irqsave(&mvchip->lock, flags);
> > +	u = readl(GPIO_IO_CONF(mvchip));
> > +	u |= 1 << pin;
> > +	writel(u, GPIO_IO_CONF(mvchip));
> why this?

See above, it configures the GPIO direction.

> > +	/*
> > +	 * Configure interrupt polarity.
> > +	 */
> > +	if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) {
> > +		u = readl(GPIO_IN_POL(mvchip));
> > +		u &= ~(1 << pin);
> > +		writel(u, GPIO_IN_POL(mvchip));
> > +	} else if (type == IRQ_TYPE_EDGE_FALLING || type == IRQ_TYPE_LEVEL_LOW) {
> > +		u = readl(GPIO_IN_POL(mvchip));
> > +		u |= 1 << pin;
> > +		writel(u, GPIO_IN_POL(mvchip));
> > +	} else if (type == IRQ_TYPE_EDGE_BOTH) {
> > +		u32 v;
> switch will make the code cleaner

Agreed, will change.

> > +static struct platform_device_id mvebu_gpio_ids[] = {
> > +	{
> > +		.name = "orion-gpio",
> > +	}, {
> > +		.name = "mv78200-gpio",
> > +	}, {
> > +		.name = "armadaxp-gpio",
> why here to0?

to0 ?

> you already detect it via DT

Hmm, not sure what you mean here. Can you expand a bit your comment?

> > +	mvchip->chip.to_irq = mvebu_gpio_to_irq;
> > +	mvchip->chip.base = mvebu_gpio_chip_count * 32;
> why you don't use the auto allocated gpio number?

Ah, didn't know there was one. Will look into this!

Thanks!

Thomas