diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 18 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpio/devres.c | 14 | ||||
-rw-r--r-- | drivers/gpio/gpio-bt8xx.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-clps711x.c | 236 | ||||
-rw-r--r-- | drivers/gpio/gpio-grgpio.c | 6 | ||||
-rw-r--r-- | drivers/gpio/gpio-ich.c | 8 | ||||
-rw-r--r-- | drivers/gpio/gpio-langwell.c | 170 | ||||
-rw-r--r-- | drivers/gpio/gpio-lynxpoint.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-ml-ioh.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-msm-v1.c | 15 | ||||
-rw-r--r-- | drivers/gpio/gpio-msm-v2.c | 195 | ||||
-rw-r--r-- | drivers/gpio/gpio-mvebu.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-omap.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-rcar.c | 100 | ||||
-rw-r--r-- | drivers/gpio/gpio-rdc321x.c | 7 | ||||
-rw-r--r-- | drivers/gpio/gpio-samsung.c | 67 | ||||
-rw-r--r-- | drivers/gpio/gpio-sta2x11.c | 6 | ||||
-rw-r--r-- | drivers/gpio/gpio-stmpe.c | 7 | ||||
-rw-r--r-- | drivers/gpio/gpio-sx150x.c | 18 | ||||
-rw-r--r-- | drivers/gpio/gpio-tc3589x.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-timberdale.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-vx855.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-xilinx.c | 144 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 8 |
25 files changed, 477 insertions, 557 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 573c449c49b..b2450ba1413 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -109,8 +109,11 @@ config GPIO_MAX730X comment "Memory mapped GPIO drivers:" config GPIO_CLPS711X - def_bool y + tristate "CLPS711X GPIO support" depends on ARCH_CLPS711X + select GPIO_GENERIC + help + Say yes here to support GPIO on CLPS711X SoCs. config GPIO_GENERIC_PLATFORM tristate "Generic memory-mapped GPIO controller support (MMIO platform device)" @@ -165,7 +168,7 @@ config GPIO_MSM_V1 config GPIO_MSM_V2 tristate "Qualcomm MSM GPIO v2" - depends on GPIOLIB && ARCH_MSM + depends on GPIOLIB && OF && ARCH_MSM help Say yes here to support the GPIO interface on ARM v7 based Qualcomm MSM chips. Most of the pins on the MSM can be @@ -209,6 +212,13 @@ config GPIO_RCAR help Say yes here to support GPIO on Renesas R-Car SoCs. +config GPIO_SAMSUNG + bool + depends on PLAT_SAMSUNG + help + Legacy GPIO support. Use only for platforms without support for + pinctrl. + config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" depends on PLAT_SPEAR @@ -234,7 +244,7 @@ config GPIO_TS5500 config GPIO_XILINX bool "Xilinx GPIO support" - depends on PPC_OF || MICROBLAZE + depends on PPC_OF || MICROBLAZE || ARCH_ZYNQ help Say yes here to support the Xilinx FPGA GPIO device @@ -330,7 +340,7 @@ config GPIO_MAX7300 depends on I2C select GPIO_MAX730X help - GPIO driver for Maxim MAX7301 I2C-based GPIO expander. + GPIO driver for Maxim MAX7300 I2C-based GPIO expander. config GPIO_MAX732X tristate "MAX7319, MAX7320-7327 I2C Port Expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 0cb2d656ad1..ef3e983a2f1 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -59,7 +59,7 @@ obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o -obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o +obj-$(CONFIG_GPIO_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 1077754f828..3e7812f0405 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -34,10 +34,10 @@ static int devm_gpio_match(struct device *dev, void *res, void *data) } /** - * devm_gpio_request - request a gpio for a managed device - * @dev: device to request the gpio for - * @gpio: gpio to allocate - * @label: the name of the requested gpio + * devm_gpio_request - request a GPIO for a managed device + * @dev: device to request the GPIO for + * @gpio: GPIO to allocate + * @label: the name of the requested GPIO * * Except for the extra @dev argument, this function takes the * same arguments and performs the same function as @@ -101,9 +101,9 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, EXPORT_SYMBOL(devm_gpio_request_one); /** - * devm_gpio_free - free an interrupt - * @dev: device to free gpio for - * @gpio: gpio to free + * devm_gpio_free - free a GPIO + * @dev: device to free GPIO for + * @gpio: GPIO to free * * Except for the extra @dev argument, this function takes the * same arguments and performs the same function as gpio_free(). diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 7d9d7cb35f2..8369e71ebe4 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -286,7 +286,7 @@ static int bt8xxgpio_resume(struct pci_dev *pdev) unsigned long flags; int err; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); err = pci_enable_device(pdev); if (err) return err; diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index ce63b75b13f..0edaf2ce926 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -1,7 +1,7 @@ /* * CLPS711X GPIO driver * - * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + * Copyright (C) 2012,2013 Alexander Shiyan <shc_work@mail.ru> * * 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 @@ -9,191 +9,91 @@ * (at your option) any later version. */ -#include <linux/io.h> -#include <linux/slab.h> +#include <linux/err.h> #include <linux/gpio.h> #include <linux/module.h> -#include <linux/spinlock.h> +#include <linux/basic_mmio_gpio.h> #include <linux/platform_device.h> -#include <mach/hardware.h> - -#define CLPS711X_GPIO_PORTS 5 -#define CLPS711X_GPIO_NAME "gpio-clps711x" - -struct clps711x_gpio { - struct gpio_chip chip[CLPS711X_GPIO_PORTS]; - spinlock_t lock; -}; - -static void __iomem *clps711x_ports[] = { - CLPS711X_VIRT_BASE + PADR, - CLPS711X_VIRT_BASE + PBDR, - CLPS711X_VIRT_BASE + PCDR, - CLPS711X_VIRT_BASE + PDDR, - CLPS711X_VIRT_BASE + PEDR, -}; - -static void __iomem *clps711x_pdirs[] = { - CLPS711X_VIRT_BASE + PADDR, - CLPS711X_VIRT_BASE + PBDDR, - CLPS711X_VIRT_BASE + PCDDR, - CLPS711X_VIRT_BASE + PDDDR, - CLPS711X_VIRT_BASE + PEDDR, -}; - -#define clps711x_port(x) clps711x_ports[x->base / 8] -#define clps711x_pdir(x) clps711x_pdirs[x->base / 8] - -static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset) +static int clps711x_gpio_probe(struct platform_device *pdev) { - return !!(readb(clps711x_port(chip)) & (1 << offset)); -} + struct device_node *np = pdev->dev.of_node; + void __iomem *dat, *dir; + struct bgpio_chip *bgc; + struct resource *res; + int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id; -static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset, - int value) -{ - int tmp; - unsigned long flags; - struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); - - spin_lock_irqsave(&gpio->lock, flags); - tmp = readb(clps711x_port(chip)) & ~(1 << offset); - if (value) - tmp |= 1 << offset; - writeb(tmp, clps711x_port(chip)); - spin_unlock_irqrestore(&gpio->lock, flags); -} - -static int gpio_clps711x_dir_in(struct gpio_chip *chip, unsigned offset) -{ - int tmp; - unsigned long flags; - struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + if ((id < 0) || (id > 4)) + return -ENODEV; - spin_lock_irqsave(&gpio->lock, flags); - tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); - writeb(tmp, clps711x_pdir(chip)); - spin_unlock_irqrestore(&gpio->lock, flags); + bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL); + if (!bgc) + return -ENOMEM; - return 0; -} + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dat = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dat)) + return PTR_ERR(dat); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dir = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + switch (id) { + case 3: + /* PORTD is inverted logic for direction register */ + err = bgpio_init(bgc, &pdev->dev, 1, dat, NULL, NULL, + NULL, dir, 0); + break; + default: + err = bgpio_init(bgc, &pdev->dev, 1, dat, NULL, NULL, + dir, NULL, 0); + break; + } -static int gpio_clps711x_dir_out(struct gpio_chip *chip, unsigned offset, - int value) -{ - int tmp; - unsigned long flags; - struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); - - spin_lock_irqsave(&gpio->lock, flags); - tmp = readb(clps711x_pdir(chip)) | (1 << offset); - writeb(tmp, clps711x_pdir(chip)); - tmp = readb(clps711x_port(chip)) & ~(1 << offset); - if (value) - tmp |= 1 << offset; - writeb(tmp, clps711x_port(chip)); - spin_unlock_irqrestore(&gpio->lock, flags); - - return 0; -} + if (err) + return err; -static int gpio_clps711x_dir_in_inv(struct gpio_chip *chip, unsigned offset) -{ - int tmp; - unsigned long flags; - struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + switch (id) { + case 4: + /* PORTE is 3 lines only */ + bgc->gc.ngpio = 3; + break; + default: + break; + } - spin_lock_irqsave(&gpio->lock, flags); - tmp = readb(clps711x_pdir(chip)) | (1 << offset); - writeb(tmp, clps711x_pdir(chip)); - spin_unlock_irqrestore(&gpio->lock, flags); + bgc->gc.base = id * 8; + platform_set_drvdata(pdev, bgc); - return 0; + return gpiochip_add(&bgc->gc); } -static int gpio_clps711x_dir_out_inv(struct gpio_chip *chip, unsigned offset, - int value) +static int clps711x_gpio_remove(struct platform_device *pdev) { - int tmp; - unsigned long flags; - struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); - - spin_lock_irqsave(&gpio->lock, flags); - tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); - writeb(tmp, clps711x_pdir(chip)); - tmp = readb(clps711x_port(chip)) & ~(1 << offset); - if (value) - tmp |= 1 << offset; - writeb(tmp, clps711x_port(chip)); - spin_unlock_irqrestore(&gpio->lock, flags); - - return 0; + struct bgpio_chip *bgc = platform_get_drvdata(pdev); + + return bgpio_remove(bgc); } -static struct { - char *name; - int nr; - int inv_dir; -} clps711x_gpio_ports[] __initconst = { - { "PORTA", 8, 0, }, - { "PORTB", 8, 0, }, - { "PORTC", 8, 0, }, - { "PORTD", 8, 1, }, - { "PORTE", 3, 0, }, +static const struct of_device_id clps711x_gpio_ids[] = { + { .compatible = "cirrus,clps711x-gpio" }, + { } }; +MODULE_DEVICE_TABLE(of, clps711x_gpio_ids); + +static struct platform_driver clps711x_gpio_driver = { + .driver = { + .name = "clps711x-gpio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(clps711x_gpio_ids), + }, + .probe = clps711x_gpio_probe, + .remove = clps711x_gpio_remove, +}; +module_platform_driver(clps711x_gpio_driver); -static int __init gpio_clps711x_init(void) -{ - int i; - struct platform_device *pdev; - struct clps711x_gpio *gpio; - - pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0); - if (!pdev) { - pr_err("Cannot create platform device: %s\n", - CLPS711X_GPIO_NAME); - return -ENOMEM; - } - - platform_device_add(pdev); - - gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio), - GFP_KERNEL); - if (!gpio) { - dev_err(&pdev->dev, "GPIO allocating memory error\n"); - platform_device_unregister(pdev); - return -ENOMEM; - } - - platform_set_drvdata(pdev, gpio); - - spin_lock_init(&gpio->lock); - - for (i = 0; i < CLPS711X_GPIO_PORTS; i++) { - gpio->chip[i].owner = THIS_MODULE; - gpio->chip[i].dev = &pdev->dev; - gpio->chip[i].label = clps711x_gpio_ports[i].name; - gpio->chip[i].base = i * 8; - gpio->chip[i].ngpio = clps711x_gpio_ports[i].nr; - gpio->chip[i].get = gpio_clps711x_get; - gpio->chip[i].set = gpio_clps711x_set; - if (!clps711x_gpio_ports[i].inv_dir) { - gpio->chip[i].direction_input = gpio_clps711x_dir_in; - gpio->chip[i].direction_output = gpio_clps711x_dir_out; - } else { - gpio->chip[i].direction_input = gpio_clps711x_dir_in_inv; - gpio->chip[i].direction_output = gpio_clps711x_dir_out_inv; - } - WARN_ON(gpiochip_add(&gpio->chip[i])); - } - - dev_info(&pdev->dev, "GPIO driver initialized\n"); - - return 0; -} -arch_initcall(gpio_clps711x_init); - -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); MODULE_DESCRIPTION("CLPS711X GPIO driver"); diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 8e08b864765..84d2478ec29 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -235,8 +235,8 @@ static irqreturn_t grgpio_irq_handler(int irq, void *dev) * This function will be called as a consequence of the call to * irq_create_mapping in grgpio_to_irq */ -int grgpio_irq_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) +static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct grgpio_priv *priv = d->host_data; struct grgpio_lirq *lirq; @@ -291,7 +291,7 @@ int grgpio_irq_map(struct irq_domain *d, unsigned int irq, return ret; } -void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) +static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) { struct grgpio_priv *priv = d->host_data; int index; diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index e16d932fd44..2729e3d2d5b 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -41,12 +41,14 @@ enum GPIO_REG { GPIO_USE_SEL = 0, GPIO_IO_SEL, GPIO_LVL, + GPO_BLINK }; -static const u8 ichx_regs[3][3] = { +static const u8 ichx_regs[4][3] = { {0x00, 0x30, 0x40}, /* USE_SEL[1-3] offsets */ {0x04, 0x34, 0x44}, /* IO_SEL[1-3] offsets */ {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */ + {0x18, 0x18, 0x18}, /* BLINK offset */ }; static const u8 ichx_reglen[3] = { @@ -148,6 +150,10 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, int val) { + /* Disable blink hardware which is available for GPIOs from 0 to 31. */ + if (nr < 32) + ichx_write_bit(GPO_BLINK, nr, 0, 0); + /* Set GPIO output value. */ ichx_write_bit(GPIO_LVL, nr, val, 0); diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 62ef10a641c..bfa1af1b519 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -1,7 +1,7 @@ /* * Moorestown platform Langwell chip GPIO driver * - * Copyright (c) 2008 - 2009, Intel Corporation. + * Copyright (c) 2008, 2009, 2013, Intel Corporation. * * 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 @@ -20,7 +20,6 @@ /* Supports: * Moorestown platform Langwell chip. * Medfield platform Penwell chip. - * Whitney point. */ #include <linux/module.h> @@ -65,7 +64,7 @@ enum GPIO_REG { struct lnw_gpio { struct gpio_chip chip; - void *reg_base; + void __iomem *reg_base; spinlock_t lock; struct pci_dev *pdev; struct irq_domain *domain; @@ -74,15 +73,13 @@ struct lnw_gpio { #define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip) static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, - enum GPIO_REG reg_type) + enum GPIO_REG reg_type) { struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 32; - void __iomem *ptr; - ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); - return ptr; + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; } static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, @@ -91,10 +88,8 @@ static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 16; - void __iomem *ptr; - ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); - return ptr; + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; } static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) @@ -305,11 +300,7 @@ static const struct irq_domain_ops lnw_gpio_irq_ops = { static int lnw_gpio_runtime_idle(struct device *dev) { - int err = pm_schedule_suspend(dev, 500); - - if (!err) - return 0; - + pm_schedule_suspend(dev, 500); return -EBUSY; } @@ -318,56 +309,40 @@ static const struct dev_pm_ops lnw_gpio_pm_ops = { }; static int lnw_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) + const struct pci_device_id *id) { - void *base; - resource_size_t start, len; + void __iomem *base; struct lnw_gpio *lnw; u32 gpio_base; u32 irq_base; int retval; int ngpio = id->driver_data; - retval = pci_enable_device(pdev); + retval = pcim_enable_device(pdev); if (retval) return retval; - retval = pci_request_regions(pdev, "langwell_gpio"); + retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev)); if (retval) { - dev_err(&pdev->dev, "error requesting resources\n"); - goto err_pci_req_region; - } - /* get the gpio_base from bar1 */ - start = pci_resource_start(pdev, 1); - len = pci_resource_len(pdev, 1); - base = ioremap_nocache(start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar1\n"); - retval = -EFAULT; - goto err_ioremap; + dev_err(&pdev->dev, "I/O memory mapping error\n"); + return retval; } - irq_base = *(u32 *)base; - gpio_base = *((u32 *)base + 1); + + base = pcim_iomap_table(pdev)[1]; + + irq_base = readl(base); + gpio_base = readl(sizeof(u32) + base); + /* release the IO mapping, since we already get the info from bar1 */ - iounmap(base); - /* get the register base from bar0 */ - start = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - base = devm_ioremap_nocache(&pdev->dev, start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar0\n"); - retval = -EFAULT; - goto err_ioremap; - } + pcim_iounmap_regions(pdev, 1 << 1); lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); if (!lnw) { - dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); - retval = -ENOMEM; - goto err_ioremap; + dev_err(&pdev->dev, "can't allocate chip data\n"); + return -ENOMEM; } - lnw->reg_base = base; + lnw->reg_base = pcim_iomap_table(pdev)[0]; lnw->chip.label = dev_name(&pdev->dev); lnw->chip.request = lnw_gpio_request; lnw->chip.direction_input = lnw_gpio_direction_input; @@ -380,18 +355,18 @@ static int lnw_gpio_probe(struct pci_dev *pdev, lnw->chip.can_sleep = 0; lnw->pdev = pdev; + spin_lock_init(&lnw->lock); + lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ngpio, irq_base, &lnw_gpio_irq_ops, lnw); - if (!lnw->domain) { - retval = -ENOMEM; - goto err_ioremap; - } + if (!lnw->domain) + return -ENOMEM; pci_set_drvdata(pdev, lnw); retval = gpiochip_add(&lnw->chip); if (retval) { - dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); - goto err_ioremap; + dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); + return retval; } lnw_irq_init_hw(lnw); @@ -399,18 +374,10 @@ static int lnw_gpio_probe(struct pci_dev *pdev, irq_set_handler_data(pdev->irq, lnw); irq_set_chained_handler(pdev->irq, lnw_irq_handler); - spin_lock_init(&lnw->lock); - pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); return 0; - -err_ioremap: - pci_release_regions(pdev); -err_pci_req_region: - pci_disable_device(pdev); - return retval; } static struct pci_driver lnw_gpio_driver = { @@ -422,88 +389,9 @@ static struct pci_driver lnw_gpio_driver = { }, }; - -static int wp_gpio_probe(struct platform_device *pdev) -{ - struct lnw_gpio *lnw; - struct gpio_chip *gc; - struct resource *rc; - int retval = 0; - - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!rc) - return -EINVAL; - - lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); - if (!lnw) { - dev_err(&pdev->dev, - "can't allocate whitneypoint_gpio chip data\n"); - return -ENOMEM; - } - lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc)); - if (lnw->reg_base == NULL) { - retval = -EINVAL; - goto err_kmalloc; - } - spin_lock_init(&lnw->lock); - gc = &lnw->chip; - gc->label = dev_name(&pdev->dev); - gc->owner = THIS_MODULE; - gc->direction_input = lnw_gpio_direction_input; - gc->direction_output = lnw_gpio_direction_output; - gc->get = lnw_gpio_get; - gc->set = lnw_gpio_set; - gc->to_irq = NULL; - gc->base = 0; - gc->ngpio = 64; - gc->can_sleep = 0; - retval = gpiochip_add(gc); - if (retval) { - dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", - retval); - goto err_ioremap; - } - platform_set_drvdata(pdev, lnw); - return 0; -err_ioremap: - iounmap(lnw->reg_base); -err_kmalloc: - kfree(lnw); - return retval; -} - -static int wp_gpio_remove(struct platform_device *pdev) -{ - struct lnw_gpio *lnw = platform_get_drvdata(pdev); - int err; - err = gpiochip_remove(&lnw->chip); - if (err) - dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); - iounmap(lnw->reg_base); - kfree(lnw); - platform_set_drvdata(pdev, NULL); - return 0; -} - -static struct platform_driver wp_gpio_driver = { - .probe = wp_gpio_probe, - .remove = wp_gpio_remove, - .driver = { - .name = "wp_gpio", - .owner = THIS_MODULE, - }, -}; - static int __init lnw_gpio_init(void) { - int ret; - ret = pci_register_driver(&lnw_gpio_driver); - if (ret < 0) - return ret; - ret = platform_driver_register(&wp_gpio_driver); - if (ret < 0) - pci_unregister_driver(&lnw_gpio_driver); - return ret; + return pci_register_driver(&lnw_gpio_driver); } device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 86c17de8769..761c4705dfb 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -447,7 +447,6 @@ static int lp_gpio_remove(struct platform_device *pdev) err = gpiochip_remove(&lg->chip); if (err) dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 0966f2637ad..6da6d7667c6 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -465,6 +465,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, dev_warn(&pdev->dev, "ml_ioh_gpio: Failed to get IRQ base num\n"); chip->irq_base = -1; + ret = irq_base; goto err_irq_alloc_descs; } chip->irq_base = irq_base; diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c index c798585a3fe..73b73969d36 100644 --- a/drivers/gpio/gpio-msm-v1.c +++ b/drivers/gpio/gpio-msm-v1.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/err.h> #include <mach/msm_gpiomux.h> @@ -630,7 +631,7 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_type = msm_gpio_irq_set_type, }; -static int __devinit gpio_msm_v1_probe(struct platform_device *pdev) +static int gpio_msm_v1_probe(struct platform_device *pdev) { int i, j = 0; const struct platform_device_id *dev_id = platform_get_device_id(pdev); @@ -652,14 +653,14 @@ static int __devinit gpio_msm_v1_probe(struct platform_device *pdev) return irq2; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base1 = devm_request_and_ioremap(&pdev->dev, res); - if (!base1) - return -EADDRNOTAVAIL; + base1 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base1)) + return PTR_ERR(base1); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - base2 = devm_request_and_ioremap(&pdev->dev, res); - if (!base2) - return -EADDRNOTAVAIL; + base2 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base2)) + return PTR_ERR(base2); for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) { if (i - FIRST_GPIO_IRQ >= diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c index dd2eddeb1e0..c2fa77086eb 100644 --- a/drivers/gpio/gpio-msm-v2.c +++ b/drivers/gpio/gpio-msm-v2.c @@ -19,18 +19,21 @@ #include <linux/bitmap.h> #include <linux/bitops.h> +#include <linux/err.h> #include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irqchip/chained_irq.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/module.h> +#include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/spinlock.h> +#include <linux/slab.h> -#include <mach/msm_gpiomux.h> -#include <mach/msm_iomap.h> +#define MAX_NR_GPIO 300 /* Bits of interest in the GPIO_IN_OUT register. */ @@ -77,13 +80,6 @@ enum { TARGET_PROC_NONE = 7, }; - -#define GPIO_INTR_CFG_SU(gpio) (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio))) -#define GPIO_CONFIG(gpio) (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio))) -#define GPIO_IN_OUT(gpio) (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio))) -#define GPIO_INTR_CFG(gpio) (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio))) -#define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio))) - /** * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure * @@ -102,11 +98,27 @@ enum { */ struct msm_gpio_dev { struct gpio_chip gpio_chip; - DECLARE_BITMAP(enabled_irqs, NR_GPIO_IRQS); - DECLARE_BITMAP(wake_irqs, NR_GPIO_IRQS); - DECLARE_BITMAP(dual_edge_irqs, NR_GPIO_IRQS); + DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); + DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO); + DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); + struct irq_domain *domain; + unsigned int summary_irq; + void __iomem *msm_tlmm_base; }; +struct msm_gpio_dev msm_gpio; + +#define GPIO_INTR_CFG_SU(gpio) (msm_gpio.msm_tlmm_base + 0x0400 + \ + (0x04 * (gpio))) +#define GPIO_CONFIG(gpio) (msm_gpio.msm_tlmm_base + 0x1000 + \ + (0x10 * (gpio))) +#define GPIO_IN_OUT(gpio) (msm_gpio.msm_tlmm_base + 0x1004 + \ + (0x10 * (gpio))) +#define GPIO_INTR_CFG(gpio) (msm_gpio.msm_tlmm_base + 0x1008 + \ + (0x10 * (gpio))) +#define GPIO_INTR_STATUS(gpio) (msm_gpio.msm_tlmm_base + 0x100c + \ + (0x10 * (gpio))) + static DEFINE_SPINLOCK(tlmm_lock); static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip) @@ -159,37 +171,29 @@ static int msm_gpio_direction_output(struct gpio_chip *chip, static int msm_gpio_request(struct gpio_chip *chip, unsigned offset) { - return msm_gpiomux_get(chip->base + offset); + return 0; } static void msm_gpio_free(struct gpio_chip *chip, unsigned offset) { - msm_gpiomux_put(chip->base + offset); + return; } static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - return MSM_GPIO_TO_INT(chip->base + offset); + struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip); + struct irq_domain *domain = g_dev->domain; + + return irq_create_mapping(domain, offset); } static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq) { - return irq - MSM_GPIO_TO_INT(chip->base); + struct irq_data *irq_data = irq_get_irq_data(irq); + + return irq_data->hwirq; } -static struct msm_gpio_dev msm_gpio = { - .gpio_chip = { - .base = 0, - .ngpio = NR_GPIO_IRQS, - .direction_input = msm_gpio_direction_input, - .direction_output = msm_gpio_direction_output, - .get = msm_gpio_get, - .set = msm_gpio_set, - .to_irq = msm_gpio_to_irq, - .request = msm_gpio_request, - .free = msm_gpio_free, - }, -}; /* For dual-edge interrupts in software, since the hardware has no * such support: @@ -227,9 +231,9 @@ static void msm_gpio_update_dual_edge_pos(unsigned gpio) if (intstat || val == val2) return; } while (loop_limit-- > 0); - pr_err("dual-edge irq failed to stabilize, " + pr_err("%s: dual-edge irq failed to stabilize, " "interrupts dropped. %#08x != %#08x\n", - val, val2); + __func__, val, val2); } static void msm_gpio_irq_ack(struct irq_data *d) @@ -316,10 +320,10 @@ static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc) chained_irq_enter(chip, desc); - for_each_set_bit(i, msm_gpio.enabled_irqs, NR_GPIO_IRQS) { + for_each_set_bit(i, msm_gpio.enabled_irqs, MAX_NR_GPIO) { if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS)) - generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip, - i)); + generic_handle_irq(irq_find_mapping(msm_gpio.domain, + i)); } chained_irq_exit(chip, desc); @@ -330,13 +334,13 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq); if (on) { - if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS)) - irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 1); + if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO)) + irq_set_irq_wake(msm_gpio.summary_irq, 1); set_bit(gpio, msm_gpio.wake_irqs); } else { clear_bit(gpio, msm_gpio.wake_irqs); - if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS)) - irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 0); + if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO)) + irq_set_irq_wake(msm_gpio.summary_irq, 0); } return 0; @@ -351,30 +355,86 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; -static int msm_gpio_probe(struct platform_device *dev) +static struct lock_class_key msm_gpio_lock_class; + +static int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { - int i, irq, ret; + irq_set_lockdep_class(irq, &msm_gpio_lock_class); + irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, + handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops msm_gpio_irq_domain_ops = { + .xlate = irq_domain_xlate_twocell, + .map = msm_gpio_irq_domain_map, +}; + +static int msm_gpio_probe(struct platform_device *pdev) +{ + int ret, ngpio; + struct resource *res; + + if (of_property_read_u32(pdev->dev.of_node, "ngpio", &ngpio)) { + dev_err(&pdev->dev, "%s: ngpio property missing\n", __func__); + return -EINVAL; + } + + if (ngpio > MAX_NR_GPIO) + WARN(1, "ngpio exceeds the MAX_NR_GPIO. Increase MAX_NR_GPIO\n"); + + bitmap_zero(msm_gpio.enabled_irqs, MAX_NR_GPIO); + bitmap_zero(msm_gpio.wake_irqs, MAX_NR_GPIO); + bitmap_zero(msm_gpio.dual_edge_irqs, MAX_NR_GPIO); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + msm_gpio.msm_tlmm_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(msm_gpio.msm_tlmm_base)) + return PTR_ERR(msm_gpio.msm_tlmm_base); + + msm_gpio.gpio_chip.ngpio = ngpio; + msm_gpio.gpio_chip.label = pdev->name; + msm_gpio.gpio_chip.dev = &pdev->dev; + msm_gpio.gpio_chip.base = 0; + msm_gpio.gpio_chip.direction_input = msm_gpio_direction_input; + msm_gpio.gpio_chip.direction_output = msm_gpio_direction_output; + msm_gpio.gpio_chip.get = msm_gpio_get; + msm_gpio.gpio_chip.set = msm_gpio_set; + msm_gpio.gpio_chip.to_irq = msm_gpio_to_irq; + msm_gpio.gpio_chip.request = msm_gpio_request; + msm_gpio.gpio_chip.free = msm_gpio_free; - bitmap_zero(msm_gpio.enabled_irqs, NR_GPIO_IRQS); - bitmap_zero(msm_gpio.wake_irqs, NR_GPIO_IRQS); - bitmap_zero(msm_gpio.dual_edge_irqs, NR_GPIO_IRQS); - msm_gpio.gpio_chip.label = dev->name; ret = gpiochip_add(&msm_gpio.gpio_chip); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "gpiochip_add failed with error %d\n", ret); return ret; + } - for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) { - irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i); - irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); + msm_gpio.summary_irq = platform_get_irq(pdev, 0); + if (msm_gpio.summary_irq < 0) { + dev_err(&pdev->dev, "No Summary irq defined for msmgpio\n"); + return msm_gpio.summary_irq; } - irq_set_chained_handler(TLMM_SCSS_SUMMARY_IRQ, - msm_summary_irq_handler); + msm_gpio.domain = irq_domain_add_linear(pdev->dev.of_node, ngpio, + &msm_gpio_irq_domain_ops, + &msm_gpio); + if (!msm_gpio.domain) + return -ENODEV; + + irq_set_chained_handler(msm_gpio.summary_irq, msm_summary_irq_handler); + return 0; } +static struct of_device_id msm_gpio_of_match[] = { + { .compatible = "qcom,msm-gpio", }, + { }, +}; + static int msm_gpio_remove(struct platform_device *dev) { int ret = gpiochip_remove(&msm_gpio.gpio_chip); @@ -382,7 +442,7 @@ static int msm_gpio_remove(struct platform_device *dev) if (ret < 0) return ret; - irq_set_handler(TLMM_SCSS_SUMMARY_IRQ, NULL); + irq_set_handler(msm_gpio.summary_irq, NULL); return 0; } @@ -393,36 +453,11 @@ static struct platform_driver msm_gpio_driver = { .driver = { .name = "msmgpio", .owner = THIS_MODULE, + .of_match_table = msm_gpio_of_match, }, }; -static struct platform_device msm_device_gpio = { - .name = "msmgpio", - .id = -1, -}; - -static int __init msm_gpio_init(void) -{ - int rc; - - rc = platform_driver_register(&msm_gpio_driver); - if (!rc) { - rc = platform_device_register(&msm_device_gpio); - if (rc) - platform_driver_unregister(&msm_gpio_driver); - } - - return rc; -} - -static void __exit msm_gpio_exit(void) -{ - platform_device_unregister(&msm_device_gpio); - platform_driver_unregister(&msm_gpio_driver); -} - -postcore_initcall(msm_gpio_init); -module_exit(msm_gpio_exit); +module_platform_driver(msm_gpio_driver) MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs"); diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 3a4816adc13..80ad35e2a8c 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -457,7 +457,7 @@ static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) if (!(cause & (1 << i))) continue; - type = irqd_get_trigger_type(irq_get_irq_data(irq)); + type = irq_get_trigger_type(irq); if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { /* Swap polarity (race with GPIO line) */ u32 polarity; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 4a430360af5..dfeb3a3a8f2 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1482,7 +1482,7 @@ static void omap_gpio_restore_context(struct gpio_bank *bank) #else #define omap_gpio_runtime_suspend NULL #define omap_gpio_runtime_resume NULL -static void omap_gpio_init_context(struct gpio_bank *p) {} +static inline void omap_gpio_init_context(struct gpio_bank *p) {} #endif static const struct dev_pm_ops gpio_pm_ops = { diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index b4ca450947b..e8198dd6861 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -49,6 +49,9 @@ struct gpio_rcar_priv { #define POSNEG 0x20 #define EDGLEVEL 0x24 #define FILONOFF 0x28 +#define BOTHEDGE 0x4c + +#define RCAR_MAX_GPIO_PER_BANK 32 static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs) { @@ -91,7 +94,8 @@ static void gpio_rcar_irq_enable(struct irq_data *d) static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, unsigned int hwirq, bool active_high_rising_edge, - bool level_trigger) + bool level_trigger, + bool both) { unsigned long flags; @@ -108,6 +112,10 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, /* Configure edge or level trigger in EDGLEVEL */ gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger); + /* Select one edge or both edges in BOTHEDGE */ + if (p->config.has_both_edge_trigger) + gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both); + /* Select "Interrupt Input Mode" in IOINTSEL */ gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true); @@ -127,16 +135,26 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_LEVEL_HIGH: - gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true); + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true, + false); break; case IRQ_TYPE_LEVEL_LOW: - gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true); + gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true, + false); break; case IRQ_TYPE_EDGE_RISING: - gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false); + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, + false); break; case IRQ_TYPE_EDGE_FALLING: - gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false); + gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false, + false); + break; + case IRQ_TYPE_EDGE_BOTH: + if (!p->config.has_both_edge_trigger) + return -EINVAL; + gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, + true); break; default: return -EINVAL; @@ -214,7 +232,14 @@ static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) { - return (int)(gpio_rcar_read(gpio_to_priv(chip), INDT) & BIT(offset)); + u32 bit = BIT(offset); + + /* testing on r8a7790 shows that INDT does not show correct pin state + * when configured as output, so use OUTDT in case of output pins */ + if (gpio_rcar_read(gpio_to_priv(chip), INOUTSEL) & bit) + return (int)(gpio_rcar_read(gpio_to_priv(chip), OUTDT) & bit); + else + return (int)(gpio_rcar_read(gpio_to_priv(chip), INDT) & bit); } static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) @@ -258,9 +283,35 @@ static struct irq_domain_ops gpio_rcar_irq_domain_ops = { .map = gpio_rcar_irq_domain_map, }; +static void gpio_rcar_parse_pdata(struct gpio_rcar_priv *p) +{ + struct gpio_rcar_config *pdata = p->pdev->dev.platform_data; + struct device_node *np = p->pdev->dev.of_node; + struct of_phandle_args args; + int ret; + + if (pdata) { + p->config = *pdata; + } else if (IS_ENABLED(CONFIG_OF) && np) { + ret = of_parse_phandle_with_args(np, "gpio-ranges", + "#gpio-range-cells", 0, &args); + p->config.number_of_pins = ret == 0 && args.args_count == 3 + ? args.args[2] + : RCAR_MAX_GPIO_PER_BANK; + p->config.gpio_base = -1; + } + + if (p->config.number_of_pins == 0 || + p->config.number_of_pins > RCAR_MAX_GPIO_PER_BANK) { + dev_warn(&p->pdev->dev, + "Invalid number of gpio lines %u, using %u\n", + p->config.number_of_pins, RCAR_MAX_GPIO_PER_BANK); + p->config.number_of_pins = RCAR_MAX_GPIO_PER_BANK; + } +} + static int gpio_rcar_probe(struct platform_device *pdev) { - struct gpio_rcar_config *pdata = pdev->dev.platform_data; struct gpio_rcar_priv *p; struct resource *io, *irq; struct gpio_chip *gpio_chip; @@ -275,14 +326,14 @@ static int gpio_rcar_probe(struct platform_device *pdev) goto err0; } - /* deal with driver instance configuration */ - if (pdata) - p->config = *pdata; - p->pdev = pdev; - platform_set_drvdata(pdev, p); spin_lock_init(&p->lock); + /* Get device configuration from DT node or platform data. */ + gpio_rcar_parse_pdata(p); + + platform_set_drvdata(pdev, p); + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -309,6 +360,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) gpio_chip->set = gpio_rcar_set; gpio_chip->to_irq = gpio_rcar_to_irq; gpio_chip->label = name; + gpio_chip->dev = &pdev->dev; gpio_chip->owner = THIS_MODULE; gpio_chip->base = p->config.gpio_base; gpio_chip->ngpio = p->config.number_of_pins; @@ -333,7 +385,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) } if (devm_request_irq(&pdev->dev, irq->start, - gpio_rcar_irq_handler, 0, name, p)) { + gpio_rcar_irq_handler, IRQF_SHARED, name, p)) { dev_err(&pdev->dev, "failed to request IRQ\n"); ret = -ENOENT; goto err1; @@ -355,10 +407,12 @@ static int gpio_rcar_probe(struct platform_device *pdev) p->config.irq_base, ret); } - ret = gpiochip_add_pin_range(gpio_chip, p->config.pctl_name, 0, - gpio_chip->base, gpio_chip->ngpio); - if (ret < 0) - dev_warn(&pdev->dev, "failed to add pin range\n"); + if (p->config.pctl_name) { + ret = gpiochip_add_pin_range(gpio_chip, p->config.pctl_name, 0, + gpio_chip->base, gpio_chip->ngpio); + if (ret < 0) + dev_warn(&pdev->dev, "failed to add pin range\n"); + } return 0; @@ -381,11 +435,23 @@ static int gpio_rcar_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id gpio_rcar_of_table[] = { + { + .compatible = "renesas,gpio-rcar", + }, + { }, +}; + +MODULE_DEVICE_TABLE(of, gpio_rcar_of_table); +#endif + static struct platform_driver gpio_rcar_device_driver = { .probe = gpio_rcar_probe, .remove = gpio_rcar_remove, .driver = { .name = "gpio_rcar", + .of_match_table = of_match_ptr(gpio_rcar_of_table), } }; diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index 1bf55f67f7a..368c3c00fca 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -187,20 +187,18 @@ static int rdc321x_gpio_probe(struct platform_device *pdev) rdc321x_gpio_dev->reg1_data_base, &rdc321x_gpio_dev->data_reg[0]); if (err) - goto out_drvdata; + goto out_free; err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, rdc321x_gpio_dev->reg2_data_base, &rdc321x_gpio_dev->data_reg[1]); if (err) - goto out_drvdata; + goto out_free; dev_info(&pdev->dev, "registering %d GPIOs\n", rdc321x_gpio_dev->chip.ngpio); return gpiochip_add(&rdc321x_gpio_dev->chip); -out_drvdata: - platform_set_drvdata(pdev, NULL); out_free: kfree(rdc321x_gpio_dev); return err; @@ -216,7 +214,6 @@ static int rdc321x_gpio_remove(struct platform_device *pdev) dev_err(&pdev->dev, "failed to unregister chip\n"); kfree(rdc321x_gpio_dev); - platform_set_drvdata(pdev, NULL); return ret; } diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index b22ca793374..a1392f47bbd 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -933,67 +933,6 @@ static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) s3c_gpiolib_track(chip); } -#if defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) -static int s3c24xx_gpio_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, u32 *flags) -{ - unsigned int pin; - - if (WARN_ON(gc->of_gpio_n_cells < 3)) - return -EINVAL; - - if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) - return -EINVAL; - - if (gpiospec->args[0] > gc->ngpio) - return -EINVAL; - - pin = gc->base + gpiospec->args[0]; - - if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1]))) - pr_warn("gpio_xlate: failed to set pin function\n"); - if (s3c_gpio_setpull(pin, gpiospec->args[2] & 0xffff)) - pr_warn("gpio_xlate: failed to set pin pull up/down\n"); - - if (flags) - *flags = gpiospec->args[2] >> 16; - - return gpiospec->args[0]; -} - -static const struct of_device_id s3c24xx_gpio_dt_match[] __initdata = { - { .compatible = "samsung,s3c24xx-gpio", }, - {} -}; - -static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, - u64 base, u64 offset) -{ - struct gpio_chip *gc = &chip->chip; - u64 address; - - if (!of_have_populated_dt()) - return; - - address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset; - gc->of_node = of_find_matching_node_by_address(NULL, - s3c24xx_gpio_dt_match, address); - if (!gc->of_node) { - pr_info("gpio: device tree node not found for gpio controller" - " with base address %08llx\n", address); - return; - } - gc->of_gpio_n_cells = 3; - gc->of_xlate = s3c24xx_gpio_xlate; -} -#else -static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, - u64 base, u64 offset) -{ - return; -} -#endif /* defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) */ - static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, int nr_chips, void __iomem *base) { @@ -1018,8 +957,6 @@ static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, gc->direction_output = samsung_gpiolib_2bit_output; samsung_gpiolib_add(chip); - - s3c24xx_gpiolib_attach_ofnode(chip, S3C24XX_PA_GPIO, i * 0x10); } } @@ -3026,6 +2963,10 @@ static __init int samsung_gpiolib_init(void) */ struct device_node *pctrl_np; static const struct of_device_id exynos_pinctrl_ids[] = { + { .compatible = "samsung,s3c2412-pinctrl", }, + { .compatible = "samsung,s3c2416-pinctrl", }, + { .compatible = "samsung,s3c2440-pinctrl", }, + { .compatible = "samsung,s3c2450-pinctrl", }, { .compatible = "samsung,exynos4210-pinctrl", }, { .compatible = "samsung,exynos4x12-pinctrl", }, { .compatible = "samsung,exynos5250-pinctrl", }, diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c index 558542552aa..f43ab6aea28 100644 --- a/drivers/gpio/gpio-sta2x11.c +++ b/drivers/gpio/gpio-sta2x11.c @@ -371,8 +371,12 @@ static int gsta_probe(struct platform_device *dev) res = platform_get_resource(dev, IORESOURCE_MEM, 0); chip = devm_kzalloc(&dev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; chip->dev = &dev->dev; - chip->reg_base = devm_request_and_ioremap(&dev->dev, res); + chip->reg_base = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(chip->reg_base)) + return PTR_ERR(chip->reg_base); for (i = 0; i < GSTA_NR_BLOCKS; i++) { chip->regs[i] = chip->reg_base + i * 4096; diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 3ce5bc38ac3..b33bad1bb4d 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -271,8 +271,8 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) return IRQ_HANDLED; } -int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hwirq) +static int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) { struct stmpe_gpio *stmpe_gpio = d->host_data; @@ -292,7 +292,7 @@ int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, return 0; } -void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) +static void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) { #ifdef CONFIG_ARM set_irq_flags(virq, 0); @@ -431,7 +431,6 @@ static int stmpe_gpio_remove(struct platform_device *pdev) if (irq >= 0) free_irq(irq, stmpe_gpio); - platform_set_drvdata(pdev, NULL); kfree(stmpe_gpio); return 0; diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index 796b6c42fa7..f371732591d 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -548,7 +548,8 @@ static int sx150x_install_irq_chip(struct sx150x_chip *chip, #endif } - err = request_threaded_irq(irq_summary, + err = devm_request_threaded_irq(&chip->client->dev, + irq_summary, NULL, sx150x_irq_thread_fn, IRQF_SHARED | IRQF_TRIGGER_FALLING, @@ -567,8 +568,6 @@ static void sx150x_remove_irq_chip(struct sx150x_chip *chip) unsigned n; unsigned irq; - free_irq(chip->irq_summary, chip); - for (n = 0; n < chip->dev_cfg->ngpios; ++n) { irq = chip->irq_base + n; irq_set_chip_and_handler(irq, NULL, NULL); @@ -591,18 +590,19 @@ static int sx150x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, i2c_funcs)) return -ENOSYS; - chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL); + chip = devm_kzalloc(&client->dev, + sizeof(struct sx150x_chip), GFP_KERNEL); if (!chip) return -ENOMEM; sx150x_init_chip(chip, client, id->driver_data, pdata); rc = sx150x_init_hw(chip, pdata); if (rc < 0) - goto probe_fail_pre_gpiochip_add; + return rc; rc = gpiochip_add(&chip->gpio_chip); - if (rc < 0) - goto probe_fail_pre_gpiochip_add; + if (rc) + return rc; if (pdata->irq_summary >= 0) { rc = sx150x_install_irq_chip(chip, @@ -617,8 +617,6 @@ static int sx150x_probe(struct i2c_client *client, return 0; probe_fail_post_gpiochip_add: WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); -probe_fail_pre_gpiochip_add: - kfree(chip); return rc; } @@ -635,8 +633,6 @@ static int sx150x_remove(struct i2c_client *client) if (chip->irq_summary >= 0) sx150x_remove_irq_chip(chip); - kfree(chip); - return 0; } diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index d34d80dfb08..4a5de273c23 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -407,7 +407,6 @@ static int tc3589x_gpio_remove(struct platform_device *pdev) free_irq(irq, tc3589x_gpio); - platform_set_drvdata(pdev, NULL); kfree(tc3589x_gpio); return 0; diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index 43774058b69..4c65f888320 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -342,8 +342,6 @@ static int timbgpio_remove(struct platform_device *pdev) release_mem_region(iomem->start, resource_size(iomem)); kfree(tgpio); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 2b7252cb242..cddfa22edb4 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -279,7 +279,6 @@ out_release: release_region(res_gpi->start, resource_size(res_gpi)); if (vg->gpo_reserved) release_region(res_gpi->start, resource_size(res_gpo)); - platform_set_drvdata(pdev, NULL); kfree(vg); return ret; } @@ -301,7 +300,6 @@ static int vx855gpio_remove(struct platform_device *pdev) release_region(res->start, resource_size(res)); } - platform_set_drvdata(pdev, NULL); kfree(vg); return 0; } diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 9ae7aa8ca48..792a05ad464 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -1,7 +1,7 @@ /* - * Xilinx gpio driver + * Xilinx gpio driver for xps/axi_gpio IP. * - * Copyright 2008 Xilinx, Inc. + * 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 @@ -12,6 +12,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/bitops.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/module.h> @@ -26,11 +27,31 @@ #define XGPIO_DATA_OFFSET (0x0) /* Data register */ #define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ +#define XGPIO_CHANNEL_OFFSET 0x8 + +/* Read/Write access to the GPIO registers */ +#ifdef CONFIG_ARCH_ZYNQ +# 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 + * 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 + */ struct xgpio_instance { struct of_mm_gpio_chip mmchip; - u32 gpio_state; /* GPIO state shadow register */ - u32 gpio_dir; /* GPIO direction shadow register */ - spinlock_t gpio_lock; /* Lock used for synchronization */ + u32 gpio_state; + u32 gpio_dir; + u32 offset; + spinlock_t gpio_lock; }; /** @@ -44,8 +65,12 @@ struct xgpio_instance { 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 = + container_of(mm_gc, struct xgpio_instance, mmchip); - return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1; + void __iomem *regs = mm_gc->regs + chip->offset; + + return !!(xgpio_readreg(regs + XGPIO_DATA_OFFSET) & BIT(gpio)); } /** @@ -63,15 +88,18 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) 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); + void __iomem *regs = mm_gc->regs; spin_lock_irqsave(&chip->gpio_lock, flags); /* Write to GPIO signal and set its direction to output */ if (val) - chip->gpio_state |= 1 << gpio; + chip->gpio_state |= BIT(gpio); else - chip->gpio_state &= ~(1 << gpio); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + chip->gpio_state &= ~BIT(gpio); + + xgpio_writereg(regs + chip->offset + XGPIO_DATA_OFFSET, + chip->gpio_state); spin_unlock_irqrestore(&chip->gpio_lock, flags); } @@ -91,12 +119,13 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) 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); + void __iomem *regs = mm_gc->regs; spin_lock_irqsave(&chip->gpio_lock, flags); /* Set the GPIO bit in shadow register and set direction as input */ - chip->gpio_dir |= (1 << gpio); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + chip->gpio_dir |= BIT(gpio); + xgpio_writereg(regs + chip->offset + XGPIO_TRI_OFFSET, chip->gpio_dir); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -119,19 +148,21 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) 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); + void __iomem *regs = mm_gc->regs; spin_lock_irqsave(&chip->gpio_lock, flags); /* Write state of GPIO signal */ if (val) - chip->gpio_state |= 1 << gpio; + chip->gpio_state |= BIT(gpio); else - chip->gpio_state &= ~(1 << gpio); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + chip->gpio_state &= ~BIT(gpio); + xgpio_writereg(regs + chip->offset + XGPIO_DATA_OFFSET, + chip->gpio_state); /* Clear the GPIO bit in shadow register and set direction as output */ - chip->gpio_dir &= (~(1 << gpio)); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + chip->gpio_dir &= ~BIT(gpio); + xgpio_writereg(regs + chip->offset + XGPIO_TRI_OFFSET, chip->gpio_dir); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -147,8 +178,10 @@ static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) struct xgpio_instance *chip = container_of(mm_gc, struct xgpio_instance, mmchip); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + xgpio_writereg(mm_gc->regs + chip->offset + XGPIO_DATA_OFFSET, + chip->gpio_state); + xgpio_writereg(mm_gc->regs + chip->offset + XGPIO_TRI_OFFSET, + chip->gpio_dir); } /** @@ -170,24 +203,20 @@ static int xgpio_of_probe(struct device_node *np) return -ENOMEM; /* Update GPIO state shadow register with default value */ - tree_info = of_get_property(np, "xlnx,dout-default", NULL); - if (tree_info) - chip->gpio_state = be32_to_cpup(tree_info); + of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state); + + /* By default, all pins are inputs */ + chip->gpio_dir = 0xFFFFFFFF; /* Update GPIO direction shadow register with default value */ - chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ - tree_info = of_get_property(np, "xlnx,tri-default", NULL); - if (tree_info) - chip->gpio_dir = be32_to_cpup(tree_info); + of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir); + + /* By default assume full GPIO controller */ + chip->mmchip.gc.ngpio = 32; /* Check device node and parent device node for device width */ - chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */ - tree_info = of_get_property(np, "xlnx,gpio-width", NULL); - if (!tree_info) - tree_info = of_get_property(np->parent, - "xlnx,gpio-width", NULL); - if (tree_info) - chip->mmchip.gc.ngpio = be32_to_cpup(tree_info); + of_property_read_u32(np, "xlnx,gpio-width", + (u32 *)&chip->mmchip.gc.ngpio); spin_lock_init(&chip->gpio_lock); @@ -206,6 +235,57 @@ static int xgpio_of_probe(struct device_node *np) 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); + if (!chip) + return -ENOMEM; + + /* Add dual channel offset */ + chip->offset = XGPIO_CHANNEL_OFFSET; + + /* Update GPIO state shadow register with default value */ + of_property_read_u32(np, "xlnx,dout-default-2", + &chip->gpio_state); + + /* By default, all pins are inputs */ + chip->gpio_dir = 0xFFFFFFFF; + + /* Update GPIO direction shadow register with default value */ + of_property_read_u32(np, "xlnx,tri-default-2", &chip->gpio_dir); + + /* By default assume full GPIO controller */ + chip->mmchip.gc.ngpio = 32; + + /* Check device node and parent device node for device width */ + of_property_read_u32(np, "xlnx,gpio2-width", + (u32 *)&chip->mmchip.gc.ngpio); + + spin_lock_init(&chip->gpio_lock); + + 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.save_regs = xgpio_save_regs; + + /* 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); + return status; + } + pr_info("XGpio: %s: dual channel registered, base is %d\n", + np->full_name, chip->mmchip.gc.base); + } + return 0; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c2534d62911..ff0fd655729 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1214,15 +1214,14 @@ int gpiochip_add(struct gpio_chip *chip) } } + spin_unlock_irqrestore(&gpio_lock, flags); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&chip->pin_ranges); #endif of_gpiochip_add(chip); -unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - if (status) goto fail; @@ -1235,6 +1234,9 @@ unlock: chip->label ? : "generic"); return 0; + +unlock: + spin_unlock_irqrestore(&gpio_lock, flags); fail: /* failures here can mean systems won't boot... */ pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n", |