[PATCH 1/5] gpio: introduce gpio-mvebu driver for Marvell SoCs

Jean-Christophe PLAGNIOL-VILLARD plagnioj at jcrosoft.com
Mon Aug 13 01:18:09 EDT 2012


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 at free-electrons.com>
> Cc: Grant Likely <grant.likely at secretlab.ca>
> Cc: Linus Walleij <linus.walleij at stericsson.com>
> Cc: Andrew Lunn <andrew at lunn.ch>
> Cc: Jason Cooper <jason at lakedaemon.net>
> Cc: Gregory Clement <gregory.clement at free-electrons.com>
> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth at 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 at free-electrons.com>
> + * Andrew Lunn <andrew at lunn.ch>
> + * Sebastian Hesselbarth <sebastian.hesselbarth at 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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list