From a4811622fea15fdc78102069f573061fc87f3570 Mon Sep 17 00:00:00 2001 From: Qipeng Zha Date: Fri, 10 Apr 2015 06:25:10 +0800 Subject: gpiolib: change gpio pin from unsigned to signed in acpi callback The signed error will be wrongly used as valid gpio offset Reported-by: David Binderman Signed-off-by: Alan Cox Signed-off-by: Qipeng Zha Acked-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index d2303d50f561..725d16138b74 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -550,7 +550,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, length = min(agpio->pin_table_length, (u16)(pin_index + bits)); for (i = pin_index; i < length; ++i) { - unsigned pin = agpio->pin_table[i]; + int pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; struct gpio_desc *desc; bool found; -- cgit v1.2.3-55-g7522 From 0218d5a8b2a3f4bdbc99523a61406b8c5ab14d39 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 13 Apr 2015 15:56:00 +0800 Subject: gpio: bcm-kona: Implement get_direction callback Implement gpio_chip's get_direction() callback, that lets other drivers get particular GPIOs direction using gpiod_get_direction(). Signed-off-by: Axel Lin Reviewed-by: Ray Jui Signed-off-by: Linus Walleij --- drivers/gpio/gpio-bcm-kona.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index b164ce837b43..a6e79225886d 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -122,6 +122,16 @@ static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio, spin_unlock_irqrestore(&kona_gpio->lock, flags); } +static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio) +{ + struct bcm_kona_gpio *kona_gpio = to_kona_gpio(chip); + void __iomem *reg_base = kona_gpio->reg_base; + u32 val; + + val = readl(reg_base + GPIO_CONTROL(gpio)) & GPIO_GPCTR0_IOTR_MASK; + return val ? GPIOF_DIR_IN : GPIOF_DIR_OUT; +} + static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) { struct bcm_kona_gpio *kona_gpio; @@ -135,12 +145,8 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) reg_base = kona_gpio->reg_base; spin_lock_irqsave(&kona_gpio->lock, flags); - /* determine the GPIO pin direction */ - val = readl(reg_base + GPIO_CONTROL(gpio)); - val &= GPIO_GPCTR0_IOTR_MASK; - /* this function only applies to output pin */ - if (GPIO_GPCTR0_IOTR_CMD_INPUT == val) + if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN) goto out; reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); @@ -166,13 +172,12 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) reg_base = kona_gpio->reg_base; spin_lock_irqsave(&kona_gpio->lock, flags); - /* determine the GPIO pin direction */ - val = readl(reg_base + GPIO_CONTROL(gpio)); - val &= GPIO_GPCTR0_IOTR_MASK; + if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN) + reg_offset = GPIO_IN_STATUS(bank_id); + else + reg_offset = GPIO_OUT_STATUS(bank_id); /* read the GPIO bank status */ - reg_offset = (GPIO_GPCTR0_IOTR_CMD_INPUT == val) ? - GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id); val = readl(reg_base + reg_offset); spin_unlock_irqrestore(&kona_gpio->lock, flags); @@ -310,6 +315,7 @@ static struct gpio_chip template_chip = { .owner = THIS_MODULE, .request = bcm_kona_gpio_request, .free = bcm_kona_gpio_free, + .get_direction = bcm_kona_gpio_get_dir, .direction_input = bcm_kona_gpio_direction_input, .get = bcm_kona_gpio_get, .direction_output = bcm_kona_gpio_direction_output, -- cgit v1.2.3-55-g7522 From 483d821108791092798f5d230686868112927044 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 21 Apr 2015 17:42:09 +0200 Subject: gpio: sysfs: fix memory leaks and device hotplug Unregister GPIOs requested through sysfs at chip remove to avoid leaking the associated memory and sysfs entries. The stale sysfs entries prevented the gpio numbers from being exported when the gpio range was later reused (e.g. at device reconnect). This also fixes the related module-reference leak. Note that kernfs makes sure that any on-going sysfs operations finish before the class devices are unregistered and that further accesses fail. The chip exported flag is used to prevent gpiod exports during removal. This also makes it harder to trigger, but does not fix, the related race between gpiochip_remove and export_store, which is really a race with gpiod_request that needs to be addressed separately. Also note that this would prevent the crashes (e.g. NULL-dereferences) at reconnect that affects pre-3.18 kernels, as well as use-after-free on operations on open attribute files on pre-3.14 kernels (prior to kernfs). Fixes: d8f388d8dc8d ("gpio: sysfs interface") Cc: stable # v2.6.27: 01cca93a9491 Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 7722ed53bd65..af3bc7a8033b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -551,6 +551,7 @@ static struct class gpio_class = { */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { + struct gpio_chip *chip; unsigned long flags; int status; const char *ioname = NULL; @@ -568,8 +569,16 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) return -EINVAL; } + chip = desc->chip; + mutex_lock(&sysfs_lock); + /* check if chip is being removed */ + if (!chip || !chip->exported) { + status = -ENODEV; + goto fail_unlock; + } + spin_lock_irqsave(&gpio_lock, flags); if (!test_bit(FLAG_REQUESTED, &desc->flags) || test_bit(FLAG_EXPORT, &desc->flags)) { @@ -783,12 +792,15 @@ void gpiochip_unexport(struct gpio_chip *chip) { int status; struct device *dev; + struct gpio_desc *desc; + unsigned int i; mutex_lock(&sysfs_lock); dev = class_find_device(&gpio_class, NULL, chip, match_export); if (dev) { put_device(dev); device_unregister(dev); + /* prevent further gpiod exports */ chip->exported = false; status = 0; } else @@ -797,6 +809,13 @@ void gpiochip_unexport(struct gpio_chip *chip) if (status) chip_dbg(chip, "%s: status %d\n", __func__, status); + + /* unregister gpiod class devices owned by sysfs */ + for (i = 0; i < chip->ngpio; i++) { + desc = &chip->desc[i]; + if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) + gpiod_free(desc); + } } static int __init gpiolib_sysfs_init(void) -- cgit v1.2.3-55-g7522 From 67ddd32bfc9f5746e6c293154f8287278e6744ba Mon Sep 17 00:00:00 2001 From: Semen Protsenko Date: Tue, 21 Apr 2015 20:27:37 +0300 Subject: gpio: max732x: Propagate wake-up setting to parent irq controller Set .irq_set_wake callback to prevent possible issues on wake-up. This patch was inspired by this commit: b80eef95beb04760629822fa130aeed54cdfafca Signed-off-by: Semen Protsenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max732x.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 0fa4543c5e02..1885e5c3569e 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -429,6 +429,14 @@ static int max732x_irq_set_type(struct irq_data *d, unsigned int type) return 0; } +static int max732x_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct max732x_chip *chip = irq_data_get_irq_chip_data(data); + + irq_set_irq_wake(chip->client->irq, on); + return 0; +} + static struct irq_chip max732x_irq_chip = { .name = "max732x", .irq_mask = max732x_irq_mask, @@ -436,6 +444,7 @@ static struct irq_chip max732x_irq_chip = { .irq_bus_lock = max732x_irq_bus_lock, .irq_bus_sync_unlock = max732x_irq_bus_sync_unlock, .irq_set_type = max732x_irq_set_type, + .irq_set_wake = max732x_irq_set_wake, }; static uint8_t max732x_irq_pending(struct max732x_chip *chip) -- cgit v1.2.3-55-g7522 From 13a43fd9e90552c8258981d0a0eb82d73406c24d Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 2 May 2015 23:11:34 +0200 Subject: gpio: add lpc18xx gpio driver Driver for the GPIO block found on NXP LPC18xx/43xx devices. Signed-off-by: Joachim Eastwood Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-lpc18xx.c | 180 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 drivers/gpio/gpio-lpc18xx.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index caefe806db5e..c2211258231b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -230,6 +230,14 @@ config GPIO_LOONGSON help driver for GPIO functionality on Loongson-2F/3A/3B processors. +config GPIO_LPC18XX + bool "NXP LPC18XX/43XX GPIO support" + default y if ARCH_LPC18XX + depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST) + help + Select this option to enable GPIO driver for + NXP LPC18XX/43XX devices. + config GPIO_LYNXPOINT tristate "Intel Lynxpoint GPIO support" depends on ACPI && X86 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index f71bb971329c..7b7f5d2e8ad4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o +obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c new file mode 100644 index 000000000000..eb68603136b0 --- /dev/null +++ b/drivers/gpio/gpio-lpc18xx.c @@ -0,0 +1,180 @@ +/* + * GPIO driver for NXP LPC18xx/43xx. + * + * Copyright (C) 2015 Joachim Eastwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* LPC18xx GPIO register offsets */ +#define LPC18XX_REG_DIR(n) (0x2000 + n * sizeof(u32)) + +#define LPC18XX_MAX_PORTS 8 +#define LPC18XX_PINS_PER_PORT 32 + +struct lpc18xx_gpio_chip { + struct gpio_chip gpio; + void __iomem *base; + struct clk *clk; + spinlock_t lock; +}; + +static inline struct lpc18xx_gpio_chip *to_lpc18xx_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct lpc18xx_gpio_chip, gpio); +} + +static int lpc18xx_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(offset); +} + +static void lpc18xx_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(offset); +} + +static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip); + writeb(value ? 1 : 0, gc->base + offset); +} + +static int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip); + return !!readb(gc->base + offset); +} + +static int lpc18xx_gpio_direction(struct gpio_chip *chip, unsigned offset, + bool out) +{ + struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip); + unsigned long flags; + u32 port, pin, dir; + + port = offset / LPC18XX_PINS_PER_PORT; + pin = offset % LPC18XX_PINS_PER_PORT; + + spin_lock_irqsave(&gc->lock, flags); + dir = readl(gc->base + LPC18XX_REG_DIR(port)); + if (out) + dir |= BIT(pin); + else + dir &= ~BIT(pin); + writel(dir, gc->base + LPC18XX_REG_DIR(port)); + spin_unlock_irqrestore(&gc->lock, flags); + + return 0; +} + +static int lpc18xx_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return lpc18xx_gpio_direction(chip, offset, false); +} + +static int lpc18xx_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + lpc18xx_gpio_set(chip, offset, value); + return lpc18xx_gpio_direction(chip, offset, true); +} + +static struct gpio_chip lpc18xx_chip = { + .label = "lpc18xx/43xx-gpio", + .request = lpc18xx_gpio_request, + .free = lpc18xx_gpio_free, + .direction_input = lpc18xx_gpio_direction_input, + .direction_output = lpc18xx_gpio_direction_output, + .set = lpc18xx_gpio_set, + .get = lpc18xx_gpio_get, + .ngpio = LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT, + .owner = THIS_MODULE, +}; + +static int lpc18xx_gpio_probe(struct platform_device *pdev) +{ + struct lpc18xx_gpio_chip *gc; + struct resource *res; + int ret; + + gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->gpio = lpc18xx_chip; + platform_set_drvdata(pdev, gc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gc->base)) + return PTR_ERR(gc->base); + + gc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(gc->clk)) { + dev_err(&pdev->dev, "input clock not found\n"); + return PTR_ERR(gc->clk); + } + + ret = clk_prepare_enable(gc->clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable clock\n"); + return ret; + } + + spin_lock_init(&gc->lock); + + gc->gpio.dev = &pdev->dev; + + ret = gpiochip_add(&gc->gpio); + if (ret) { + dev_err(&pdev->dev, "failed to add gpio chip\n"); + clk_disable_unprepare(gc->clk); + return ret; + } + + return 0; +} + +static int lpc18xx_gpio_remove(struct platform_device *pdev) +{ + struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev); + + gpiochip_remove(&gc->gpio); + clk_disable_unprepare(gc->clk); + + return 0; +} + +static const struct of_device_id lpc18xx_gpio_match[] = { + { .compatible = "nxp,lpc1850-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc18xx_gpio_match); + +static struct platform_driver lpc18xx_gpio_driver = { + .probe = lpc18xx_gpio_probe, + .remove = lpc18xx_gpio_remove, + .driver = { + .name = "lpc18xx-gpio", + .of_match_table = lpc18xx_gpio_match, + }, +}; +module_platform_driver(lpc18xx_gpio_driver); + +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-55-g7522 From 68689dbf35e13fc20928ba3a0b959d28c6281e9e Mon Sep 17 00:00:00 2001 From: Semen Protsenko Date: Tue, 21 Apr 2015 16:19:04 +0300 Subject: gpio: max732x: Add IRQF_SHARED to irq flags It's possible that multiple MAX732X can be hooked up to the same interrupt line with the processor. So add IRQF_SHARED in requesting irq. Signed-off-by: Semen Protsenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max732x.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 1885e5c3569e..857907aecec3 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -516,12 +516,10 @@ static int max732x_irq_setup(struct max732x_chip *chip, chip->irq_features = has_irq; mutex_init(&chip->irq_lock); - ret = devm_request_threaded_irq(&client->dev, - client->irq, - NULL, - max732x_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), chip); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, max732x_irq_handler, IRQF_ONESHOT | + IRQF_TRIGGER_FALLING | IRQF_SHARED, + dev_name(&client->dev), chip); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", client->irq); -- cgit v1.2.3-55-g7522 From 606f13e9efa0892561e7c471242e4ad0bcaf6ecb Mon Sep 17 00:00:00 2001 From: Semen Protsenko Date: Wed, 22 Apr 2015 16:20:41 +0300 Subject: gpio: max732x: Fix irq-events handler MAX732X clears all pending interrupts on I2C read (when interrupts register is being read). Driver doesn't need to send any ACKs when interrupt was handled. So replace handle_edge_irq() with handle_simple_irq(). Using handle_edge_irq() (w/o .irq_ack callback set) may lead to NULL pointer dereference in some cases. E.g. this was observed on hibernation process: Unable to handle kernel NULL pointer dereference at virtual address 0 Backtrace: (handle_edge_irq) from (resend_irqs) (resend_irqs) from (tasklet_action) (tasklet_action) from (__do_softirq) (__do_softirq) from (run_ksoftirqd) (run_ksoftirqd) from (smpboot_thread_fn) (smpboot_thread_fn) from (kthread) (kthread) from (ret_from_fork) Signed-off-by: Semen Protsenko Reviewed-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max732x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 857907aecec3..aed4ca9338bc 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -528,7 +528,7 @@ static int max732x_irq_setup(struct max732x_chip *chip, ret = gpiochip_irqchip_add(&chip->gpio_chip, &max732x_irq_chip, irq_base, - handle_edge_irq, + handle_simple_irq, IRQ_TYPE_NONE); if (ret) { dev_err(&client->dev, -- cgit v1.2.3-55-g7522 From d2d05c65c40e067ca5898399069053f095c67d6f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 23 Apr 2015 16:54:17 -0700 Subject: gpio: omap: Fix regression for MPUIO interrupts At some point with all the GPIO clean-up we've broken the MPUIO interrupts. Those are just a little bit different from the GPIO interrupts, so we can fix it up just by setting different irqchip functions for it. And then we can just remove all old code trying to do the same. Cc: Aaro Koskinen Cc: Javier Martinez Canillas Cc: Kevin Hilman Cc: Nishanth Menon Signed-off-by: Tony Lindgren Reviewed-by: Grygorii Strashko Acked-by: Santosh Shilimkar Reviewed-by: Felipe Balbi Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 48 +++++++++--------------------------------------- 1 file changed, 9 insertions(+), 39 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index cd1d5bf48f36..b232397ad7ec 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1054,38 +1054,8 @@ static void omap_gpio_mod_init(struct gpio_bank *bank) dev_err(bank->dev, "Could not get gpio dbck\n"); } -static void -omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start, - unsigned int num) -{ - struct irq_chip_generic *gc; - struct irq_chip_type *ct; - - gc = irq_alloc_generic_chip("MPUIO", 1, irq_start, bank->base, - handle_simple_irq); - if (!gc) { - dev_err(bank->dev, "Memory alloc failed for gc\n"); - return; - } - - ct = gc->chip_types; - - /* NOTE: No ack required, reading IRQ status clears it. */ - ct->chip.irq_mask = irq_gc_mask_set_bit; - ct->chip.irq_unmask = irq_gc_mask_clr_bit; - ct->chip.irq_set_type = omap_gpio_irq_type; - - if (bank->regs->wkup_en) - ct->chip.irq_set_wake = omap_gpio_wake_enable; - - ct->regs.mask = OMAP_MPUIO_GPIO_INT / bank->stride; - irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, - IRQ_NOREQUEST | IRQ_NOPROBE, 0); -} - static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) { - int j; static int gpio; int irq_base = 0; int ret; @@ -1132,6 +1102,15 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) } #endif + /* MPUIO is a bit different, reading IRQ status clears it */ + if (bank->is_mpuio) { + irqc->irq_ack = dummy_irq_chip.irq_ack; + irqc->irq_mask = irq_gc_mask_set_bit; + irqc->irq_unmask = irq_gc_mask_clr_bit; + if (!bank->regs->wkup_en) + irqc->irq_set_wake = NULL; + } + ret = gpiochip_irqchip_add(&bank->chip, irqc, irq_base, omap_gpio_irq_handler, IRQ_TYPE_NONE); @@ -1145,15 +1124,6 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) gpiochip_set_chained_irqchip(&bank->chip, irqc, bank->irq, omap_gpio_irq_handler); - for (j = 0; j < bank->width; j++) { - int irq = irq_find_mapping(bank->chip.irqdomain, j); - if (bank->is_mpuio) { - omap_mpuio_alloc_gc(bank, irq, bank->width); - irq_set_chip_and_handler(irq, NULL, NULL); - set_irq_flags(irq, 0); - } - } - return 0; } -- cgit v1.2.3-55-g7522 From cac089f9026e9ddb3481daf08f0fc4e5949fa1af Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 23 Apr 2015 16:56:22 -0700 Subject: gpio: omap: Allow building as a loadable module We currently get all kinds of errors building the omap gpio driver as a module starting with: undefined reference to `omap2_gpio_resume_after_idle' undefined reference to `omap2_gpio_prepare_for_idle' ... Let's fix the issue by adding inline functions to the header. Note that we can now also remove the two unused functions for omap_set_gpio_debounce and omap_set_gpio_debounce_time. Then doing rmmod on the module produces further warnings because of missing exit related functions. Let's add those. And finally, we can make the Kconfig entry just a tristate option that's selected for omaps. Cc: Javier Martinez Canillas Cc: Kevin Hilman Cc: Nishanth Menon Signed-off-by: Tony Lindgren Reviewed-by: Grygorii Strashko Acked-by: Santosh Shilimkar Reviewed-by: Felipe Balbi Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-omap.c | 24 ++++++++++++++++++++++++ include/linux/platform_data/gpio-omap.h | 12 ++++++++++-- 3 files changed, 35 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c2211258231b..4c4dba075277 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -316,7 +316,7 @@ config GPIO_OCTEON family of SOCs. config GPIO_OMAP - bool "TI OMAP GPIO support" if COMPILE_TEST && !ARCH_OMAP2PLUS + tristate "TI OMAP GPIO support" if ARCH_OMAP2PLUS || COMPILE_TEST default y if ARCH_OMAP depends on ARM select GENERIC_IRQ_CHIP diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index cd1d5bf48f36..38c268fb7ba8 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1263,6 +1263,17 @@ static int omap_gpio_probe(struct platform_device *pdev) return 0; } +static int omap_gpio_remove(struct platform_device *pdev) +{ + struct gpio_bank *bank = platform_get_drvdata(pdev); + + list_del(&bank->node); + gpiochip_remove(&bank->chip); + pm_runtime_disable(bank->dev); + + return 0; +} + #ifdef CONFIG_ARCH_OMAP2PLUS #if defined(CONFIG_PM) @@ -1448,6 +1459,7 @@ static int omap_gpio_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ +#if IS_BUILTIN(CONFIG_GPIO_OMAP) void omap2_gpio_prepare_for_idle(int pwr_mode) { struct gpio_bank *bank; @@ -1473,6 +1485,7 @@ void omap2_gpio_resume_after_idle(void) pm_runtime_get_sync(bank->dev); } } +#endif #if defined(CONFIG_PM) static void omap_gpio_init_context(struct gpio_bank *p) @@ -1628,6 +1641,7 @@ MODULE_DEVICE_TABLE(of, omap_gpio_match); static struct platform_driver omap_gpio_driver = { .probe = omap_gpio_probe, + .remove = omap_gpio_remove, .driver = { .name = "omap_gpio", .pm = &gpio_pm_ops, @@ -1645,3 +1659,13 @@ static int __init omap_gpio_drv_reg(void) return platform_driver_register(&omap_gpio_driver); } postcore_initcall(omap_gpio_drv_reg); + +static void __exit omap_gpio_exit(void) +{ + platform_driver_unregister(&omap_gpio_driver); +} +module_exit(omap_gpio_exit); + +MODULE_DESCRIPTION("omap gpio driver"); +MODULE_ALIAS("platform:gpio-omap"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/gpio-omap.h b/include/linux/platform_data/gpio-omap.h index 5d50b25a73d7..cb2618147c34 100644 --- a/include/linux/platform_data/gpio-omap.h +++ b/include/linux/platform_data/gpio-omap.h @@ -208,9 +208,17 @@ struct omap_gpio_platform_data { int (*get_context_loss_count)(struct device *dev); }; +#if IS_BUILTIN(CONFIG_GPIO_OMAP) extern void omap2_gpio_prepare_for_idle(int off_mode); extern void omap2_gpio_resume_after_idle(void); -extern void omap_set_gpio_debounce(int gpio, int enable); -extern void omap_set_gpio_debounce_time(int gpio, int enable); +#else +static inline void omap2_gpio_prepare_for_idle(int off_mode) +{ +} + +static inline void omap2_gpio_resume_after_idle(void) +{ +} +#endif #endif -- cgit v1.2.3-55-g7522 From 0b354dc4335b230c05d8de3648404943553ca54f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2015 21:54:07 +0900 Subject: gpio: Constify irq_domain_ops The irq_domain_ops are not modified by the driver and the irqdomain core code accepts pointer to a const data. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-bcm-kona.c | 2 +- drivers/gpio/gpio-em.c | 2 +- drivers/gpio/gpio-grgpio.c | 2 +- drivers/gpio/gpio-mpc8xxx.c | 2 +- drivers/gpio/gpio-sa1100.c | 2 +- drivers/gpio/gpio-sodaville.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index b164ce837b43..cb89f37ffc02 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -534,7 +534,7 @@ static void bcm_kona_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) irq_set_chip_data(irq, NULL); } -static struct irq_domain_ops bcm_kona_irq_ops = { +static const struct irq_domain_ops bcm_kona_irq_ops = { .map = bcm_kona_gpio_irq_map, .unmap = bcm_kona_gpio_irq_unmap, .xlate = irq_domain_xlate_twocell, diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 3cfcfc620c8e..fbf287307c4c 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -266,7 +266,7 @@ static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int irq, return 0; } -static struct irq_domain_ops em_gio_irq_domain_ops = { +static const struct irq_domain_ops em_gio_irq_domain_ops = { .map = em_gio_irq_domain_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 35a02770c8b0..0a8f7617e72e 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -332,7 +332,7 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) spin_unlock_irqrestore(&priv->bgc.lock, flags); } -static struct irq_domain_ops grgpio_irq_domain_ops = { +static const struct irq_domain_ops grgpio_irq_domain_ops = { .map = grgpio_irq_map, .unmap = grgpio_irq_unmap, }; diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index a65b75161aa4..20aa66f34f6e 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -329,7 +329,7 @@ static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq, return 0; } -static struct irq_domain_ops mpc8xxx_gpio_irq_ops = { +static const struct irq_domain_ops mpc8xxx_gpio_irq_ops = { .map = mpc8xxx_gpio_irq_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index bec397a60204..3fa22dade243 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -160,7 +160,7 @@ static int sa1100_gpio_irqdomain_map(struct irq_domain *d, return 0; } -static struct irq_domain_ops sa1100_gpio_irqdomain_ops = { +static const struct irq_domain_ops sa1100_gpio_irqdomain_ops = { .map = sa1100_gpio_irqdomain_map, .xlate = irq_domain_xlate_onetwocell, }; diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index d8da36cd8123..65bc9f47a68e 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -125,7 +125,7 @@ static int sdv_xlate(struct irq_domain *h, struct device_node *node, return 0; } -static struct irq_domain_ops irq_domain_sdv_ops = { +static const struct irq_domain_ops irq_domain_sdv_ops = { .xlate = sdv_xlate, }; -- cgit v1.2.3-55-g7522 From ff71880006795290f371caae13e740491ec76956 Mon Sep 17 00:00:00 2001 From: Kamlakant Patel Date: Tue, 28 Apr 2015 20:10:45 +0530 Subject: gpio: xlp: GPIO controller for Netlogic XLP SoCs Add GPIO controller driver for Netlogic XLP MIPS64 SOCs. This driver is instantiated by device tree and supports interrupts for GPIOs. Signed-off-by: Kamlakant Patel Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 11 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-xlp.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 437 insertions(+) create mode 100644 drivers/gpio/gpio-xlp.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4c4dba075277..5faaf5ff0d4d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -496,6 +496,17 @@ config GPIO_XILINX help Say yes here to support the Xilinx FPGA GPIO device +config GPIO_XLP + tristate "Netlogic XLP GPIO support" + depends on CPU_XLP + select GPIOLIB_IRQCHIP + help + This driver provides support for GPIO interface on Netlogic XLP MIPS64 + SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX, + XLP9XX and XLP5XX. + + If unsure, say N. + config GPIO_XTENSA bool "Xtensa GPIO32 support" depends on XTENSA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 7b7f5d2e8ad4..1b55fdae6dd3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o +obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c new file mode 100644 index 000000000000..8532697768f6 --- /dev/null +++ b/drivers/gpio/gpio-xlp.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2003-2015 Broadcom Corporation + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +/* + * XLP GPIO has multiple 32 bit registers for each feature where each register + * controls 32 pins. So, pins up to 64 require 2 32-bit registers and up to 96 + * require 3 32-bit registers for each feature. + * Here we only define offset of the first register for each feature. Offset of + * the registers for pins greater than 32 can be calculated as following(Use + * GPIO_INT_STAT as example): + * + * offset = (gpio / XLP_GPIO_REGSZ) * 4; + * reg_addr = addr + offset; + * + * where addr is base address of the that feature register and gpio is the pin. + */ +#define GPIO_OUTPUT_EN 0x00 +#define GPIO_PADDRV 0x08 +#define GPIO_INT_EN00 0x18 +#define GPIO_INT_EN10 0x20 +#define GPIO_INT_EN20 0x28 +#define GPIO_INT_EN30 0x30 +#define GPIO_INT_POL 0x38 +#define GPIO_INT_TYPE 0x40 +#define GPIO_INT_STAT 0x48 + +#define GPIO_9XX_BYTESWAP 0X00 +#define GPIO_9XX_CTRL 0X04 +#define GPIO_9XX_OUTPUT_EN 0x14 +#define GPIO_9XX_PADDRV 0x24 +/* + * Only for 4 interrupt enable reg are defined for now, + * total reg available are 12. + */ +#define GPIO_9XX_INT_EN00 0x44 +#define GPIO_9XX_INT_EN10 0x54 +#define GPIO_9XX_INT_EN20 0x64 +#define GPIO_9XX_INT_EN30 0x74 +#define GPIO_9XX_INT_POL 0x104 +#define GPIO_9XX_INT_TYPE 0x114 +#define GPIO_9XX_INT_STAT 0x124 + +#define GPIO_3XX_INT_EN00 0x18 +#define GPIO_3XX_INT_EN10 0x20 +#define GPIO_3XX_INT_EN20 0x28 +#define GPIO_3XX_INT_EN30 0x30 +#define GPIO_3XX_INT_POL 0x78 +#define GPIO_3XX_INT_TYPE 0x80 +#define GPIO_3XX_INT_STAT 0x88 + +/* Interrupt type register mask */ +#define XLP_GPIO_IRQ_TYPE_LVL 0x0 +#define XLP_GPIO_IRQ_TYPE_EDGE 0x1 + +/* Interrupt polarity register mask */ +#define XLP_GPIO_IRQ_POL_HIGH 0x0 +#define XLP_GPIO_IRQ_POL_LOW 0x1 + +#define XLP_GPIO_REGSZ 32 +#define XLP_GPIO_IRQ_BASE 768 +#define XLP_MAX_NR_GPIO 96 + +/* XLP variants supported by this driver */ +enum { + XLP_GPIO_VARIANT_XLP832 = 1, + XLP_GPIO_VARIANT_XLP316, + XLP_GPIO_VARIANT_XLP208, + XLP_GPIO_VARIANT_XLP980, + XLP_GPIO_VARIANT_XLP532 +}; + +struct xlp_gpio_priv { + struct gpio_chip chip; + DECLARE_BITMAP(gpio_enabled_mask, XLP_MAX_NR_GPIO); + void __iomem *gpio_intr_en; /* pointer to first intr enable reg */ + void __iomem *gpio_intr_stat; /* pointer to first intr status reg */ + void __iomem *gpio_intr_type; /* pointer to first intr type reg */ + void __iomem *gpio_intr_pol; /* pointer to first intr polarity reg */ + void __iomem *gpio_out_en; /* pointer to first output enable reg */ + void __iomem *gpio_paddrv; /* pointer to first pad drive reg */ + spinlock_t lock; +}; + +static struct xlp_gpio_priv *gpio_chip_to_xlp_priv(struct gpio_chip *gc) +{ + return container_of(gc, struct xlp_gpio_priv, chip); +} + +static int xlp_gpio_get_reg(void __iomem *addr, unsigned gpio) +{ + u32 pos, regset; + + pos = gpio % XLP_GPIO_REGSZ; + regset = (gpio / XLP_GPIO_REGSZ) * 4; + return !!(readl(addr + regset) & BIT(pos)); +} + +static void xlp_gpio_set_reg(void __iomem *addr, unsigned gpio, int state) +{ + u32 value, pos, regset; + + pos = gpio % XLP_GPIO_REGSZ; + regset = (gpio / XLP_GPIO_REGSZ) * 4; + value = readl(addr + regset); + + if (state) + value |= BIT(pos); + else + value &= ~BIT(pos); + + writel(value, addr + regset); +} + +static void xlp_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x0); + __clear_bit(d->hwirq, priv->gpio_enabled_mask); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void xlp_gpio_irq_mask_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x0); + xlp_gpio_set_reg(priv->gpio_intr_stat, d->hwirq, 0x1); + __clear_bit(d->hwirq, priv->gpio_enabled_mask); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void xlp_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x1); + __set_bit(d->hwirq, priv->gpio_enabled_mask); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int xlp_gpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + int pol, irq_type; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + irq_type = XLP_GPIO_IRQ_TYPE_EDGE; + pol = XLP_GPIO_IRQ_POL_HIGH; + break; + case IRQ_TYPE_EDGE_FALLING: + irq_type = XLP_GPIO_IRQ_TYPE_EDGE; + pol = XLP_GPIO_IRQ_POL_LOW; + break; + case IRQ_TYPE_LEVEL_HIGH: + irq_type = XLP_GPIO_IRQ_TYPE_LVL; + pol = XLP_GPIO_IRQ_POL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + irq_type = XLP_GPIO_IRQ_TYPE_LVL; + pol = XLP_GPIO_IRQ_POL_LOW; + break; + default: + return -EINVAL; + } + + xlp_gpio_set_reg(priv->gpio_intr_type, d->hwirq, irq_type); + xlp_gpio_set_reg(priv->gpio_intr_pol, d->hwirq, pol); + + return 0; +} + +static struct irq_chip xlp_gpio_irq_chip = { + .name = "XLP-GPIO", + .irq_mask_ack = xlp_gpio_irq_mask_ack, + .irq_disable = xlp_gpio_irq_disable, + .irq_set_type = xlp_gpio_set_irq_type, + .irq_unmask = xlp_gpio_irq_unmask, + .flags = IRQCHIP_ONESHOT_SAFE, +}; + +static irqreturn_t xlp_gpio_generic_handler(int irq, void *data) +{ + struct xlp_gpio_priv *priv = data; + int gpio, regoff; + u32 gpio_stat; + + regoff = -1; + gpio_stat = 0; + for_each_set_bit(gpio, priv->gpio_enabled_mask, XLP_MAX_NR_GPIO) { + if (regoff != gpio / XLP_GPIO_REGSZ) { + regoff = gpio / XLP_GPIO_REGSZ; + gpio_stat = readl(priv->gpio_intr_stat + regoff * 4); + } + if (gpio_stat & BIT(gpio % XLP_GPIO_REGSZ)) + generic_handle_irq(irq_find_mapping( + priv->chip.irqdomain, gpio)); + } + + return IRQ_HANDLED; +} + +static int xlp_gpio_dir_output(struct gpio_chip *gc, unsigned gpio, int state) +{ + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + + BUG_ON(gpio >= gc->ngpio); + xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x1); + + return 0; +} + +static int xlp_gpio_dir_input(struct gpio_chip *gc, unsigned gpio) +{ + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + + BUG_ON(gpio >= gc->ngpio); + xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x0); + + return 0; +} + +static int xlp_gpio_get(struct gpio_chip *gc, unsigned gpio) +{ + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + + BUG_ON(gpio >= gc->ngpio); + return xlp_gpio_get_reg(priv->gpio_paddrv, gpio); +} + +static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state) +{ + struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc); + + BUG_ON(gpio >= gc->ngpio); + xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state); +} + +static const struct of_device_id xlp_gpio_of_ids[] = { + { + .compatible = "netlogic,xlp832-gpio", + .data = (void *)XLP_GPIO_VARIANT_XLP832, + }, + { + .compatible = "netlogic,xlp316-gpio", + .data = (void *)XLP_GPIO_VARIANT_XLP316, + }, + { + .compatible = "netlogic,xlp208-gpio", + .data = (void *)XLP_GPIO_VARIANT_XLP208, + }, + { + .compatible = "netlogic,xlp980-gpio", + .data = (void *)XLP_GPIO_VARIANT_XLP980, + }, + { + .compatible = "netlogic,xlp532-gpio", + .data = (void *)XLP_GPIO_VARIANT_XLP532, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, xlp_gpio_of_ids); + +static int xlp_gpio_probe(struct platform_device *pdev) +{ + struct gpio_chip *gc; + struct resource *iores; + struct xlp_gpio_priv *priv; + const struct of_device_id *of_id; + void __iomem *gpio_base; + int irq_base, irq, err; + int ngpio; + u32 soc_type; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -ENODEV; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + gpio_base = devm_ioremap_resource(&pdev->dev, iores); + if (IS_ERR(gpio_base)) + return PTR_ERR(gpio_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "Failed to get soc type!\n"); + return -ENODEV; + } + + soc_type = (uintptr_t) of_id->data; + + switch (soc_type) { + case XLP_GPIO_VARIANT_XLP832: + priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN; + priv->gpio_paddrv = gpio_base + GPIO_PADDRV; + priv->gpio_intr_stat = gpio_base + GPIO_INT_STAT; + priv->gpio_intr_type = gpio_base + GPIO_INT_TYPE; + priv->gpio_intr_pol = gpio_base + GPIO_INT_POL; + priv->gpio_intr_en = gpio_base + GPIO_INT_EN00; + ngpio = 41; + break; + case XLP_GPIO_VARIANT_XLP208: + case XLP_GPIO_VARIANT_XLP316: + priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN; + priv->gpio_paddrv = gpio_base + GPIO_PADDRV; + priv->gpio_intr_stat = gpio_base + GPIO_3XX_INT_STAT; + priv->gpio_intr_type = gpio_base + GPIO_3XX_INT_TYPE; + priv->gpio_intr_pol = gpio_base + GPIO_3XX_INT_POL; + priv->gpio_intr_en = gpio_base + GPIO_3XX_INT_EN00; + + ngpio = (soc_type == XLP_GPIO_VARIANT_XLP208) ? 42 : 57; + break; + case XLP_GPIO_VARIANT_XLP980: + case XLP_GPIO_VARIANT_XLP532: + priv->gpio_out_en = gpio_base + GPIO_9XX_OUTPUT_EN; + priv->gpio_paddrv = gpio_base + GPIO_9XX_PADDRV; + priv->gpio_intr_stat = gpio_base + GPIO_9XX_INT_STAT; + priv->gpio_intr_type = gpio_base + GPIO_9XX_INT_TYPE; + priv->gpio_intr_pol = gpio_base + GPIO_9XX_INT_POL; + priv->gpio_intr_en = gpio_base + GPIO_9XX_INT_EN00; + + ngpio = (soc_type == XLP_GPIO_VARIANT_XLP980) ? 66 : 67; + break; + default: + dev_err(&pdev->dev, "Unknown Processor type!\n"); + return -ENODEV; + } + + bitmap_zero(priv->gpio_enabled_mask, XLP_MAX_NR_GPIO); + + gc = &priv->chip; + + gc->base = 0; + gc->dev = &pdev->dev; + gc->ngpio = ngpio; + gc->of_node = pdev->dev.of_node; + gc->direction_output = xlp_gpio_dir_output; + gc->direction_input = xlp_gpio_dir_input; + gc->set = xlp_gpio_set; + gc->get = xlp_gpio_get; + + spin_lock_init(&priv->lock); + + err = devm_request_irq(&pdev->dev, irq, xlp_gpio_generic_handler, + IRQ_TYPE_NONE, pdev->name, priv); + if (err) + return err; + + irq_base = irq_alloc_descs(-1, XLP_GPIO_IRQ_BASE, gc->ngpio, 0); + if (irq_base < 0) { + dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n"); + return err; + } + + err = gpiochip_add(gc); + if (err < 0) + goto out_free_desc; + + err = gpiochip_irqchip_add(gc, &xlp_gpio_irq_chip, irq_base, + handle_level_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(&pdev->dev, "Could not connect irqchip to gpiochip!\n"); + goto out_gpio_remove; + } + + dev_info(&pdev->dev, "registered %d GPIOs\n", gc->ngpio); + + return 0; + +out_gpio_remove: + gpiochip_remove(gc); +out_free_desc: + irq_free_descs(irq_base, gc->ngpio); + return err; +} + +static struct platform_driver xlp_gpio_driver = { + .driver = { + .name = "xlp-gpio", + .of_match_table = xlp_gpio_of_ids, + }, + .probe = xlp_gpio_probe, +}; +module_platform_driver(xlp_gpio_driver); + +MODULE_AUTHOR("Kamlakant Patel "); +MODULE_AUTHOR("Ganesan Ramalingam "); +MODULE_DESCRIPTION("Netlogic XLP GPIO Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-55-g7522 From b19e7f51a55fe740c18038d1d6957aedfc078d07 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Wed, 29 Apr 2015 18:34:59 +0300 Subject: gpio: gpio-generic: add flag to read out output value from reg_set The change introduces BGPIOF_READ_OUTPUT_REG_SET flag for gpio-generic GPIO chip implementation, which allows to get correct configured value from reg_set register, input value is still get from reg_dat. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-generic.c | 22 +++++++++++++++++++--- include/linux/basic_mmio_gpio.h | 1 + 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c index b92a690f5765..9bda3727fac1 100644 --- a/drivers/gpio/gpio-generic.c +++ b/drivers/gpio/gpio-generic.c @@ -135,6 +135,17 @@ static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc, return 1 << (bgc->bits - 1 - pin); } +static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long pinmask = bgc->pin2mask(bgc, gpio); + + if (bgc->dir & pinmask) + return bgc->read_reg(bgc->reg_set) & pinmask; + else + return bgc->read_reg(bgc->reg_dat) & pinmask; +} + static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) { struct bgpio_chip *bgc = to_bgpio_chip(gc); @@ -416,7 +427,8 @@ static int bgpio_setup_accessors(struct device *dev, static int bgpio_setup_io(struct bgpio_chip *bgc, void __iomem *dat, void __iomem *set, - void __iomem *clr) + void __iomem *clr, + unsigned long flags) { bgc->reg_dat = dat; @@ -437,7 +449,11 @@ static int bgpio_setup_io(struct bgpio_chip *bgc, bgc->gc.set_multiple = bgpio_set_multiple; } - bgc->gc.get = bgpio_get; + if (!(flags & BGPIOF_UNREADABLE_REG_SET) && + (flags & BGPIOF_READ_OUTPUT_REG_SET)) + bgc->gc.get = bgpio_get_set; + else + bgc->gc.get = bgpio_get; return 0; } @@ -500,7 +516,7 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev, bgc->gc.ngpio = bgc->bits; bgc->gc.request = bgpio_request; - ret = bgpio_setup_io(bgc, dat, set, clr); + ret = bgpio_setup_io(bgc, dat, set, clr, flags); if (ret) return ret; diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h index 0e97856b2cff..14eea946e640 100644 --- a/include/linux/basic_mmio_gpio.h +++ b/include/linux/basic_mmio_gpio.h @@ -74,5 +74,6 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev, #define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */ #define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */ #define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3) +#define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */ #endif /* __BASIC_MMIO_GPIO_H */ -- cgit v1.2.3-55-g7522 From f30663398ee28ecd1b66ceeb6a73ef704d4abf78 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Wed, 29 Apr 2015 18:35:00 +0300 Subject: gpio: moxart: get value of output gpio from generic driver Adding a BGPIOF_READ_OUTPUT_REG_SET initialization flag to GPIO generic MMIO driver makes possible to remove a private get() value function from the driver. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-moxart.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c index c3ab46e595da..abd8676ce2b6 100644 --- a/drivers/gpio/gpio-moxart.c +++ b/drivers/gpio/gpio-moxart.c @@ -39,17 +39,6 @@ static void moxart_gpio_free(struct gpio_chip *chip, unsigned offset) pinctrl_free_gpio(offset); } -static int moxart_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct bgpio_chip *bgc = to_bgpio_chip(chip); - u32 ret = bgc->read_reg(bgc->reg_dir); - - if (ret & BIT(offset)) - return !!(bgc->read_reg(bgc->reg_set) & BIT(offset)); - else - return !!(bgc->read_reg(bgc->reg_dat) & BIT(offset)); -} - static int moxart_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -68,8 +57,9 @@ static int moxart_gpio_probe(struct platform_device *pdev) return PTR_ERR(base); ret = bgpio_init(bgc, dev, 4, base + GPIO_DATA_IN, - base + GPIO_DATA_OUT, NULL, - base + GPIO_PIN_DIRECTION, NULL, 0); + base + GPIO_DATA_OUT, NULL, + base + GPIO_PIN_DIRECTION, NULL, + BGPIOF_READ_OUTPUT_REG_SET); if (ret) { dev_err(&pdev->dev, "bgpio_init failed\n"); return ret; @@ -78,7 +68,6 @@ static int moxart_gpio_probe(struct platform_device *pdev) bgc->gc.label = "moxart-gpio"; bgc->gc.request = moxart_gpio_request; bgc->gc.free = moxart_gpio_free; - bgc->gc.get = moxart_gpio_get; bgc->data = bgc->read_reg(bgc->reg_set); bgc->gc.base = 0; bgc->gc.ngpio = 32; -- cgit v1.2.3-55-g7522 From 442b2494b17d1a4f0a14721580271eb23ebffd42 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Wed, 29 Apr 2015 18:35:01 +0300 Subject: gpio: mxc: read output value from GPIO_DR register All supported iMX GPIO controllers store configured GPIO output value in GPIO_DR data register, which is represented by GPIO generic reg_set. Provide a BGPIOF_READ_OUTPUT_REG_SET flag to bgpio_init() to allow correct getting of previously set output value. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mxc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 9f7446a7ac64..38daa640e1b5 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -450,7 +450,8 @@ static int mxc_gpio_probe(struct platform_device *pdev) err = bgpio_init(&port->bgc, &pdev->dev, 4, port->base + GPIO_PSR, port->base + GPIO_DR, NULL, - port->base + GPIO_GDIR, NULL, 0); + port->base + GPIO_GDIR, NULL, + BGPIOF_READ_OUTPUT_REG_SET); if (err) goto out_bgio; -- cgit v1.2.3-55-g7522 From c884fbd452147e952ae160e750553d00ea4dc4c9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 6 May 2015 13:29:06 +0300 Subject: gpio / ACPI: Add support for retrieving GpioInt resources from a device ACPI specification knows two types of GPIOs: GpioIo and GpioInt. The latter is used to describe that a given device interrupt line is connected to a specific GPIO pin. Typical ACPI _CRS entry for such device looks like below: Name (_CRS, ResourceTemplate () { I2cSerialBus (0x004A, ControllerInitiated, 0x00061A80, AddressingMode7Bit, "\\_SB.PCI0.I2C6", 0x00, ResourceConsumer) GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO0", 0x00, ResourceConsumer) { 0x004B } GpioInt (Level, ActiveLow, Shared, PullDefault, 0x0000, "\\_SB.GPO0", 0x00, ResourceConsumer) { 0x004C } }) Currently drivers need to request a GPIO corresponding to the right GpioInt and then translate that to Linux IRQ number. This adds unnecessary lines of boiler-plate code. We can ease this a bit by introducing acpi_dev_gpio_irq_get() analogous to of_irq_get(). This function translates given GpioInt resource under the device in question to the suitable Linux IRQ number. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 29 +++++++++++++++++++++++++++++ include/linux/acpi.h | 7 +++++++ 2 files changed, 36 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index d2303d50f561..bff29bb0a3fe 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -514,6 +514,35 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT); } +/** + * acpi_dev_gpio_irq_get() - Find GpioInt and translate it to Linux IRQ number + * @adev: pointer to a ACPI device to get IRQ from + * @index: index of GpioInt resource (starting from %0) + * + * If the device has one or more GpioInt resources, this function can be + * used to translate from the GPIO offset in the resource to the Linux IRQ + * number. + * + * Return: Linux IRQ number (>%0) on success, negative errno on failure. + */ +int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) +{ + int idx, i; + + for (i = 0, idx = 0; idx <= index; i++) { + struct acpi_gpio_info info; + struct gpio_desc *desc; + + desc = acpi_get_gpiod_by_index(adev, NULL, i, &info); + if (IS_ERR(desc)) + break; + if (info.gpioint && idx++ == index) + return gpiod_to_irq(desc); + } + return -ENOENT; +} +EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get); + static acpi_status acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, u32 bits, u64 *value, void *handler_context, diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e35e29c..f57c440642cd 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -721,6 +721,8 @@ static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev) if (adev) adev->driver_gpios = NULL; } + +int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index); #else static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev, const struct acpi_gpio_mapping *gpios) @@ -728,6 +730,11 @@ static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev, return -ENXIO; } static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev) {} + +static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) +{ + return -ENXIO; +} #endif /* Device properties */ -- cgit v1.2.3-55-g7522 From f1fb9c61b6649987a8c3afe5c4a58563e4e582d7 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 30 Apr 2015 21:47:39 -0400 Subject: drivers/gpio: include for modular crystalcove code This file is built off of a tristate Kconfig option and also contains modular function calls so it should explicitly include module.h to avoid compile breakage during header shuffles done in the future. Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij --- drivers/gpio/gpio-crystalcove.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 91a7ffe83135..cf28ec525e93 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include -- cgit v1.2.3-55-g7522 From f4f79d40623f5bc7cd7207c621256bc2000676d3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:56:47 +0900 Subject: gpio: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mxc.c | 2 +- drivers/gpio/gpio-mxs.c | 2 +- drivers/gpio/gpio-ts5500.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 38daa640e1b5..7007036a4db8 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -131,7 +131,7 @@ static struct mxc_gpio_hwdata *mxc_gpio_hwdata; #define GPIO_INT_FALL_EDGE (mxc_gpio_hwdata->fall_edge) #define GPIO_INT_BOTH_EDGES 0x4 -static struct platform_device_id mxc_gpio_devtype[] = { +static const struct platform_device_id mxc_gpio_devtype[] = { { .name = "imx1-gpio", .driver_data = IMX1_GPIO, diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 84cbda6acdda..150a3225aa41 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -239,7 +239,7 @@ static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset) return !(dir & mask); } -static struct platform_device_id mxs_gpio_ids[] = { +static const struct platform_device_id mxs_gpio_ids[] = { { .name = "imx23-gpio", .driver_data = IMX23_GPIO, diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index 92fbabd82879..b29a102d136b 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -440,7 +440,7 @@ static int ts5500_dio_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id ts5500_dio_ids[] = { +static const struct platform_device_id ts5500_dio_ids[] = { { "ts5500-dio1", TS5500_DIO1 }, { "ts5500-dio2", TS5500_DIO2 }, { "ts5500-dio-lcd", TS5500_LCD }, -- cgit v1.2.3-55-g7522 From a4effddb6d12dc60d95918411387d1a62a6a0c88 Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Fri, 1 May 2015 14:29:06 +0200 Subject: gpio: x-gene: Remove a useless memset priv->irq is allocated using devm_kzalloc so there is no need to memset it. Signed-off-by: Christophe Jaillet Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xgene-sb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index fb9d29a5d584..a7b8f8f0bf70 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -112,7 +112,6 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev) GFP_KERNEL); if (!priv->irq) return -ENOMEM; - memset(priv->irq, 0, sizeof(u32) * XGENE_MAX_GPIO_DS); for (i = 0; i < priv->nirq; i++) { priv->irq[default_lines[i]] = platform_get_irq(pdev, i); -- cgit v1.2.3-55-g7522 From 3c1b5c9bd991d1c023f736b0187582aae8a5b30a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 4 May 2015 16:37:16 +0200 Subject: gpio: xilinx: Fix kernel-doc warnings Fix kernel-doc format: - Add gpio-width description - Remove additional "inited" variable description - Add return value description - xgpio_remove Signed-off-by: Michal Simek Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xilinx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 61243d177740..77fe5d3cb105 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -41,10 +41,10 @@ /** * struct xgpio_instance - Stores information about GPIO device * @mmchip: OF GPIO chip for memory mapped banks + * @gpio_width: GPIO width for every channel * @gpio_state: GPIO state shadow register * @gpio_dir: GPIO direction shadow register * @gpio_lock: Lock used for synchronization - * @inited: True if the port has been inited */ struct xgpio_instance { struct of_mm_gpio_chip mmchip; @@ -231,6 +231,8 @@ static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) * @pdev: pointer to the platform device * * This function remove gpiochips and frees all the allocated resources. + * + * Return: 0 always */ static int xgpio_remove(struct platform_device *pdev) { -- cgit v1.2.3-55-g7522 From fab28b89a1c418382638d3837739faad9111fa8e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:27 +0200 Subject: gpio: clean up gpiochip_remove Clean up gpiochip_remove somewhat and only output warning about removing chip with GPIOs requested once. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 59eaa23767d8..5a5c208d31c7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -325,8 +325,10 @@ static void gpiochip_free_hogs(struct gpio_chip *chip); */ void gpiochip_remove(struct gpio_chip *chip) { + struct gpio_desc *desc; unsigned long flags; unsigned id; + bool requested = false; gpiochip_unexport(chip); @@ -339,15 +341,17 @@ void gpiochip_remove(struct gpio_chip *chip) spin_lock_irqsave(&gpio_lock, flags); for (id = 0; id < chip->ngpio; id++) { - if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) - dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); + desc = &chip->desc[id]; + desc->chip = NULL; + if (test_bit(FLAG_REQUESTED, &desc->flags)) + requested = true; } - for (id = 0; id < chip->ngpio; id++) - chip->desc[id].chip = NULL; - list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); + if (requested) + dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); + kfree(chip->desc); chip->desc = NULL; } -- cgit v1.2.3-55-g7522 From 52176d0d3be29ccb01a44ee77532bb3969939ecf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:28 +0200 Subject: gpio: sysfs: fix redundant lock-as-irq handling Drivers should call gpiochip_lock_as_irq (which prevents the pin direction from being changed) in their irq_request_resources callbacks but some drivers currently fail to do so. Instead a second, explicit and often redundant call to lock-as-irq is made by the sysfs-interface implementation after an irq has been requested. Move the explicit call before the irq-request to match the unlock done after the irq is later released. Note that this also fixes an irq leak, should the explicit call ever have failed. Also add a comment about removing the redundant call once the broken drivers have been fixed. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index af3bc7a8033b..b2b62cc6f9e1 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -195,20 +195,28 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, } } - ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags, - "gpiolib", value_sd); + /* + * FIXME: This should be done in the irq_request_resources callback + * when the irq is requested, but a few drivers currently fail + * to do so. + * + * Remove this redundant call (along with the corresponding + * unlock) when those drivers have been fixed. + */ + ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); if (ret < 0) goto free_id; - ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); - if (ret < 0) { - gpiod_warn(desc, "failed to flag the GPIO for IRQ\n"); - goto free_id; - } + ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags, + "gpiolib", value_sd); + if (ret < 0) + goto err_unlock; desc->flags |= gpio_flags; return 0; +err_unlock: + gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); free_id: idr_remove(&dirent_idr, id); desc->flags &= GPIO_FLAGS_MASK; -- cgit v1.2.3-55-g7522 From cecf58ab551f746c1e2b549d90b51a94b442d1e0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:29 +0200 Subject: gpio: sysfs: preparatory clean ups Put the recently introduced gpio-chip pointer to some more use in gpiod_export. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index b2b62cc6f9e1..658ed28e6d7d 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -599,7 +599,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto fail_unlock; } - if (desc->chip->direction_input && desc->chip->direction_output && + if (chip->direction_input && chip->direction_output && direction_may_change) { set_bit(FLAG_SYSFS_DIR, &desc->flags); } @@ -607,10 +607,10 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) spin_unlock_irqrestore(&gpio_lock, flags); offset = gpio_chip_hwgpio(desc); - if (desc->chip->names && desc->chip->names[offset]) - ioname = desc->chip->names[offset]; + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; - dev = device_create_with_groups(&gpio_class, desc->chip->dev, + dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0), desc, gpio_groups, ioname ? ioname : "gpio%u", desc_to_gpio(desc)); -- cgit v1.2.3-55-g7522 From 3ff74be5c1a72df6d304dcb453f68e3c3bea3bdd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:30 +0200 Subject: gpio: sysfs: reduce gpiochip-export locking scope Reduce scope of sysfs_lock protection during chip export and unexport, which is only needed to prevent gpiod (re-)exports during chip removal. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 658ed28e6d7d..d19bf234e878 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -779,7 +779,6 @@ int gpiochip_export(struct gpio_chip *chip) return 0; /* use chip->base for the ID; it's already known to be unique */ - mutex_lock(&sysfs_lock); dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0), chip, gpiochip_groups, "gpiochip%d", chip->base); @@ -787,6 +786,8 @@ int gpiochip_export(struct gpio_chip *chip) status = PTR_ERR(dev); else status = 0; + + mutex_lock(&sysfs_lock); chip->exported = (status == 0); mutex_unlock(&sysfs_lock); @@ -803,17 +804,17 @@ void gpiochip_unexport(struct gpio_chip *chip) struct gpio_desc *desc; unsigned int i; - mutex_lock(&sysfs_lock); dev = class_find_device(&gpio_class, NULL, chip, match_export); if (dev) { put_device(dev); device_unregister(dev); /* prevent further gpiod exports */ + mutex_lock(&sysfs_lock); chip->exported = false; + mutex_unlock(&sysfs_lock); status = 0; } else status = -ENODEV; - mutex_unlock(&sysfs_lock); if (status) chip_dbg(chip, "%s: status %d\n", __func__, status); -- cgit v1.2.3-55-g7522 From 6a4b6b0a3b55f23f4cc9ad85a1539c581bcb0874 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:31 +0200 Subject: gpio: sysfs: clean up chip class-device handling Clean gpio-chip class device registration and deregistration. The class device is registered when a gpio-chip is added (or from gpiolib_sysfs_init post-core init call), and deregistered when the chip is removed. Store the class device in struct gpio_chip directly rather than do a class-device lookup on deregistration. This also removes the need for the exported flag. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 39 +++++++++++++-------------------------- include/linux/gpio/driver.h | 4 ++-- 2 files changed, 15 insertions(+), 28 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index d19bf234e878..767b79adb9a4 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -582,7 +582,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) mutex_lock(&sysfs_lock); /* check if chip is being removed */ - if (!chip || !chip->exported) { + if (!chip || !chip->cdev) { status = -ENODEV; goto fail_unlock; } @@ -767,7 +767,6 @@ EXPORT_SYMBOL_GPL(gpiod_unexport); int gpiochip_export(struct gpio_chip *chip) { - int status; struct device *dev; /* Many systems register gpio chips for SOC support very early, @@ -783,41 +782,29 @@ int gpiochip_export(struct gpio_chip *chip) chip, gpiochip_groups, "gpiochip%d", chip->base); if (IS_ERR(dev)) - status = PTR_ERR(dev); - else - status = 0; + return PTR_ERR(dev); mutex_lock(&sysfs_lock); - chip->exported = (status == 0); + chip->cdev = dev; mutex_unlock(&sysfs_lock); - if (status) - chip_dbg(chip, "%s: status %d\n", __func__, status); - - return status; + return 0; } void gpiochip_unexport(struct gpio_chip *chip) { - int status; - struct device *dev; struct gpio_desc *desc; unsigned int i; - dev = class_find_device(&gpio_class, NULL, chip, match_export); - if (dev) { - put_device(dev); - device_unregister(dev); - /* prevent further gpiod exports */ - mutex_lock(&sysfs_lock); - chip->exported = false; - mutex_unlock(&sysfs_lock); - status = 0; - } else - status = -ENODEV; + if (!chip->cdev) + return; - if (status) - chip_dbg(chip, "%s: status %d\n", __func__, status); + device_unregister(chip->cdev); + + /* prevent further gpiod exports */ + mutex_lock(&sysfs_lock); + chip->cdev = NULL; + mutex_unlock(&sysfs_lock); /* unregister gpiod class devices owned by sysfs */ for (i = 0; i < chip->ngpio; i++) { @@ -845,7 +832,7 @@ static int __init gpiolib_sysfs_init(void) */ spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(chip, &gpio_chips, list) { - if (chip->exported) + if (chip->cdev) continue; /* diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index f1b36593ec9f..2c1e639f66bd 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -20,6 +20,7 @@ struct seq_file; * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics * @dev: optional device providing the GPIOs + * @cdev: class device used by sysfs interface (may be NULL) * @owner: helps prevent removal of modules exporting active GPIOs * @list: links gpio_chips together for traversal * @request: optional hook for chip-specific activation, such as @@ -57,7 +58,6 @@ struct seq_file; * implies that if the chip supports IRQs, these IRQs need to be threaded * as the chip access may sleep when e.g. reading out the IRQ status * registers. - * @exported: flags if the gpiochip is exported for use from sysfs. Private. * @irq_not_threaded: flag must be set if @can_sleep is set but the * IRQs don't need to be threaded * @@ -74,6 +74,7 @@ struct seq_file; struct gpio_chip { const char *label; struct device *dev; + struct device *cdev; struct module *owner; struct list_head list; @@ -109,7 +110,6 @@ struct gpio_chip { const char *const *names; bool can_sleep; bool irq_not_threaded; - bool exported; #ifdef CONFIG_GPIOLIB_IRQCHIP /* -- cgit v1.2.3-55-g7522 From 426577bd8846d67b735b3a4e8926ef00abb15297 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:32 +0200 Subject: gpio: sysfs: rename gpiochip registration functions Rename the gpio-chip export/unexport functions to the more descriptive names gpiochip_sysfs_register and gpiochip_sysfs_unregister. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 16 +++++++++------- drivers/gpio/gpiolib.c | 4 ++-- drivers/gpio/gpiolib.h | 8 ++++---- 3 files changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 767b79adb9a4..aeb73ef2955e 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -765,13 +765,14 @@ void gpiod_unexport(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_unexport); -int gpiochip_export(struct gpio_chip *chip) +int gpiochip_sysfs_register(struct gpio_chip *chip) { struct device *dev; - /* Many systems register gpio chips for SOC support very early, + /* + * Many systems add gpio chips for SOC support very early, * before driver model support is available. In those cases we - * export this later, in gpiolib_sysfs_init() ... here we just + * register later, in gpiolib_sysfs_init() ... here we just * verify that _some_ field of gpio_class got initialized. */ if (!gpio_class.p) @@ -791,7 +792,7 @@ int gpiochip_export(struct gpio_chip *chip) return 0; } -void gpiochip_unexport(struct gpio_chip *chip) +void gpiochip_sysfs_unregister(struct gpio_chip *chip) { struct gpio_desc *desc; unsigned int i; @@ -836,15 +837,16 @@ static int __init gpiolib_sysfs_init(void) continue; /* - * TODO we yield gpio_lock here because gpiochip_export() - * acquires a mutex. This is unsafe and needs to be fixed. + * TODO we yield gpio_lock here because + * gpiochip_sysfs_register() acquires a mutex. This is unsafe + * and needs to be fixed. * * Also it would be nice to use gpiochip_find() here so we * can keep gpio_chips local to gpiolib.c, but the yield of * gpio_lock prevents us from doing this. */ spin_unlock_irqrestore(&gpio_lock, flags); - status = gpiochip_export(chip); + status = gpiochip_sysfs_register(chip); spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5a5c208d31c7..2ce3df3504e6 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -285,7 +285,7 @@ int gpiochip_add(struct gpio_chip *chip) of_gpiochip_add(chip); acpi_gpiochip_add(chip); - status = gpiochip_export(chip); + status = gpiochip_sysfs_register(chip); if (status) goto err_remove_chip; @@ -330,7 +330,7 @@ void gpiochip_remove(struct gpio_chip *chip) unsigned id; bool requested = false; - gpiochip_unexport(chip); + gpiochip_sysfs_unregister(chip); gpiochip_irqchip_remove(chip); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 594b1798c0e7..ea72f651904d 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -151,17 +151,17 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc) #ifdef CONFIG_GPIO_SYSFS -int gpiochip_export(struct gpio_chip *chip); -void gpiochip_unexport(struct gpio_chip *chip); +int gpiochip_sysfs_register(struct gpio_chip *chip); +void gpiochip_sysfs_unregister(struct gpio_chip *chip); #else -static inline int gpiochip_export(struct gpio_chip *chip) +static inline int gpiochip_sysfs_register(struct gpio_chip *chip) { return 0; } -static inline void gpiochip_unexport(struct gpio_chip *chip) +static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip) { } -- cgit v1.2.3-55-g7522 From 166a85e44245d771bd7042f3ad72aa0e12bb53bd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:33 +0200 Subject: gpio: remove gpiod_sysfs_set_active_low Remove gpiod_sysfs_set_active_low (and gpio_sysfs_set_active_low) which allowed code to change the polarity of a gpio line even after it had been exported through sysfs. Drivers should not care, and generally does not know, about gpio-line polarity which is a hardware feature that needs to be described by firmware. It is currently possible to define gpio-line polarity in device-tree and acpi firmware or using platform data. Userspace can also change the polarity through sysfs. Note that drivers using the legacy gpio interface could still use GPIOF_ACTIVE_LOW to change the polarity before exporting the gpio. There are no in-kernel users of this interface. Cc: Jonathan Corbet Cc: Harry Wei Cc: Arnd Bergmann Cc: linux-doc@vger.kernel.org Cc: linux-kernel@zh-kernel.org Cc: linux-arch@vger.kernel.org Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- Documentation/gpio/gpio-legacy.txt | 9 ------- Documentation/gpio/sysfs.txt | 8 ------- Documentation/zh_CN/gpio.txt | 8 ------- drivers/gpio/gpiolib-sysfs.c | 48 ++------------------------------------ include/asm-generic/gpio.h | 5 ---- include/linux/gpio.h | 7 ------ include/linux/gpio/consumer.h | 6 ----- 7 files changed, 2 insertions(+), 89 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/gpio/gpio-legacy.txt b/Documentation/gpio/gpio-legacy.txt index 6f83fa965b4b..79ab5648d69b 100644 --- a/Documentation/gpio/gpio-legacy.txt +++ b/Documentation/gpio/gpio-legacy.txt @@ -751,9 +751,6 @@ requested using gpio_request(): int gpio_export_link(struct device *dev, const char *name, unsigned gpio) - /* change the polarity of a GPIO node in sysfs */ - int gpio_sysfs_set_active_low(unsigned gpio, int value); - After a kernel driver requests a GPIO, it may only be made available in the sysfs interface by gpio_export(). The driver can control whether the signal direction may change. This helps drivers prevent userspace code @@ -767,9 +764,3 @@ After the GPIO has been exported, gpio_export_link() allows creating symlinks from elsewhere in sysfs to the GPIO sysfs node. Drivers can use this to provide the interface under their own device in sysfs with a descriptive name. - -Drivers can use gpio_sysfs_set_active_low() to hide GPIO line polarity -differences between boards from user space. This only affects the -sysfs interface. Polarity change can be done both before and after -gpio_export(), and previously enabled poll(2) support for either -rising or falling edge will be reconfigured to follow this setting. diff --git a/Documentation/gpio/sysfs.txt b/Documentation/gpio/sysfs.txt index c2c3a97f8ff7..535b6a8a7a7c 100644 --- a/Documentation/gpio/sysfs.txt +++ b/Documentation/gpio/sysfs.txt @@ -132,9 +132,6 @@ requested using gpio_request(): int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc); - /* change the polarity of a GPIO node in sysfs */ - int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); - After a kernel driver requests a GPIO, it may only be made available in the sysfs interface by gpiod_export(). The driver can control whether the signal direction may change. This helps drivers prevent userspace code @@ -148,8 +145,3 @@ After the GPIO has been exported, gpiod_export_link() allows creating symlinks from elsewhere in sysfs to the GPIO sysfs node. Drivers can use this to provide the interface under their own device in sysfs with a descriptive name. - -Drivers can use gpiod_sysfs_set_active_low() to hide GPIO line polarity -differences between boards from user space. Polarity change can be done both -before and after gpiod_export(), and previously enabled poll(2) support for -either rising or falling edge will be reconfigured to follow this setting. diff --git a/Documentation/zh_CN/gpio.txt b/Documentation/zh_CN/gpio.txt index d5b8f01833f4..bce972521065 100644 --- a/Documentation/zh_CN/gpio.txt +++ b/Documentation/zh_CN/gpio.txt @@ -638,9 +638,6 @@ GPIO 控制器的路径类似 /sys/class/gpio/gpiochip42/ (对于从#42 GPIO int gpio_export_link(struct device *dev, const char *name, unsigned gpio) - /* 改变 sysfs 中的一个 GPIO 节点的极性 */ - int gpio_sysfs_set_active_low(unsigned gpio, int value); - 在一个内核驱动申请一个 GPIO 之后,它可以通过 gpio_export()使其在 sysfs 接口中可见。该驱动可以控制信号方向是否可修改。这有助于防止用户空间代码无意间 破坏重要的系统状态。 @@ -651,8 +648,3 @@ GPIO 控制器的路径类似 /sys/class/gpio/gpiochip42/ (对于从#42 GPIO 在 GPIO 被导出之后,gpio_export_link()允许在 sysfs 文件系统的任何地方 创建一个到这个 GPIO sysfs 节点的符号链接。这样驱动就可以通过一个描述性的 名字,在 sysfs 中他们所拥有的设备下提供一个(到这个 GPIO sysfs 节点的)接口。 - -驱动可以使用 gpio_sysfs_set_active_low() 来在用户空间隐藏电路板之间 -GPIO 线的极性差异。这个仅对 sysfs 接口起作用。极性的改变可以在 gpio_export() -前后进行,且之前使能的轮询操作(poll(2))支持(上升或下降沿)将会被重新配置来遵循 -这个设置。 diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index aeb73ef2955e..9dcd346a20fb 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -308,8 +308,8 @@ static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, clear_bit(FLAG_ACTIVE_LOW, &desc->flags); /* reconfigure poll(2) support if enabled on one edge only */ - if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^ - !!test_bit(FLAG_TRIG_FALL, &desc->flags))) { + if (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^ + !!test_bit(FLAG_TRIG_FALL, &desc->flags)) { unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK; gpio_setup_irq(desc, dev, 0); @@ -680,50 +680,6 @@ int gpiod_export_link(struct device *dev, const char *name, } EXPORT_SYMBOL_GPL(gpiod_export_link); -/** - * gpiod_sysfs_set_active_low - set the polarity of gpio sysfs value - * @gpio: gpio to change - * @value: non-zero to use active low, i.e. inverted values - * - * Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute. - * The GPIO does not have to be exported yet. If poll(2) support has - * been enabled for either rising or falling edge, it will be - * reconfigured to follow the new polarity. - * - * Returns zero on success, else an error. - */ -int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) -{ - struct device *dev = NULL; - int status = -EINVAL; - - if (!desc) { - pr_warn("%s: invalid GPIO\n", __func__); - return -EINVAL; - } - - mutex_lock(&sysfs_lock); - - if (test_bit(FLAG_EXPORT, &desc->flags)) { - dev = class_find_device(&gpio_class, NULL, desc, match_export); - if (dev == NULL) { - status = -ENODEV; - goto unlock; - } - } - - status = sysfs_set_active_low(desc, dev, value); - put_device(dev); -unlock: - mutex_unlock(&sysfs_lock); - - if (status) - gpiod_dbg(desc, "%s: status %d\n", __func__, status); - - return status; -} -EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low); - /** * gpiod_unexport - reverse effect of gpio_export() * @gpio: gpio to make unavailable diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 9bb0d11729c9..40ec1433f05d 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -128,11 +128,6 @@ static inline int gpio_export_link(struct device *dev, const char *name, return gpiod_export_link(dev, name, gpio_to_desc(gpio)); } -static inline int gpio_sysfs_set_active_low(unsigned gpio, int value) -{ - return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value); -} - static inline void gpio_unexport(unsigned gpio) { gpiod_unexport(gpio_to_desc(gpio)); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index ab81339a8590..d12b5d566e4b 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -196,13 +196,6 @@ static inline int gpio_export_link(struct device *dev, const char *name, return -EINVAL; } -static inline int gpio_sysfs_set_active_low(unsigned gpio, int value) -{ - /* GPIO can never have been requested */ - WARN_ON(1); - return -EINVAL; -} - static inline void gpio_unexport(unsigned gpio) { /* GPIO can never have been exported */ diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 3a7c9ffd5ab9..09a7fb0062a6 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -449,7 +449,6 @@ static inline int desc_to_gpio(const struct gpio_desc *desc) int gpiod_export(struct gpio_desc *desc, bool direction_may_change); int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc); -int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); void gpiod_unexport(struct gpio_desc *desc); #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ @@ -466,11 +465,6 @@ static inline int gpiod_export_link(struct device *dev, const char *name, return -ENOSYS; } -static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) -{ - return -ENOSYS; -} - static inline void gpiod_unexport(struct gpio_desc *desc) { } -- cgit v1.2.3-55-g7522 From 6beac9d1aa9bf104b01816852131fa175c0a178b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:34 +0200 Subject: gpio: sysfs: use DEVICE_ATTR macros Use DEVICE_ATTR_RO and DEVICE_ATTR_RW rather than specifying masks and callbacks directly. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 9dcd346a20fb..a78dabd4035b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -38,7 +38,7 @@ static DEFINE_MUTEX(sysfs_lock); * /edge configuration */ -static ssize_t gpio_direction_show(struct device *dev, +static ssize_t direction_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_desc *desc = dev_get_drvdata(dev); @@ -59,7 +59,7 @@ static ssize_t gpio_direction_show(struct device *dev, return status; } -static ssize_t gpio_direction_store(struct device *dev, +static ssize_t direction_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); @@ -81,11 +81,9 @@ static ssize_t gpio_direction_store(struct device *dev, mutex_unlock(&sysfs_lock); return status ? : size; } +static DEVICE_ATTR_RW(direction); -static /* const */ DEVICE_ATTR(direction, 0644, - gpio_direction_show, gpio_direction_store); - -static ssize_t gpio_value_show(struct device *dev, +static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_desc *desc = dev_get_drvdata(dev); @@ -102,7 +100,7 @@ static ssize_t gpio_value_show(struct device *dev, return status; } -static ssize_t gpio_value_store(struct device *dev, +static ssize_t value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); @@ -127,9 +125,7 @@ static ssize_t gpio_value_store(struct device *dev, mutex_unlock(&sysfs_lock); return status; } - -static DEVICE_ATTR(value, 0644, - gpio_value_show, gpio_value_store); +static DEVICE_ATTR_RW(value); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { @@ -237,7 +233,7 @@ static const struct { { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) }, }; -static ssize_t gpio_edge_show(struct device *dev, +static ssize_t edge_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); @@ -264,7 +260,7 @@ static ssize_t gpio_edge_show(struct device *dev, return status; } -static ssize_t gpio_edge_store(struct device *dev, +static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); @@ -291,8 +287,7 @@ found: return status; } - -static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store); +static DEVICE_ATTR_RW(edge); static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, int value) @@ -319,7 +314,7 @@ static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, return status; } -static ssize_t gpio_active_low_show(struct device *dev, +static ssize_t active_low_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); @@ -338,7 +333,7 @@ static ssize_t gpio_active_low_show(struct device *dev, return status; } -static ssize_t gpio_active_low_store(struct device *dev, +static ssize_t active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); @@ -360,9 +355,7 @@ static ssize_t gpio_active_low_store(struct device *dev, return status ? : size; } - -static DEVICE_ATTR(active_low, 0644, - gpio_active_low_show, gpio_active_low_store); +static DEVICE_ATTR_RW(active_low); static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) @@ -410,32 +403,32 @@ static const struct attribute_group *gpio_groups[] = { * /ngpio ... matching gpio_chip.ngpio */ -static ssize_t chip_base_show(struct device *dev, +static ssize_t base_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%d\n", chip->base); } -static DEVICE_ATTR(base, 0444, chip_base_show, NULL); +static DEVICE_ATTR_RO(base); -static ssize_t chip_label_show(struct device *dev, +static ssize_t label_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%s\n", chip->label ? : ""); } -static DEVICE_ATTR(label, 0444, chip_label_show, NULL); +static DEVICE_ATTR_RO(label); -static ssize_t chip_ngpio_show(struct device *dev, +static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%u\n", chip->ngpio); } -static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); +static DEVICE_ATTR_RO(ngpio); static struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, -- cgit v1.2.3-55-g7522 From 54d9acd7540995b06a47974e3d06da7f68df9d4a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:35 +0200 Subject: gpio: sysfs: release irq after class-device deregistration Make sure to release any irq only after the class device has been deregistered. This avoids a race between gpiod_unexport and edge_store, where an irq could be allocated just before the gpio class device is deregistered without relying on FLAG_EXPORT and the global sysfs lock. Note that there is no need to hold the sysfs lock when releasing the irq after the class device is gone as kernfs will prevent further attribute operations. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index a78dabd4035b..e4b079eec9bc 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -695,7 +695,6 @@ void gpiod_unexport(struct gpio_desc *desc) dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev) { - gpio_setup_irq(desc, dev, 0); clear_bit(FLAG_SYSFS_DIR, &desc->flags); clear_bit(FLAG_EXPORT, &desc->flags); } else @@ -706,6 +705,11 @@ void gpiod_unexport(struct gpio_desc *desc) if (dev) { device_unregister(dev); + /* + * Release irq after deregistration to prevent race with + * edge_store. + */ + gpio_setup_irq(desc, dev, 0); put_device(dev); } -- cgit v1.2.3-55-g7522 From f0b7866a02370d9a03b0be497b9f88d982b8b67d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:36 +0200 Subject: gpio: sysfs: remove redundant export tests The attribute operations will never be called for an unregistered device so remove redundant checks for FLAG_EXPORT. Note that kernfs will also guarantee that any active sysfs operation has finished before the attribute is removed during deregistration. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 74 ++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 51 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index e4b079eec9bc..10baf31efe74 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -46,14 +46,10 @@ static ssize_t direction_show(struct device *dev, mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) { - status = -EIO; - } else { - gpiod_get_direction(desc); - status = sprintf(buf, "%s\n", + gpiod_get_direction(desc); + status = sprintf(buf, "%s\n", test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); - } mutex_unlock(&sysfs_lock); return status; @@ -67,9 +63,7 @@ static ssize_t direction_store(struct device *dev, mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) - status = -EIO; - else if (sysfs_streq(buf, "high")) + if (sysfs_streq(buf, "high")) status = gpiod_direction_output_raw(desc, 1); else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) status = gpiod_direction_output_raw(desc, 0); @@ -91,10 +85,7 @@ static ssize_t value_show(struct device *dev, mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) - status = -EIO; - else - status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); + status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); mutex_unlock(&sysfs_lock); return status; @@ -108,11 +99,9 @@ static ssize_t value_store(struct device *dev, mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) - status = -EIO; - else if (!test_bit(FLAG_IS_OUT, &desc->flags)) + if (!test_bit(FLAG_IS_OUT, &desc->flags)) { status = -EPERM; - else { + } else { long value; status = kstrtol(buf, 0, &value); @@ -237,23 +226,18 @@ static ssize_t edge_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); - ssize_t status; + unsigned long mask; + ssize_t status = 0; + int i; mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) - status = -EIO; - else { - int i; - - status = 0; - for (i = 0; i < ARRAY_SIZE(trigger_types); i++) - if ((desc->flags & GPIO_TRIGGER_MASK) - == trigger_types[i].flags) { - status = sprintf(buf, "%s\n", - trigger_types[i].name); - break; - } + for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { + mask = desc->flags & GPIO_TRIGGER_MASK; + if (mask == trigger_types[i].flags) { + status = sprintf(buf, "%s\n", trigger_types[i].name); + break; + } } mutex_unlock(&sysfs_lock); @@ -275,13 +259,9 @@ static ssize_t edge_store(struct device *dev, found: mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) - status = -EIO; - else { - status = gpio_setup_irq(desc, dev, trigger_types[i].flags); - if (!status) - status = size; - } + status = gpio_setup_irq(desc, dev, trigger_types[i].flags); + if (!status) + status = size; mutex_unlock(&sysfs_lock); @@ -322,10 +302,7 @@ static ssize_t active_low_show(struct device *dev, mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) - status = -EIO; - else - status = sprintf(buf, "%d\n", + status = sprintf(buf, "%d\n", !!test_bit(FLAG_ACTIVE_LOW, &desc->flags)); mutex_unlock(&sysfs_lock); @@ -338,18 +315,13 @@ static ssize_t active_low_store(struct device *dev, { struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; + long value; mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) { - status = -EIO; - } else { - long value; - - status = kstrtol(buf, 0, &value); - if (status == 0) - status = sysfs_set_active_low(desc, dev, value != 0); - } + status = kstrtol(buf, 0, &value); + if (status == 0) + status = sysfs_set_active_low(desc, dev, value != 0); mutex_unlock(&sysfs_lock); -- cgit v1.2.3-55-g7522 From c43960fbcc514b0ec8169a66ba9955608e6775a7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:37 +0200 Subject: gpio: sysfs: add gpiod class-device data Add gpiod class-device data. This is a first step in getting rid of the insane gpio-descriptor flag overloading, backward irq-interface implementation, and course grained sysfs-interface locking (a single static mutex for every operation on all exported gpios in a system). Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 62 ++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 10baf31efe74..097e7e539c9d 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -6,9 +6,14 @@ #include #include #include +#include #include "gpiolib.h" +struct gpiod_data { + struct gpio_desc *desc; +}; + static DEFINE_IDR(dirent_idr); @@ -41,7 +46,8 @@ static DEFINE_MUTEX(sysfs_lock); static ssize_t direction_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; ssize_t status; mutex_lock(&sysfs_lock); @@ -58,7 +64,8 @@ static ssize_t direction_show(struct device *dev, static ssize_t direction_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; ssize_t status; mutex_lock(&sysfs_lock); @@ -80,7 +87,8 @@ static DEVICE_ATTR_RW(direction); static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; ssize_t status; mutex_lock(&sysfs_lock); @@ -94,7 +102,8 @@ static ssize_t value_show(struct device *dev, static ssize_t value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; ssize_t status; mutex_lock(&sysfs_lock); @@ -225,7 +234,8 @@ static const struct { static ssize_t edge_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; unsigned long mask; ssize_t status = 0; int i; @@ -247,7 +257,8 @@ static ssize_t edge_show(struct device *dev, static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; ssize_t status; int i; @@ -297,7 +308,8 @@ static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, static ssize_t active_low_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; ssize_t status; mutex_lock(&sysfs_lock); @@ -313,7 +325,8 @@ static ssize_t active_low_show(struct device *dev, static ssize_t active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; ssize_t status; long value; @@ -333,7 +346,8 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = container_of(kobj, struct device, kobj); - struct gpio_desc *desc = dev_get_drvdata(dev); + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; umode_t mode = attr->mode; bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags); @@ -525,6 +539,7 @@ static struct class gpio_class = { int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { struct gpio_chip *chip; + struct gpiod_data *data; unsigned long flags; int status; const char *ioname = NULL; @@ -549,7 +564,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) /* check if chip is being removed */ if (!chip || !chip->cdev) { status = -ENODEV; - goto fail_unlock; + goto err_unlock; } spin_lock_irqsave(&gpio_lock, flags); @@ -561,7 +576,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) test_bit(FLAG_REQUESTED, &desc->flags), test_bit(FLAG_EXPORT, &desc->flags)); status = -EPERM; - goto fail_unlock; + goto err_unlock; } if (chip->direction_input && chip->direction_output && @@ -571,33 +586,45 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) spin_unlock_irqrestore(&gpio_lock, flags); + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto err_unlock; + } + + data->desc = desc; + offset = gpio_chip_hwgpio(desc); if (chip->names && chip->names[offset]) ioname = chip->names[offset]; dev = device_create_with_groups(&gpio_class, chip->dev, - MKDEV(0, 0), desc, gpio_groups, + MKDEV(0, 0), data, gpio_groups, ioname ? ioname : "gpio%u", desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); - goto fail_unlock; + goto err_free_data; } set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; -fail_unlock: +err_free_data: + kfree(data); +err_unlock: mutex_unlock(&sysfs_lock); gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } EXPORT_SYMBOL_GPL(gpiod_export); -static int match_export(struct device *dev, const void *data) +static int match_export(struct device *dev, const void *desc) { - return dev_get_drvdata(dev) == data; + struct gpiod_data *data = dev_get_drvdata(dev); + + return data->desc == desc; } /** @@ -653,6 +680,7 @@ EXPORT_SYMBOL_GPL(gpiod_export_link); */ void gpiod_unexport(struct gpio_desc *desc) { + struct gpiod_data *data; int status = 0; struct device *dev = NULL; @@ -676,6 +704,7 @@ void gpiod_unexport(struct gpio_desc *desc) mutex_unlock(&sysfs_lock); if (dev) { + data = dev_get_drvdata(dev); device_unregister(dev); /* * Release irq after deregistration to prevent race with @@ -683,6 +712,7 @@ void gpiod_unexport(struct gpio_desc *desc) */ gpio_setup_irq(desc, dev, 0); put_device(dev); + kfree(data); } if (status) -- cgit v1.2.3-55-g7522 From 0f62850808b9ad14e933a68bd11d7c41f1930dfe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:38 +0200 Subject: gpio: sysfs: remove redundant gpio-descriptor parameters Remove redundant gpio-descriptor parameters from sysfs_set_active_low and gpio_setup_irq. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 097e7e539c9d..0bc959fbcf23 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -133,9 +133,10 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) return IRQ_HANDLED; } -static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, - unsigned long gpio_flags) +static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) { + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; struct kernfs_node *value_sd; unsigned long irq_flags; int ret, irq, id; @@ -257,8 +258,6 @@ static ssize_t edge_show(struct device *dev, static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; ssize_t status; int i; @@ -270,7 +269,7 @@ static ssize_t edge_store(struct device *dev, found: mutex_lock(&sysfs_lock); - status = gpio_setup_irq(desc, dev, trigger_types[i].flags); + status = gpio_setup_irq(dev, trigger_types[i].flags); if (!status) status = size; @@ -280,9 +279,10 @@ found: } static DEVICE_ATTR_RW(edge); -static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, - int value) +static int sysfs_set_active_low(struct device *dev, int value) { + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; int status = 0; if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) @@ -298,8 +298,8 @@ static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, !!test_bit(FLAG_TRIG_FALL, &desc->flags)) { unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK; - gpio_setup_irq(desc, dev, 0); - status = gpio_setup_irq(desc, dev, trigger_flags); + gpio_setup_irq(dev, 0); + status = gpio_setup_irq(dev, trigger_flags); } return status; @@ -325,8 +325,6 @@ static ssize_t active_low_show(struct device *dev, static ssize_t active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; ssize_t status; long value; @@ -334,7 +332,7 @@ static ssize_t active_low_store(struct device *dev, status = kstrtol(buf, 0, &value); if (status == 0) - status = sysfs_set_active_low(desc, dev, value != 0); + status = sysfs_set_active_low(dev, value != 0); mutex_unlock(&sysfs_lock); @@ -710,7 +708,7 @@ void gpiod_unexport(struct gpio_desc *desc) * Release irq after deregistration to prevent race with * edge_store. */ - gpio_setup_irq(desc, dev, 0); + gpio_setup_irq(dev, 0); put_device(dev); kfree(data); } -- cgit v1.2.3-55-g7522 From a08f5c21f4a069cd75eb8a6170e3fe8b96e964d6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:39 +0200 Subject: gpio: sysfs: clean up interrupt-interface implementation Store the value sysfs entry in the gpiod data rather than in a global table accessed through an index stored in the overloaded gpio-descriptor flag field. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 54 +++++++++++++++----------------------------- drivers/gpio/gpiolib.h | 3 --- 2 files changed, 18 insertions(+), 39 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 0bc959fbcf23..bccba406fc22 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -12,11 +12,9 @@ struct gpiod_data { struct gpio_desc *desc; + struct kernfs_node *value_kn; }; -static DEFINE_IDR(dirent_idr); - - /* lock protects against unexport_gpio() being called while * sysfs files are active. */ @@ -127,9 +125,10 @@ static DEVICE_ATTR_RW(value); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { - struct kernfs_node *value_sd = priv; + struct gpiod_data *data = priv; + + sysfs_notify_dirent(data->value_kn); - sysfs_notify_dirent(value_sd); return IRQ_HANDLED; } @@ -137,9 +136,8 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - struct kernfs_node *value_sd; unsigned long irq_flags; - int ret, irq, id; + int ret, irq; if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) return 0; @@ -148,17 +146,15 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) if (irq < 0) return -EIO; - id = desc->flags >> ID_SHIFT; - value_sd = idr_find(&dirent_idr, id); - if (value_sd) - free_irq(irq, value_sd); + if (data->value_kn) + free_irq(irq, data); desc->flags &= ~GPIO_TRIGGER_MASK; if (!gpio_flags) { gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); ret = 0; - goto free_id; + goto free_kn; } irq_flags = IRQF_SHARED; @@ -169,25 +165,12 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; - if (!value_sd) { - value_sd = sysfs_get_dirent(dev->kobj.sd, "value"); - if (!value_sd) { + if (!data->value_kn) { + data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); + if (!data->value_kn) { ret = -ENODEV; goto err_out; } - - ret = idr_alloc(&dirent_idr, value_sd, 1, 0, GFP_KERNEL); - if (ret < 0) - goto free_sd; - id = ret; - - desc->flags &= GPIO_FLAGS_MASK; - desc->flags |= (unsigned long)id << ID_SHIFT; - - if (desc->flags >> ID_SHIFT != id) { - ret = -ERANGE; - goto free_id; - } } /* @@ -200,10 +183,10 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) */ ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); if (ret < 0) - goto free_id; + goto free_kn; ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags, - "gpiolib", value_sd); + "gpiolib", data); if (ret < 0) goto err_unlock; @@ -212,12 +195,11 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) err_unlock: gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); -free_id: - idr_remove(&dirent_idr, id); - desc->flags &= GPIO_FLAGS_MASK; -free_sd: - if (value_sd) - sysfs_put(value_sd); +free_kn: + if (data->value_kn) { + sysfs_put(data->value_kn); + data->value_kn = NULL; + } err_out: return ret; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index ea72f651904d..5c4f27020564 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -92,9 +92,6 @@ struct gpio_desc { #define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ -#define ID_SHIFT 16 /* add new flags before this one */ - -#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1) #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) const char *label; -- cgit v1.2.3-55-g7522 From b91e18076f0d3ff9b49322a5b8eee595a1e4c082 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:40 +0200 Subject: gpio: sysfs: only call irq helper if needed Only call irq helper if actually reconfiguring interrupt state. This is a preparatory step in introducing separate gpio-irq request and free functions. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index bccba406fc22..201b757ad0db 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -139,9 +139,6 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) unsigned long irq_flags; int ret, irq; - if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) - return 0; - irq = gpiod_to_irq(desc); if (irq < 0) return -EIO; @@ -240,6 +237,9 @@ static ssize_t edge_show(struct device *dev, static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; + unsigned long flags; ssize_t status; int i; @@ -249,12 +249,20 @@ static ssize_t edge_store(struct device *dev, return -EINVAL; found: + flags = trigger_types[i].flags; + mutex_lock(&sysfs_lock); - status = gpio_setup_irq(dev, trigger_types[i].flags); + if ((desc->flags & GPIO_TRIGGER_MASK) == flags) { + status = size; + goto out_unlock; + } + + status = gpio_setup_irq(dev, flags); if (!status) status = size; +out_unlock: mutex_unlock(&sysfs_lock); return status; @@ -690,7 +698,8 @@ void gpiod_unexport(struct gpio_desc *desc) * Release irq after deregistration to prevent race with * edge_store. */ - gpio_setup_irq(dev, 0); + if (desc->flags & GPIO_TRIGGER_MASK) + gpio_setup_irq(dev, 0); put_device(dev); kfree(data); } -- cgit v1.2.3-55-g7522 From 2ec74a959354dde38155e62f0af3ee0b1a7283ac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:41 +0200 Subject: gpio: sysfs: split irq allocation and deallocation Add separate helper functions for irq request and free. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 74 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 201b757ad0db..d9b3faa01fee 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -13,6 +13,7 @@ struct gpiod_data { struct gpio_desc *desc; struct kernfs_node *value_kn; + int irq; }; /* lock protects against unexport_gpio() being called while @@ -132,27 +133,20 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) return IRQ_HANDLED; } -static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) +static int gpio_sysfs_request_irq(struct device *dev, unsigned long gpio_flags) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; unsigned long irq_flags; - int ret, irq; + int ret; - irq = gpiod_to_irq(desc); - if (irq < 0) + data->irq = gpiod_to_irq(desc); + if (data->irq < 0) return -EIO; - if (data->value_kn) - free_irq(irq, data); - - desc->flags &= ~GPIO_TRIGGER_MASK; - - if (!gpio_flags) { - gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); - ret = 0; - goto free_kn; - } + data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); + if (!data->value_kn) + return -ENODEV; irq_flags = IRQF_SHARED; if (test_bit(FLAG_TRIG_FALL, &gpio_flags)) @@ -162,14 +156,6 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; - if (!data->value_kn) { - data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); - if (!data->value_kn) { - ret = -ENODEV; - goto err_out; - } - } - /* * FIXME: This should be done in the irq_request_resources callback * when the irq is requested, but a few drivers currently fail @@ -180,27 +166,36 @@ static int gpio_setup_irq(struct device *dev, unsigned long gpio_flags) */ ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); if (ret < 0) - goto free_kn; + goto err_put_kn; - ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags, + ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags, "gpiolib", data); if (ret < 0) goto err_unlock; desc->flags |= gpio_flags; + return 0; err_unlock: gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); -free_kn: - if (data->value_kn) { - sysfs_put(data->value_kn); - data->value_kn = NULL; - } -err_out: +err_put_kn: + sysfs_put(data->value_kn); + return ret; } +static void gpio_sysfs_free_irq(struct device *dev) +{ + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; + + desc->flags &= ~GPIO_TRIGGER_MASK; + free_irq(data->irq, data); + gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); + sysfs_put(data->value_kn); +} + static const struct { const char *name; unsigned long flags; @@ -240,7 +235,7 @@ static ssize_t edge_store(struct device *dev, struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; unsigned long flags; - ssize_t status; + ssize_t status = size; int i; for (i = 0; i < ARRAY_SIZE(trigger_types); i++) @@ -258,9 +253,14 @@ found: goto out_unlock; } - status = gpio_setup_irq(dev, flags); - if (!status) - status = size; + if (desc->flags & GPIO_TRIGGER_MASK) + gpio_sysfs_free_irq(dev); + + if (flags) { + status = gpio_sysfs_request_irq(dev, flags); + if (!status) + status = size; + } out_unlock: mutex_unlock(&sysfs_lock); @@ -288,8 +288,8 @@ static int sysfs_set_active_low(struct device *dev, int value) !!test_bit(FLAG_TRIG_FALL, &desc->flags)) { unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK; - gpio_setup_irq(dev, 0); - status = gpio_setup_irq(dev, trigger_flags); + gpio_sysfs_free_irq(dev); + status = gpio_sysfs_request_irq(dev, trigger_flags); } return status; @@ -699,7 +699,7 @@ void gpiod_unexport(struct gpio_desc *desc) * edge_store. */ if (desc->flags & GPIO_TRIGGER_MASK) - gpio_setup_irq(dev, 0); + gpio_sysfs_free_irq(dev); put_device(dev); kfree(data); } -- cgit v1.2.3-55-g7522 From e4339ce32372e2f8c98222c0923b79476c29a309 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:42 +0200 Subject: gpio: sysfs: clean up edge_store Remove goto from success path. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index d9b3faa01fee..1161a46618dd 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -236,14 +236,16 @@ static ssize_t edge_store(struct device *dev, struct gpio_desc *desc = data->desc; unsigned long flags; ssize_t status = size; - int i; + int i; - for (i = 0; i < ARRAY_SIZE(trigger_types); i++) + for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { if (sysfs_streq(trigger_types[i].name, buf)) - goto found; - return -EINVAL; + break; + } + + if (i == ARRAY_SIZE(trigger_types)) + return -EINVAL; -found: flags = trigger_types[i].flags; mutex_lock(&sysfs_lock); -- cgit v1.2.3-55-g7522 From 56d30ec14c13303eb9e7c34358ba6549fc7b0121 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:43 +0200 Subject: gpio: sysfs: clean up gpiod_export_link locking Drop unnecessary locking from gpiod_export_link. If the class device has not already been unregistered, class_find_device returns the ref-counted class device so there's no need for locking. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 1161a46618dd..682e4d34999c 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -631,34 +631,22 @@ static int match_export(struct device *dev, const void *desc) int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc) { - int status = -EINVAL; + struct device *cdev; + int ret; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); return -EINVAL; } - mutex_lock(&sysfs_lock); - - if (test_bit(FLAG_EXPORT, &desc->flags)) { - struct device *tdev; - - tdev = class_find_device(&gpio_class, NULL, desc, match_export); - if (tdev != NULL) { - status = sysfs_create_link(&dev->kobj, &tdev->kobj, - name); - put_device(tdev); - } else { - status = -ENODEV; - } - } - - mutex_unlock(&sysfs_lock); + cdev = class_find_device(&gpio_class, NULL, desc, match_export); + if (!cdev) + return -ENODEV; - if (status) - gpiod_dbg(desc, "%s: status %d\n", __func__, status); + ret = sysfs_create_link(&dev->kobj, &cdev->kobj, name); + put_device(cdev); - return status; + return ret; } EXPORT_SYMBOL_GPL(gpiod_export_link); -- cgit v1.2.3-55-g7522 From 6ffcb7971486ea4f1eb14f07f8efb0b6f829a23c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:44 +0200 Subject: gpio: sysfs: use per-gpio locking Add a per-gpio mutex to serialise attribute operations rather than use one global mutex for all gpios and chips. Having a single global lock for all gpios in a system adds unnecessary latency to the sysfs interface, and especially when having gpio controllers connected over slow buses. Now that the global gpio-sysfs interrupt table is gone and with per-gpio data in place, we can easily switch to using a more fine-grained locking scheme. Keep the global mutex to serialise the global (class) operations of gpio export and unexport and chip removal. Also document the locking assumptions made. Note that this is also needed to fix a race between gpiod_export and gpiod_unexport. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 52 +++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 18 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 682e4d34999c..1bb05aa33a84 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -12,12 +12,15 @@ struct gpiod_data { struct gpio_desc *desc; + + struct mutex mutex; struct kernfs_node *value_kn; int irq; }; -/* lock protects against unexport_gpio() being called while - * sysfs files are active. +/* + * Lock to serialise gpiod export and unexport, and prevent re-export of + * gpiod whose chip is being unregistered. */ static DEFINE_MUTEX(sysfs_lock); @@ -49,14 +52,15 @@ static ssize_t direction_show(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); gpiod_get_direction(desc); status = sprintf(buf, "%s\n", test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); + return status; } @@ -67,7 +71,7 @@ static ssize_t direction_store(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); if (sysfs_streq(buf, "high")) status = gpiod_direction_output_raw(desc, 1); @@ -78,7 +82,8 @@ static ssize_t direction_store(struct device *dev, else status = -EINVAL; - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); + return status ? : size; } static DEVICE_ATTR_RW(direction); @@ -90,11 +95,12 @@ static ssize_t value_show(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); + return status; } @@ -105,7 +111,7 @@ static ssize_t value_store(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); if (!test_bit(FLAG_IS_OUT, &desc->flags)) { status = -EPERM; @@ -119,7 +125,8 @@ static ssize_t value_store(struct device *dev, } } - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); + return status; } static DEVICE_ATTR_RW(value); @@ -133,6 +140,7 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) return IRQ_HANDLED; } +/* Caller holds gpiod-data mutex. */ static int gpio_sysfs_request_irq(struct device *dev, unsigned long gpio_flags) { struct gpiod_data *data = dev_get_drvdata(dev); @@ -185,6 +193,10 @@ err_put_kn: return ret; } +/* + * Caller holds gpiod-data mutex (unless called after class-device + * deregistration). + */ static void gpio_sysfs_free_irq(struct device *dev) { struct gpiod_data *data = dev_get_drvdata(dev); @@ -215,7 +227,7 @@ static ssize_t edge_show(struct device *dev, ssize_t status = 0; int i; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { mask = desc->flags & GPIO_TRIGGER_MASK; @@ -225,7 +237,8 @@ static ssize_t edge_show(struct device *dev, } } - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); + return status; } @@ -248,7 +261,7 @@ static ssize_t edge_store(struct device *dev, flags = trigger_types[i].flags; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); if ((desc->flags & GPIO_TRIGGER_MASK) == flags) { status = size; @@ -265,12 +278,13 @@ static ssize_t edge_store(struct device *dev, } out_unlock: - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); return status; } static DEVICE_ATTR_RW(edge); +/* Caller holds gpiod-data mutex. */ static int sysfs_set_active_low(struct device *dev, int value) { struct gpiod_data *data = dev_get_drvdata(dev); @@ -304,12 +318,12 @@ static ssize_t active_low_show(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); status = sprintf(buf, "%d\n", !!test_bit(FLAG_ACTIVE_LOW, &desc->flags)); - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); return status; } @@ -317,16 +331,17 @@ static ssize_t active_low_show(struct device *dev, static ssize_t active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { + struct gpiod_data *data = dev_get_drvdata(dev); ssize_t status; long value; - mutex_lock(&sysfs_lock); + mutex_lock(&data->mutex); status = kstrtol(buf, 0, &value); if (status == 0) status = sysfs_set_active_low(dev, value != 0); - mutex_unlock(&sysfs_lock); + mutex_unlock(&data->mutex); return status ? : size; } @@ -583,6 +598,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) } data->desc = desc; + mutex_init(&data->mutex); offset = gpio_chip_hwgpio(desc); if (chip->names && chip->names[offset]) -- cgit v1.2.3-55-g7522 From 72eba6f66a0017356380eec64db8780305aee2b8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:45 +0200 Subject: gpio: sysfs: fix race between gpiod export and unexport Make sure to deregister the class device (and release the irq) while holding the sysfs lock in gpio_unexport to prevent racing with gpio_export. Note that this requires the recently introduced per-gpio locking to avoid a deadlock with the kernfs active protection when waiting for the attribute operations to drain during deregistration. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 51 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 25 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 1bb05aa33a84..1540f7d60f06 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -674,9 +674,8 @@ EXPORT_SYMBOL_GPL(gpiod_export_link); */ void gpiod_unexport(struct gpio_desc *desc) { - struct gpiod_data *data; - int status = 0; - struct device *dev = NULL; + struct gpiod_data *data; + struct device *dev; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); @@ -685,33 +684,35 @@ void gpiod_unexport(struct gpio_desc *desc) mutex_lock(&sysfs_lock); - if (test_bit(FLAG_EXPORT, &desc->flags)) { + if (!test_bit(FLAG_EXPORT, &desc->flags)) + goto err_unlock; - dev = class_find_device(&gpio_class, NULL, desc, match_export); - if (dev) { - clear_bit(FLAG_SYSFS_DIR, &desc->flags); - clear_bit(FLAG_EXPORT, &desc->flags); - } else - status = -ENODEV; - } + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (!dev) + goto err_unlock; + + data = dev_get_drvdata(dev); + + clear_bit(FLAG_SYSFS_DIR, &desc->flags); + clear_bit(FLAG_EXPORT, &desc->flags); + + device_unregister(dev); + + /* + * Release irq after deregistration to prevent race with edge_store. + */ + if (desc->flags & GPIO_TRIGGER_MASK) + gpio_sysfs_free_irq(dev); mutex_unlock(&sysfs_lock); - if (dev) { - data = dev_get_drvdata(dev); - device_unregister(dev); - /* - * Release irq after deregistration to prevent race with - * edge_store. - */ - if (desc->flags & GPIO_TRIGGER_MASK) - gpio_sysfs_free_irq(dev); - put_device(dev); - kfree(data); - } + put_device(dev); + kfree(data); - if (status) - gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return; + +err_unlock: + mutex_unlock(&sysfs_lock); } EXPORT_SYMBOL_GPL(gpiod_unexport); -- cgit v1.2.3-55-g7522 From 2f323b85675d89c85e72bec191d42b72d905a652 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:46 +0200 Subject: gpio: sysfs: rename active-low helper Rename active-low helper using common prefix. Also remove unnecessary manipulation of value argument. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 1540f7d60f06..06372c7c822c 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -285,7 +285,7 @@ out_unlock: static DEVICE_ATTR_RW(edge); /* Caller holds gpiod-data mutex. */ -static int sysfs_set_active_low(struct device *dev, int value) +static int gpio_sysfs_set_active_low(struct device *dev, int value) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; @@ -339,7 +339,7 @@ static ssize_t active_low_store(struct device *dev, status = kstrtol(buf, 0, &value); if (status == 0) - status = sysfs_set_active_low(dev, value != 0); + status = gpio_sysfs_set_active_low(dev, value); mutex_unlock(&data->mutex); -- cgit v1.2.3-55-g7522 From 427fdeef5014c77aea7ca10c104d534786c3c1e0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:47 +0200 Subject: gpio: sysfs: remove FLAG_SYSFS_DIR Remove FLAG_SYSFS_DIR, which is sysfs-interface specific, and store it in the class-device data instead. Note that the flag is only used during export. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 15 +++++++-------- drivers/gpio/gpiolib.h | 1 - 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 06372c7c822c..9047b2d556a0 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -16,6 +16,8 @@ struct gpiod_data { struct mutex mutex; struct kernfs_node *value_kn; int irq; + + bool direction_can_change; }; /* @@ -354,7 +356,7 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; umode_t mode = attr->mode; - bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags); + bool show_direction = data->direction_can_change; if (attr == &dev_attr_direction.attr) { if (!show_direction) @@ -583,12 +585,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) status = -EPERM; goto err_unlock; } - - if (chip->direction_input && chip->direction_output && - direction_may_change) { - set_bit(FLAG_SYSFS_DIR, &desc->flags); - } - spin_unlock_irqrestore(&gpio_lock, flags); data = kzalloc(sizeof(*data), GFP_KERNEL); @@ -599,6 +595,10 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) data->desc = desc; mutex_init(&data->mutex); + if (chip->direction_input && chip->direction_output) + data->direction_can_change = direction_may_change; + else + data->direction_can_change = false; offset = gpio_chip_hwgpio(desc); if (chip->names && chip->names[offset]) @@ -693,7 +693,6 @@ void gpiod_unexport(struct gpio_desc *desc) data = dev_get_drvdata(dev); - clear_bit(FLAG_SYSFS_DIR, &desc->flags); clear_bit(FLAG_EXPORT, &desc->flags); device_unregister(dev); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 5c4f27020564..479041f8d381 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -89,7 +89,6 @@ struct gpio_desc { #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ -#define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) -- cgit v1.2.3-55-g7522 From cef1717b7ff9ad7de747aedb5d222936913570fd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:10:48 +0200 Subject: gpio: sysfs: move irq trigger flags to class-device data Move irq trigger flags, which as sysfs-interface specific, to the class device data. This avoids accessing the gpio-descriptor flags field using non-atomic operations without any locking, and allows for a more clear separation of the sysfs interface from gpiolib core. Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 47 ++++++++++++++++++++++---------------------- drivers/gpio/gpiolib.h | 4 ---- 2 files changed, 24 insertions(+), 27 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 9047b2d556a0..b57ed8e55ab5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -10,12 +10,18 @@ #include "gpiolib.h" +#define GPIO_IRQF_TRIGGER_FALLING BIT(0) +#define GPIO_IRQF_TRIGGER_RISING BIT(1) +#define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ + GPIO_IRQF_TRIGGER_RISING) + struct gpiod_data { struct gpio_desc *desc; struct mutex mutex; struct kernfs_node *value_kn; int irq; + unsigned char irq_flags; bool direction_can_change; }; @@ -143,7 +149,7 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) } /* Caller holds gpiod-data mutex. */ -static int gpio_sysfs_request_irq(struct device *dev, unsigned long gpio_flags) +static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; @@ -159,10 +165,10 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned long gpio_flags) return -ENODEV; irq_flags = IRQF_SHARED; - if (test_bit(FLAG_TRIG_FALL, &gpio_flags)) + if (flags & GPIO_IRQF_TRIGGER_FALLING) irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; - if (test_bit(FLAG_TRIG_RISE, &gpio_flags)) + if (flags & GPIO_IRQF_TRIGGER_RISING) irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; @@ -183,7 +189,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned long gpio_flags) if (ret < 0) goto err_unlock; - desc->flags |= gpio_flags; + data->irq_flags = flags; return 0; @@ -204,7 +210,7 @@ static void gpio_sysfs_free_irq(struct device *dev) struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - desc->flags &= ~GPIO_TRIGGER_MASK; + data->irq_flags = 0; free_irq(data->irq, data); gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); sysfs_put(data->value_kn); @@ -212,28 +218,25 @@ static void gpio_sysfs_free_irq(struct device *dev) static const struct { const char *name; - unsigned long flags; + unsigned char flags; } trigger_types[] = { { "none", 0 }, - { "falling", BIT(FLAG_TRIG_FALL) }, - { "rising", BIT(FLAG_TRIG_RISE) }, - { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) }, + { "falling", GPIO_IRQF_TRIGGER_FALLING }, + { "rising", GPIO_IRQF_TRIGGER_RISING }, + { "both", GPIO_IRQF_TRIGGER_BOTH }, }; static ssize_t edge_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; - unsigned long mask; ssize_t status = 0; int i; mutex_lock(&data->mutex); for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { - mask = desc->flags & GPIO_TRIGGER_MASK; - if (mask == trigger_types[i].flags) { + if (data->irq_flags == trigger_types[i].flags) { status = sprintf(buf, "%s\n", trigger_types[i].name); break; } @@ -248,8 +251,7 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; - unsigned long flags; + unsigned char flags; ssize_t status = size; int i; @@ -265,12 +267,12 @@ static ssize_t edge_store(struct device *dev, mutex_lock(&data->mutex); - if ((desc->flags & GPIO_TRIGGER_MASK) == flags) { + if (flags == data->irq_flags) { status = size; goto out_unlock; } - if (desc->flags & GPIO_TRIGGER_MASK) + if (data->irq_flags) gpio_sysfs_free_irq(dev); if (flags) { @@ -292,6 +294,7 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; int status = 0; + unsigned int flags = data->irq_flags; if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) return 0; @@ -302,12 +305,10 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) clear_bit(FLAG_ACTIVE_LOW, &desc->flags); /* reconfigure poll(2) support if enabled on one edge only */ - if (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^ - !!test_bit(FLAG_TRIG_FALL, &desc->flags)) { - unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK; - + if (flags == GPIO_IRQF_TRIGGER_FALLING || + flags == GPIO_IRQF_TRIGGER_RISING) { gpio_sysfs_free_irq(dev); - status = gpio_sysfs_request_irq(dev, trigger_flags); + status = gpio_sysfs_request_irq(dev, flags); } return status; @@ -700,7 +701,7 @@ void gpiod_unexport(struct gpio_desc *desc) /* * Release irq after deregistration to prevent race with edge_store. */ - if (desc->flags & GPIO_TRIGGER_MASK) + if (data->irq_flags) gpio_sysfs_free_irq(dev); mutex_unlock(&sysfs_lock); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 479041f8d381..bf343004b008 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -83,16 +83,12 @@ struct gpio_desc { #define FLAG_IS_OUT 1 #define FLAG_EXPORT 2 /* protected by sysfs_lock */ #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ -#define FLAG_TRIG_FALL 4 /* trigger on falling edge */ -#define FLAG_TRIG_RISE 5 /* trigger on rising edge */ #define FLAG_ACTIVE_LOW 6 /* value has active low */ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ -#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) - const char *label; }; -- cgit v1.2.3-55-g7522 From 6d86750ce62391f5a0a7985d5c050c2e3c823ab9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 4 May 2015 17:23:25 +0200 Subject: gpio: fix gpio leak in gpiochip_add error path Make sure to free any hogged gpios on errors in gpiochip_add. Also move all forward declarations to the top of the file. Fixes: f625d4601759 ("gpio: add GPIO hogging mechanism") Signed-off-by: Johan Hovold Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 59eaa23767d8..6bc612b8a49f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -53,6 +53,11 @@ static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); LIST_HEAD(gpio_chips); + +static void gpiochip_free_hogs(struct gpio_chip *chip); +static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); + + static inline void desc_set_label(struct gpio_desc *d, const char *label) { d->label = label; @@ -297,6 +302,7 @@ int gpiochip_add(struct gpio_chip *chip) err_remove_chip: acpi_gpiochip_remove(chip); + gpiochip_free_hogs(chip); of_gpiochip_remove(chip); spin_lock_irqsave(&gpio_lock, flags); list_del(&chip->list); @@ -313,10 +319,6 @@ err_free_descs: } EXPORT_SYMBOL_GPL(gpiochip_add); -/* Forward-declaration */ -static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); -static void gpiochip_free_hogs(struct gpio_chip *chip); - /** * gpiochip_remove() - unregister a gpio_chip * @chip: the chip to unregister -- cgit v1.2.3-55-g7522 From b8a3f52e9824841fb1a79de649fc3487f734bc9f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 7 May 2015 15:34:37 +0800 Subject: gpio: xlp: Add missing .owner and .label settings for gpio_chip Signed-off-by: Axel Lin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xlp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 8532697768f6..9bdab7203d65 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -366,6 +366,8 @@ static int xlp_gpio_probe(struct platform_device *pdev) gc = &priv->chip; + gc->owner = THIS_MODULE; + gc->label = dev_name(&pdev->dev); gc->base = 0; gc->dev = &pdev->dev; gc->ngpio = ngpio; -- cgit v1.2.3-55-g7522 From f230e8ffc03f17bd9d6b90ea890b8252a8cc1821 Mon Sep 17 00:00:00 2001 From: Michael Brunner Date: Mon, 11 May 2015 12:46:49 +0200 Subject: gpio: gpio-kempld: Fix get_direction return value This patch fixes an inverted return value of the gpio get_direction function. The wrong value causes the direction sysfs entry and GPIO debugfs file to indicate incorrect GPIO direction settings. In some cases it also prevents setting GPIO output values. The problem is also present in all other stable kernel versions since linux-3.12. Cc: Stable # v3.12+ Reported-by: Jochen Henneberg Signed-off-by: Michael Brunner Reviewed-by: Guenter Roeck Signed-off-by: Linus Walleij --- drivers/gpio/gpio-kempld.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c index 6b8115f34208..83f281dda1e0 100644 --- a/drivers/gpio/gpio-kempld.c +++ b/drivers/gpio/gpio-kempld.c @@ -117,7 +117,7 @@ static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset) = container_of(chip, struct kempld_gpio_data, chip); struct kempld_device_data *pld = gpio->pld; - return kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset); + return !kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset); } static int kempld_gpio_pincount(struct kempld_device_data *pld) -- cgit v1.2.3-55-g7522 From 8864afaa635be099c5c73a2f0d640a12e58f1d14 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 12 May 2015 15:20:25 +0200 Subject: gpio: dln2: fix build breakage The dln2 driver was initialising a gpiolib private field, which is now gone. Reported-by: kbuild test robot Signed-off-by: Johan Hovold Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpio-dln2.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index dbdb4de82c6d..6685712c15cf 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -466,7 +466,6 @@ static int dln2_gpio_probe(struct platform_device *pdev) dln2->gpio.owner = THIS_MODULE; dln2->gpio.base = -1; dln2->gpio.ngpio = pins; - dln2->gpio.exported = true; dln2->gpio.can_sleep = true; dln2->gpio.irq_not_threaded = true; dln2->gpio.set = dln2_gpio_set; -- cgit v1.2.3-55-g7522 From 25e4fe92a20bbffde87500615250f1d54bfb832f Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Tue, 12 May 2015 20:12:23 +0300 Subject: gpiolib: cleanup chained handler and data Clean up chained handler and handler data if they were set by gpiochip_set_chained_irqchip(). Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 7 +++++++ include/linux/gpio/driver.h | 1 + 2 files changed, 8 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 2ce3df3504e6..59cb4303e251 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -443,6 +443,8 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, */ irq_set_handler_data(parent_irq, gpiochip); irq_set_chained_handler(parent_irq, parent_handler); + + gpiochip->irq_parent = parent_irq; } /* Set the parent IRQ for all affected IRQs */ @@ -551,6 +553,11 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) acpi_gpiochip_free_interrupts(gpiochip); + if (gpiochip->irq_parent) { + irq_set_chained_handler(gpiochip->irq_parent, NULL); + irq_set_handler_data(gpiochip->irq_parent, NULL); + } + /* Remove all IRQ mappings and delete the domain */ if (gpiochip->irqdomain) { for (offset = 0; offset < gpiochip->ngpio; offset++) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 2c1e639f66bd..96a678842cde 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -121,6 +121,7 @@ struct gpio_chip { unsigned int irq_base; irq_flow_handler_t irq_handler; unsigned int irq_default_type; + int irq_parent; #endif #if defined(CONFIG_OF_GPIO) -- cgit v1.2.3-55-g7522 From 20a8a96873f69dc915e872e291f4376f436b295a Mon Sep 17 00:00:00 2001 From: Colin Cronin Date: Mon, 18 May 2015 11:41:43 -0700 Subject: Drivers: gpio: Fix spelling errors Fixed several spelling errors in gpio-lynxpoint, gpio-pca953x, gpio-tegra, gpio-zynq, gpiolib-of, gpiolib. Signed-off-by: Colin Cronin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-lynxpoint.c | 2 +- drivers/gpio/gpio-pca953x.c | 2 +- drivers/gpio/gpio-tegra.c | 2 +- drivers/gpio/gpio-zynq.c | 2 +- drivers/gpio/gpiolib-of.c | 2 +- drivers/gpio/gpiolib.c | 10 +++++----- 6 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 127c755b38dc..153af464c7a7 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -72,7 +72,7 @@ struct lp_gpio { * * per gpio specific registers consist of two 32bit registers per gpio * (LP_CONFIG1 and LP_CONFIG2), with 94 gpios there's a total of - * 188 config registes. + * 188 config registers. * * A simplified view of the register layout look like this: * diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e2da64abbccd..eed42035207d 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -630,7 +630,7 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert) memset(val, 0, NBANK(chip)); pca953x_write_regs(chip, PCA957X_INVRT, val); - /* To enable register 6, 7 to controll pull up and pull down */ + /* To enable register 6, 7 to control pull up and pull down */ memset(val, 0x02, NBANK(chip)); pca953x_write_regs(chip, PCA957X_BKEN, val); diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 1741981d53c8..a19f81a661b4 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -288,7 +288,7 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio)); /* if gpio is edge triggered, clear condition - * before executing the hander so that we don't + * before executing the handler so that we don't * miss edges */ if (lvl & (0x100 << pin)) { diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 184c4b1b2558..0d9663f53834 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -335,7 +335,7 @@ static void zynq_gpio_irq_ack(struct irq_data *irq_data) * @irq_data: irq data containing irq number of gpio pin for the interrupt * to enable * - * Clears the INTSTS bit and unmasks the given interrrupt. + * Clears the INTSTS bit and unmasks the given interrupt. */ static void zynq_gpio_irq_enable(struct irq_data *irq_data) { diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a6c67c6b4680..9a0ec48a4737 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -242,7 +242,7 @@ int of_gpio_simple_xlate(struct gpio_chip *gc, { /* * We're discouraging gpio_cells < 2, since that way you'll have to - * write your own xlate function (that will have to retrive the GPIO + * write your own xlate function (that will have to retrieve the GPIO * number and the flags from a single gpio cell -- this is possible, * but not recommended). */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 59cb4303e251..e01f6b2bfb3a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -617,7 +617,7 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip, of_node = gpiochip->dev->of_node; #ifdef CONFIG_OF_GPIO /* - * If the gpiochip has an assigned OF node this takes precendence + * If the gpiochip has an assigned OF node this takes precedence * FIXME: get rid of this and use gpiochip->dev->of_node everywhere */ if (gpiochip->of_node) @@ -1220,7 +1220,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value); /* * _gpio_set_open_drain_value() - Set the open drain gpio's value. * @desc: gpio descriptor whose state need to be set. - * @value: Non-zero for setting it HIGH otherise it will set to LOW. + * @value: Non-zero for setting it HIGH otherwise it will set to LOW. */ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value) { @@ -1247,7 +1247,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value) /* * _gpio_set_open_source_value() - Set the open source gpio's value. * @desc: gpio descriptor whose state need to be set. - * @value: Non-zero for setting it HIGH otherise it will set to LOW. + * @value: Non-zero for setting it HIGH otherwise it will set to LOW. */ static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value) { @@ -1889,7 +1889,7 @@ EXPORT_SYMBOL_GPL(gpiod_count); * * Return the GPIO descriptor corresponding to the function con_id of device * dev, -ENOENT if no GPIO has been assigned to the requested function, or - * another IS_ERR() code if an error occured while trying to acquire the GPIO. + * another IS_ERR() code if an error occurred while trying to acquire the GPIO. */ struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags) @@ -1969,7 +1969,7 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, * * Return a valid GPIO descriptor, -ENOENT if no GPIO has been assigned to the * requested function and/or index, or another IS_ERR() code if an error - * occured while trying to acquire the GPIO. + * occurred while trying to acquire the GPIO. */ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev, const char *con_id, -- cgit v1.2.3-55-g7522 From 3fff99bc4e926d9602a7d6e8c008a0175a099ce4 Mon Sep 17 00:00:00 2001 From: Rojhalat Ibrahim Date: Wed, 13 May 2015 11:04:56 +0200 Subject: gpiolib: rename gpiod_set_array to gpiod_set_array_value There have been concerns that the function names gpiod_set_array() and gpiod_get_array() might be confusing to users. One might expect gpiod_get_array() to return array values, while it is actually the array counterpart of gpiod_get(). To be consistent with the single descriptor API we could rename gpiod_set_array() to gpiod_set_array_value(). This makes some function names a bit lengthy: gpiod_set_raw_array_value_cansleep(). Signed-off-by: Rojhalat Ibrahim Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 54 ++++++++++++++++++---------------- drivers/net/phy/mdio-mux-gpio.c | 3 +- drivers/tty/serial/serial_mctrl_gpio.c | 2 +- include/linux/gpio/consumer.h | 37 +++++++++++------------ 4 files changed, 51 insertions(+), 45 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e01f6b2bfb3a..8dfb54f92594 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1316,10 +1316,10 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, } } -static void gpiod_set_array_priv(bool raw, bool can_sleep, - unsigned int array_size, - struct gpio_desc **desc_array, - int *value_array) +static void gpiod_set_array_value_priv(bool raw, bool can_sleep, + unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) { int i = 0; @@ -1412,7 +1412,7 @@ void gpiod_set_value(struct gpio_desc *desc, int value) EXPORT_SYMBOL_GPL(gpiod_set_value); /** - * gpiod_set_raw_array() - assign values to an array of GPIOs + * gpiod_set_raw_array_value() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor / value arrays * @desc_array: array of GPIO descriptors whose values will be assigned * @value_array: array of values to assign @@ -1423,17 +1423,18 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); * This function should be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. */ -void gpiod_set_raw_array(unsigned int array_size, +void gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) { if (!desc_array) return; - gpiod_set_array_priv(true, false, array_size, desc_array, value_array); + gpiod_set_array_value_priv(true, false, array_size, desc_array, + value_array); } -EXPORT_SYMBOL_GPL(gpiod_set_raw_array); +EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); /** - * gpiod_set_array() - assign values to an array of GPIOs + * gpiod_set_array_value() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor / value arrays * @desc_array: array of GPIO descriptors whose values will be assigned * @value_array: array of values to assign @@ -1444,14 +1445,15 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array); * This function should be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. */ -void gpiod_set_array(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array) +void gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, int *value_array) { if (!desc_array) return; - gpiod_set_array_priv(false, false, array_size, desc_array, value_array); + gpiod_set_array_value_priv(false, false, array_size, desc_array, + value_array); } -EXPORT_SYMBOL_GPL(gpiod_set_array); +EXPORT_SYMBOL_GPL(gpiod_set_array_value); /** * gpiod_cansleep() - report whether gpio value access may sleep @@ -1613,7 +1615,7 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); /** - * gpiod_set_raw_array_cansleep() - assign values to an array of GPIOs + * gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor / value arrays * @desc_array: array of GPIO descriptors whose values will be assigned * @value_array: array of values to assign @@ -1623,19 +1625,20 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); * * This function is to be called from contexts that can sleep. */ -void gpiod_set_raw_array_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - int *value_array) +void gpiod_set_raw_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) { might_sleep_if(extra_checks); if (!desc_array) return; - gpiod_set_array_priv(true, true, array_size, desc_array, value_array); + gpiod_set_array_value_priv(true, true, array_size, desc_array, + value_array); } -EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep); +EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep); /** - * gpiod_set_array_cansleep() - assign values to an array of GPIOs + * gpiod_set_array_value_cansleep() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor / value arrays * @desc_array: array of GPIO descriptors whose values will be assigned * @value_array: array of values to assign @@ -1645,16 +1648,17 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep); * * This function is to be called from contexts that can sleep. */ -void gpiod_set_array_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - int *value_array) +void gpiod_set_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) { might_sleep_if(extra_checks); if (!desc_array) return; - gpiod_set_array_priv(false, true, array_size, desc_array, value_array); + gpiod_set_array_value_priv(false, true, array_size, desc_array, + value_array); } -EXPORT_SYMBOL_GPL(gpiod_set_array_cansleep); +EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); /** * gpiod_add_lookup_table() - register GPIO device consumers diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index 66edd99bc302..7ddb1ab70891 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -35,7 +35,8 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, for (n = 0; n < s->gpios->ndescs; n++) values[n] = (desired_child >> n) & 1; - gpiod_set_array_cansleep(s->gpios->ndescs, s->gpios->desc, values); + gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc, + values); return 0; } diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 0ec756c62bcf..d7b846d41630 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -55,7 +55,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl); count++; } - gpiod_set_array(count, desc_array, value_array); + gpiod_set_array_value(count, desc_array, value_array); } EXPORT_SYMBOL_GPL(mctrl_gpio_set); diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 09a7fb0062a6..fd098169fe87 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -100,24 +100,25 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value); /* Value get/set from non-sleeping context */ int gpiod_get_value(const struct gpio_desc *desc); void gpiod_set_value(struct gpio_desc *desc, int value); -void gpiod_set_array(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array); +void gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, int *value_array); int gpiod_get_raw_value(const struct gpio_desc *desc); void gpiod_set_raw_value(struct gpio_desc *desc, int value); -void gpiod_set_raw_array(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array); +void gpiod_set_raw_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); /* Value get/set from sleeping context */ int gpiod_get_value_cansleep(const struct gpio_desc *desc); void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); -void gpiod_set_array_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - int *value_array); +void gpiod_set_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); -void gpiod_set_raw_array_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - int *value_array); +void gpiod_set_raw_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); @@ -304,9 +305,9 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value) /* GPIO can never have been requested */ WARN_ON(1); } -static inline void gpiod_set_array(unsigned int array_size, - struct gpio_desc **desc_array, - int *value_array) +static inline void gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) { /* GPIO can never have been requested */ WARN_ON(1); @@ -322,9 +323,9 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) /* GPIO can never have been requested */ WARN_ON(1); } -static inline void gpiod_set_raw_array(unsigned int array_size, - struct gpio_desc **desc_array, - int *value_array) +static inline void gpiod_set_raw_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) { /* GPIO can never have been requested */ WARN_ON(1); @@ -341,7 +342,7 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) /* GPIO can never have been requested */ WARN_ON(1); } -static inline void gpiod_set_array_cansleep(unsigned int array_size, +static inline void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) { @@ -360,7 +361,7 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, /* GPIO can never have been requested */ WARN_ON(1); } -static inline void gpiod_set_raw_array_cansleep(unsigned int array_size, +static inline void gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) { -- cgit v1.2.3-55-g7522 From 501ef0f95a57e7c32138733c468394a52244c85b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 21 May 2015 13:21:37 +0200 Subject: gpio: rcar: Check for irq_set_irq_wake() failures If an interrupt controller doesn't support wake-up configuration, irq_set_irq_wake() returns an error code. Then any subsequent call trying to deconfigure wake-up will cause an imbalance, and a warning will be printed: WARNING: CPU: 1 PID: 1341 at kernel/irq/manage.c:540 irq_set_irq_wake+0x9c/0xf8() Unbalanced IRQ 26 wake disable To fix this, refrain from any further parent interrupt controller (de)configuration if irq_set_irq_wake() failed. Alternative fixes would be: - calling "gic_set_irqchip_flags(IRQCHIP_SKIP_SET_WAKE)" from the platform code, - setting "gic_chip.flags = IRQCHIP_SKIP_SET_WAKE" in the GIC driver code, but these were withheld as the GIC hardware doesn't really support wake-up interrupts. Fixes: ab82fa7da4dce5c7 ("gpio: rcar: Prevent module clock disable when wake-up is enabled") Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/gpio/gpio-rcar.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index fd3977465948..1e14a6c74ed1 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -177,8 +177,17 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv, gpio_chip); - - irq_set_irq_wake(p->irq_parent, on); + int error; + + if (p->irq_parent) { + error = irq_set_irq_wake(p->irq_parent, on); + if (error) { + dev_dbg(&p->pdev->dev, + "irq %u doesn't support irq_set_wake\n", + p->irq_parent); + p->irq_parent = 0; + } + } if (!p->clk) return 0; -- cgit v1.2.3-55-g7522 From 5f982c70a7c3382d3532ac6d13fdea48ab38b934 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 22 May 2015 17:35:48 +0300 Subject: gpio: omap: fix omap_gpio_free to not clean up irq configuration This patch fixes following issue: - GPIOn is used as IRQ by some dev, for example PCF8575.INT -> gpio6.11 - PCFx driver knows nothing about type of IRQ line (GPIO or not) so it doesn't request gpio and just do request_irq() - If gpio6.11 will be exported through the sysfs and then un-xeported then IRQs from PCFx will not be received any more, because IRQ configuration for gpio6.11 will be cleaned up unconditionally in omap_gpio_free. Fix this by removing all GPIO IRQ specific code from omap_gpio_free() and also do GPIO clean up (change direction to 'in' and disable debounce) only if corresponding GPIO is not used as IRQ too. GPIO IRQ will be properly cleaned up by GPIO irqchip code. Signed-off-by: Grygorii Strashko Tested-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 6efee35f0ad4..505266153b4c 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -690,8 +690,11 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) spin_lock_irqsave(&bank->lock, flags); bank->mod_usage &= ~(BIT(offset)); + if (!LINE_USED(bank->irq_usage, offset)) { + omap_set_gpio_direction(bank, offset, 1); + omap_clear_gpio_debounce(bank, offset); + } omap_disable_gpio_module(bank, offset); - omap_reset_gpio(bank, offset); spin_unlock_irqrestore(&bank->lock, flags); /* -- cgit v1.2.3-55-g7522 From 1562e4618ded89b07d145d6985f469fe8be04830 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 22 May 2015 17:35:49 +0300 Subject: gpio: omap: fix error handling in omap_gpio_irq_type The GPIO bank will be kept powered in case if input parameters are invalid or error occurred in omap_gpio_irq_type. Hence, fix it by ensuring that GPIO bank will be unpowered in case of errors and add additional check of value returned from omap_set_gpio_triggering(). Signed-off-by: Grygorii Strashko Tested-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 505266153b4c..81e229f5ade3 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -488,9 +488,6 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type) unsigned long flags; unsigned offset = d->hwirq; - if (!BANK_USED(bank)) - pm_runtime_get_sync(bank->dev); - if (type & ~IRQ_TYPE_SENSE_MASK) return -EINVAL; @@ -498,12 +495,18 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type) (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) return -EINVAL; + if (!BANK_USED(bank)) + pm_runtime_get_sync(bank->dev); + spin_lock_irqsave(&bank->lock, flags); retval = omap_set_gpio_triggering(bank, offset, type); + if (retval) + goto error; omap_gpio_init_irq(bank, offset); if (!omap_gpio_is_input(bank, offset)) { spin_unlock_irqrestore(&bank->lock, flags); - return -EINVAL; + retval = -EINVAL; + goto error; } spin_unlock_irqrestore(&bank->lock, flags); @@ -512,6 +515,11 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type) else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) __irq_set_handler_locked(d->irq, handle_edge_irq); + return 0; + +error: + if (!BANK_USED(bank)) + pm_runtime_put(bank->dev); return retval; } -- cgit v1.2.3-55-g7522 From 6e96c1b5e54889cd11ce29723a5c38ba284c1d91 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 22 May 2015 17:35:50 +0300 Subject: gpio: omap: rework omap_x_irq_shutdown to touch only irqs specific registers The GPIO Chip and GPIO IRQ Chip functionality are essentially orthogonal, so GPIO IRQ Chip implementation shouldn't touch GPIO specific registers and vise versa. Hence, rework omap_gpio_irq_shutdown and try to touch only irqs specific registers: - don't configure GPIO as input (it, actually, should be already configured as input). - don't clear debounce configuration if GPIO is still used as GPIO. We need to take in to account here commit c9c55d921115 ("gpio/omap: fix off-mode bug: clear debounce settings on free/reset"). Also remove omap_reset_gpio() function as it is not used any more. Signed-off-by: Grygorii Strashko Tested-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 81e229f5ade3..b2fcdf4f8bd7 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -646,15 +646,6 @@ static int omap_set_gpio_wakeup(struct gpio_bank *bank, unsigned offset, return 0; } -static void omap_reset_gpio(struct gpio_bank *bank, unsigned offset) -{ - omap_set_gpio_direction(bank, offset, 1); - omap_set_gpio_irqenable(bank, offset, 0); - omap_clear_gpio_irqstatus(bank, offset); - omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); - omap_clear_gpio_debounce(bank, offset); -} - /* Use disable_irq_wake() and enable_irq_wake() functions from drivers */ static int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable) { @@ -821,8 +812,12 @@ static void omap_gpio_irq_shutdown(struct irq_data *d) spin_lock_irqsave(&bank->lock, flags); bank->irq_usage &= ~(BIT(offset)); + omap_set_gpio_irqenable(bank, offset, 0); + omap_clear_gpio_irqstatus(bank, offset); + omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); + if (!LINE_USED(bank->mod_usage, offset)) + omap_clear_gpio_debounce(bank, offset); omap_disable_gpio_module(bank, offset); - omap_reset_gpio(bank, offset); spin_unlock_irqrestore(&bank->lock, flags); /* -- cgit v1.2.3-55-g7522 From c3518172129a60a1f3071e61a8a4ffc50c7b2a68 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 22 May 2015 17:35:51 +0300 Subject: gpio: omap: rework omap_gpio_request to touch only gpio specific registers The GPIO Chip and GPIO IRQ Chip functionality are essentially orthogonal, so GPIO Chip implementation shouldn't touch GPIO IRQ specific registers and vise versa. Hence, rework omap_gpio_request: - don't reset GPIO IRQ triggering type to IRQ_TYPE_NONE, because GPIO irqchip should be responsible for that; - call directly omap_enable_gpio_module as all needed checks are already present inside it. Signed-off-by: Grygorii Strashko Tested-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index b2fcdf4f8bd7..606b401d2017 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -668,14 +668,7 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) pm_runtime_get_sync(bank->dev); spin_lock_irqsave(&bank->lock, flags); - /* Set trigger to none. You need to enable the desired trigger with - * request_irq() or set_irq_type(). Only do this if the IRQ line has - * not already been requested. - */ - if (!LINE_USED(bank->irq_usage, offset)) { - omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); - omap_enable_gpio_module(bank, offset); - } + omap_enable_gpio_module(bank, offset); bank->mod_usage |= BIT(offset); spin_unlock_irqrestore(&bank->lock, flags); -- cgit v1.2.3-55-g7522 From 121dcb760426ca67ee90a8b2db6a75eee010f8e3 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 22 May 2015 17:35:52 +0300 Subject: gpio: omap: rework omap_gpio_irq_startup to handle current pin state properly The omap_gpio_irq_startup() can be called at time when: - corresponding GPIO has been requested already and in this case it has to be configured as input already. If not - return with -EINVAL and do not try to re-configure it as it could be unsafe. - corresponding GPIO is free: reconfigure GPIO as input. In addition, call omap_enable_gpio_module directly as all needed checks are already present inside it. Signed-off-by: Grygorii Strashko Tested-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 606b401d2017..b0c57d505be7 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -790,11 +790,23 @@ static unsigned int omap_gpio_irq_startup(struct irq_data *d) pm_runtime_get_sync(bank->dev); spin_lock_irqsave(&bank->lock, flags); - omap_gpio_init_irq(bank, offset); + + if (!LINE_USED(bank->mod_usage, offset)) + omap_set_gpio_direction(bank, offset, 1); + else if (!omap_gpio_is_input(bank, offset)) + goto err; + omap_enable_gpio_module(bank, offset); + bank->irq_usage |= BIT(offset); + spin_unlock_irqrestore(&bank->lock, flags); omap_gpio_unmask_irq(d); return 0; +err: + spin_unlock_irqrestore(&bank->lock, flags); + if (!BANK_USED(bank)) + pm_runtime_put(bank->dev); + return -EINVAL; } static void omap_gpio_irq_shutdown(struct irq_data *d) -- cgit v1.2.3-55-g7522 From b6ac1280b6969607c5a01e316cc4ab693490c333 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 22 May 2015 12:35:12 +1200 Subject: gpio: Prevent an integer overflow in the pca953x driver Interrupts were missed if an 8-bit integer overflow occurred. This was observed when bank0,pin7 and bank1,pin7 changed simultaniously. As the 8-bit totals were only checked against zero, replace them with booleans. Name the booleans so that their purpose is clear. Signed-off-by: Joshua Scott Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pca953x.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index eed42035207d..d233eb3b8132 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -443,12 +443,13 @@ static struct irq_chip pca953x_irq_chip = { .irq_set_type = pca953x_irq_set_type, }; -static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) +static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) { u8 cur_stat[MAX_BANK]; u8 old_stat[MAX_BANK]; - u8 pendings = 0; - u8 trigger[MAX_BANK], triggers = 0; + bool pending_seen = false; + bool trigger_seen = false; + u8 trigger[MAX_BANK]; int ret, i, offset = 0; switch (chip->chip_type) { @@ -461,7 +462,7 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) } ret = pca953x_read_regs(chip, offset, cur_stat); if (ret) - return 0; + return false; /* Remove output pins from the equation */ for (i = 0; i < NBANK(chip); i++) @@ -471,11 +472,12 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) for (i = 0; i < NBANK(chip); i++) { trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i]; - triggers += trigger[i]; + if (trigger[i]) + trigger_seen = true; } - if (!triggers) - return 0; + if (!trigger_seen) + return false; memcpy(chip->irq_stat, cur_stat, NBANK(chip)); @@ -483,10 +485,11 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) pending[i] = (old_stat[i] & chip->irq_trig_fall[i]) | (cur_stat[i] & chip->irq_trig_raise[i]); pending[i] &= trigger[i]; - pendings += pending[i]; + if (pending[i]) + pending_seen = true; } - return pendings; + return pending_seen; } static irqreturn_t pca953x_irq_handler(int irq, void *devid) -- cgit v1.2.3-55-g7522 From 08b085a07efe12568d86dff064e6f089e2971744 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Mon, 25 May 2015 22:39:50 +0200 Subject: gpio-stp-xway: Fix enabling the highest bit of the PHY LEDs 0x3 only masks two bits, but three bits have to be allowed. This fixes GPHY0 LED2 (which is the highest bit of phy2) on my board. Signed-off-by: Martin Blumenstingl Acked-by: John Crispin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stp-xway.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 202361eb7279..6d4148f53b51 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -58,7 +58,7 @@ #define XWAY_STP_ADSL_MASK 0x3 /* 2 groups of 3 bits can be driven by the phys */ -#define XWAY_STP_PHY_MASK 0x3 +#define XWAY_STP_PHY_MASK 0x7 #define XWAY_STP_PHY1_SHIFT 27 #define XWAY_STP_PHY2_SHIFT 15 -- cgit v1.2.3-55-g7522 From ffb8e44bd7617ede81d526d33d13d96a2c6a6e20 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 26 May 2015 13:34:02 +0200 Subject: gpio: pcf857x: Check for irq_set_irq_wake() failures If an interrupt controller doesn't support wake-up configuration, irq_set_irq_wake() returns an error code. Then any subsequent call trying to deconfigure wake-up will cause an imbalance, and a warning will be printed: WARNING: CPU: 1 PID: 1341 at kernel/irq/manage.c:540 irq_set_irq_wake+0x Unbalanced IRQ 26 wake disable To fix this, refrain from any further parent interrupt controller (de)configuration if irq_set_irq_wake() failed. Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pcf857x.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 945f0cda8529..83db0e19ec7e 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -91,6 +91,7 @@ struct pcf857x { spinlock_t slock; /* protect irq demux */ unsigned out; /* software latch */ unsigned status; /* current status */ + unsigned int irq_parent; int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -217,9 +218,19 @@ static unsigned int noop_ret(struct irq_data *data) static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on) { struct pcf857x *gpio = irq_data_get_irq_chip_data(data); + int error = 0; + + if (gpio->irq_parent) { + error = irq_set_irq_wake(gpio->irq_parent, on); + if (error) { + dev_dbg(&gpio->client->dev, + "irq %u doesn't support irq_set_wake\n", + gpio->irq_parent); + gpio->irq_parent = 0; + } + } - irq_set_irq_wake(gpio->client->irq, on); - return 0; + return error; } static struct irq_chip pcf857x_irq_chip = { @@ -364,6 +375,7 @@ static int pcf857x_probe(struct i2c_client *client, gpiochip_set_chained_irqchip(&gpio->chip, &pcf857x_irq_chip, client->irq, NULL); + gpio->irq_parent = client->irq; } /* Let platform code set up the GPIOs and their users. -- cgit v1.2.3-55-g7522 From 50f09073932362d29020b03ca8510f14acc0ca17 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 26 May 2015 23:12:00 +0200 Subject: gpio: stp-xway: Use the of_property_read_u32 helper This removes some redundant code but does not have any functional impact. Signed-off-by: Martin Blumenstingl Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stp-xway.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 6d4148f53b51..81bdbe7ba2a4 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -200,7 +200,7 @@ static int xway_stp_hw_init(struct xway_stp *chip) static int xway_stp_probe(struct platform_device *pdev) { struct resource *res; - const __be32 *shadow, *groups, *dsl, *phy; + u32 shadow, groups, dsl, phy; struct xway_stp *chip; struct clk *clk; int ret = 0; @@ -223,33 +223,28 @@ static int xway_stp_probe(struct platform_device *pdev) chip->gc.owner = THIS_MODULE; /* store the shadow value if one was passed by the devicetree */ - shadow = of_get_property(pdev->dev.of_node, "lantiq,shadow", NULL); - if (shadow) - chip->shadow = be32_to_cpu(*shadow); + if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow)) + chip->shadow = shadow; /* find out which gpio groups should be enabled */ - groups = of_get_property(pdev->dev.of_node, "lantiq,groups", NULL); - if (groups) - chip->groups = be32_to_cpu(*groups) & XWAY_STP_GROUP_MASK; + if (!of_property_read_u32(pdev->dev.of_node, "lantiq,groups", &groups)) + chip->groups = groups & XWAY_STP_GROUP_MASK; else chip->groups = XWAY_STP_GROUP0; chip->gc.ngpio = fls(chip->groups) * 8; /* find out which gpios are controlled by the dsl core */ - dsl = of_get_property(pdev->dev.of_node, "lantiq,dsl", NULL); - if (dsl) - chip->dsl = be32_to_cpu(*dsl) & XWAY_STP_ADSL_MASK; + if (!of_property_read_u32(pdev->dev.of_node, "lantiq,dsl", &dsl)) + chip->dsl = dsl & XWAY_STP_ADSL_MASK; /* find out which gpios are controlled by the phys */ if (of_machine_is_compatible("lantiq,ar9") || of_machine_is_compatible("lantiq,gr9") || of_machine_is_compatible("lantiq,vr9")) { - phy = of_get_property(pdev->dev.of_node, "lantiq,phy1", NULL); - if (phy) - chip->phy1 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK; - phy = of_get_property(pdev->dev.of_node, "lantiq,phy2", NULL); - if (phy) - chip->phy2 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK; + if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy1", &phy)) + chip->phy1 = phy & XWAY_STP_PHY_MASK; + if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy2", &phy)) + chip->phy2 = phy & XWAY_STP_PHY_MASK; } /* check which edge trigger we should use, default to a falling edge */ -- cgit v1.2.3-55-g7522 From 61e749d7e1627d375156553ea0ae83c4f6bb5a9b Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Thu, 28 May 2015 10:58:49 +0800 Subject: gpio: crystalcove: set IRQCHIP_SKIP_SET_WAKE for the irqchip The CrystalCove GPIO irqchip doesn't have irq_set_wake callback defined so we should set IRQCHIP_SKIP_SET_WAKE for it or it would cause an irq desc's wake_depth unbalanced warning during system resume phase from the gpio_keys driver, which is the driver for the power button of the ASUS T100 laptop. Signed-off-by: Aaron Lu Cc: Signed-off-by: Linus Walleij --- drivers/gpio/gpio-crystalcove.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index cf28ec525e93..2c39811366bf 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -256,6 +256,7 @@ static struct irq_chip crystalcove_irqchip = { .irq_set_type = crystalcove_irq_type, .irq_bus_lock = crystalcove_bus_lock, .irq_bus_sync_unlock = crystalcove_bus_sync_unlock, + .flags = IRQCHIP_SKIP_SET_WAKE, }; static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) -- cgit v1.2.3-55-g7522 From 81d49ce8a8c1978009ec02963d1b0926a1d5f1d4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 28 May 2015 17:26:10 +0800 Subject: gpio: tb10x: Drop unneeded free_irq() call Current code uses devm_request_irq() in .probe, so drop the unneeded free_irq() call in .remove. Signed-off-by: Axel Lin Acked-by: Christian Ruppert Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tb10x.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 46b89614aa91..12c99d969b98 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -292,7 +292,6 @@ static int tb10x_gpio_remove(struct platform_device *pdev) BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0); kfree(tb10x_gpio->domain->gc); irq_domain_remove(tb10x_gpio->domain); - free_irq(tb10x_gpio->irq, tb10x_gpio); } gpiochip_remove(&tb10x_gpio->gc); -- cgit v1.2.3-55-g7522 From 733cf014f02040b3ad5864f3fd73d50d49c84b49 Mon Sep 17 00:00:00 2001 From: Y Vo Date: Fri, 29 May 2015 16:52:41 +0700 Subject: gpio: xgene: add ACPI support for APM X-Gene GPIO standby driver Add ACPI support for APM X-Gene GPIO standby driver. Signed-off-by: Y Vo Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xgene-sb.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index a7b8f8f0bf70..d57068b9083e 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -25,8 +25,11 @@ #include #include #include +#include #include +#include "gpiolib.h" + #define XGENE_MAX_GPIO_DS 22 #define XGENE_MAX_GPIO_DS_IRQ 6 @@ -128,6 +131,11 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev) else dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n"); + if (priv->nirq > 0) { + /* Register interrupt handlers for gpio signaled acpi events */ + acpi_gpiochip_request_interrupts(&priv->bgc.gc); + } + return ret; } @@ -135,6 +143,10 @@ static int xgene_gpio_sb_remove(struct platform_device *pdev) { struct xgene_gpio_sb *priv = platform_get_drvdata(pdev); + if (priv->nirq > 0) { + acpi_gpiochip_free_interrupts(&priv->bgc.gc); + } + return bgpio_remove(&priv->bgc); } @@ -144,10 +156,19 @@ static const struct of_device_id xgene_gpio_sb_of_match[] = { }; MODULE_DEVICE_TABLE(of, xgene_gpio_sb_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_gpio_sb_acpi_match[] = { + {"APMC0D15", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, xgene_gpio_sb_acpi_match); +#endif + static struct platform_driver xgene_gpio_sb_driver = { .driver = { .name = "xgene-gpio-sb", .of_match_table = xgene_gpio_sb_of_match, + .acpi_match_table = ACPI_PTR(xgene_gpio_sb_acpi_match), }, .probe = xgene_gpio_sb_probe, .remove = xgene_gpio_sb_remove, -- cgit v1.2.3-55-g7522 From 3b0213d56eb7f78c9a0e78dfcd9ec077bb1cf4e9 Mon Sep 17 00:00:00 2001 From: Gregory Fong Date: Thu, 28 May 2015 19:14:05 -0700 Subject: gpio: Add GPIO support for Broadcom STB SoCs This adds support for the GPIO IP "UPG GIO" used on Broadcom STB SoCs (BCM7XXX and some others). Uses basic_mmio_gpio to instantiate a gpio_chip for each bank. The driver assumes that it handles the base set of GPIOs on the system and that it can start its numbering sequence from 0, so any GPIO expanders used with it must dynamically assign GPIO numbers after this driver has finished registering its GPIOs. Does not implement the interrupt-controller portion yet, will be done in a future commit. v2: - change include to use instead of - get rid of unnecessary imask member in struct bank - rename GPIO_PER_BANK -> MAX_GPIO_PER_BANK - always have 32 GPIOs per bank and add 'width' member in struct bank to hold actual number of GPIOs in use - mark of_match table as const List-usage-fixed-by: Brian Norris Signed-off-by: Gregory Fong Signed-off-by: Linus Walleij --- MAINTAINERS | 7 ++ drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-brcmstb.c | 252 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 drivers/gpio/gpio-brcmstb.c (limited to 'drivers/gpio') diff --git a/MAINTAINERS b/MAINTAINERS index 590304b96b03..260cd95f9189 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2241,6 +2241,13 @@ N: bcm9583* N: bcm583* N: bcm113* +BROADCOM BRCMSTB GPIO DRIVER +M: Gregory Fong +L: bcm-kernel-feedback-list@broadcom.com> +S: Supported +F: drivers/gpio/gpio-brcmstb.c +F: Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt + BROADCOM KONA GPIO DRIVER M: Ray Jui L: bcm-kernel-feedback-list@broadcom.com diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5faaf5ff0d4d..d86de6ae0a0e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -126,6 +126,14 @@ config GPIO_BCM_KONA help Turn on GPIO support for Broadcom "Kona" chips. +config GPIO_BRCMSTB + tristate "BRCMSTB GPIO support" + default y if ARCH_BRCMSTB + depends on OF_GPIO && (ARCH_BRCMSTB || COMPILE_TEST) + select GPIO_GENERIC + help + Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs. + config GPIO_CLPS711X tristate "CLPS711X GPIO support" depends on ARCH_CLPS711X || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1b55fdae6dd3..893bbffc313a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o +obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c new file mode 100644 index 000000000000..7a3cb1fa0a76 --- /dev/null +++ b/drivers/gpio/gpio-brcmstb.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * 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 the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define GIO_BANK_SIZE 0x20 +#define GIO_ODEN(bank) (((bank) * GIO_BANK_SIZE) + 0x00) +#define GIO_DATA(bank) (((bank) * GIO_BANK_SIZE) + 0x04) +#define GIO_IODIR(bank) (((bank) * GIO_BANK_SIZE) + 0x08) +#define GIO_EC(bank) (((bank) * GIO_BANK_SIZE) + 0x0c) +#define GIO_EI(bank) (((bank) * GIO_BANK_SIZE) + 0x10) +#define GIO_MASK(bank) (((bank) * GIO_BANK_SIZE) + 0x14) +#define GIO_LEVEL(bank) (((bank) * GIO_BANK_SIZE) + 0x18) +#define GIO_STAT(bank) (((bank) * GIO_BANK_SIZE) + 0x1c) + +struct brcmstb_gpio_bank { + struct list_head node; + int id; + struct bgpio_chip bgc; + struct brcmstb_gpio_priv *parent_priv; + u32 width; +}; + +struct brcmstb_gpio_priv { + struct list_head bank_list; + void __iomem *reg_base; + int num_banks; + struct platform_device *pdev; + int gpio_base; +}; + +#define MAX_GPIO_PER_BANK 32 +#define GPIO_BANK(gpio) ((gpio) >> 5) +/* assumes MAX_GPIO_PER_BANK is a multiple of 2 */ +#define GPIO_BIT(gpio) ((gpio) & (MAX_GPIO_PER_BANK - 1)) + +static inline struct brcmstb_gpio_bank * +brcmstb_gpio_gc_to_bank(struct gpio_chip *gc) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + return container_of(bgc, struct brcmstb_gpio_bank, bgc); +} + +static inline struct brcmstb_gpio_priv * +brcmstb_gpio_gc_to_priv(struct gpio_chip *gc) +{ + struct brcmstb_gpio_bank *bank = brcmstb_gpio_gc_to_bank(gc); + return bank->parent_priv; +} + +/* Make sure that the number of banks matches up between properties */ +static int brcmstb_gpio_sanity_check_banks(struct device *dev, + struct device_node *np, struct resource *res) +{ + int res_num_banks = resource_size(res) / GIO_BANK_SIZE; + int num_banks = + of_property_count_u32_elems(np, "brcm,gpio-bank-widths"); + + if (res_num_banks != num_banks) { + dev_err(dev, "Mismatch in banks: res had %d, bank-widths had %d\n", + res_num_banks, num_banks); + return -EINVAL; + } else { + return 0; + } +} + +static int brcmstb_gpio_remove(struct platform_device *pdev) +{ + struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev); + struct list_head *pos; + struct brcmstb_gpio_bank *bank; + int ret = 0; + + list_for_each(pos, &priv->bank_list) { + bank = list_entry(pos, struct brcmstb_gpio_bank, node); + ret = bgpio_remove(&bank->bgc); + if (ret) + dev_err(&pdev->dev, "gpiochip_remove fail in cleanup"); + } + return ret; +} + +static int brcmstb_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); + struct brcmstb_gpio_bank *bank = brcmstb_gpio_gc_to_bank(gc); + int offset; + + if (gc->of_gpio_n_cells != 2) { + WARN_ON(1); + return -EINVAL; + } + + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + return -EINVAL; + + offset = gpiospec->args[0] - (gc->base - priv->gpio_base); + if (offset >= gc->ngpio) + return -EINVAL; + + if (unlikely(offset >= bank->width)) { + dev_warn_ratelimited(&priv->pdev->dev, + "Received request for invalid GPIO offset %d\n", + gpiospec->args[0]); + } + + if (flags) + *flags = gpiospec->args[1]; + + return offset; +} + +static int brcmstb_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *reg_base; + struct brcmstb_gpio_priv *priv; + struct resource *res; + struct property *prop; + const __be32 *p; + u32 bank_width; + int err; + static int gpio_base; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(reg_base)) + return PTR_ERR(reg_base); + + priv->gpio_base = gpio_base; + priv->reg_base = reg_base; + priv->pdev = pdev; + + INIT_LIST_HEAD(&priv->bank_list); + if (brcmstb_gpio_sanity_check_banks(dev, np, res)) + return -EINVAL; + + of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p, + bank_width) { + struct brcmstb_gpio_bank *bank; + struct bgpio_chip *bgc; + struct gpio_chip *gc; + + bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL); + if (!bank) { + err = -ENOMEM; + goto fail; + } + + bank->parent_priv = priv; + bank->id = priv->num_banks; + if (bank_width <= 0 || bank_width > MAX_GPIO_PER_BANK) { + dev_err(dev, "Invalid bank width %d\n", bank_width); + goto fail; + } else { + bank->width = bank_width; + } + + /* + * Regs are 4 bytes wide, have data reg, no set/clear regs, + * and direction bits have 0 = output and 1 = input + */ + bgc = &bank->bgc; + err = bgpio_init(bgc, dev, 4, + reg_base + GIO_DATA(bank->id), + NULL, NULL, NULL, + reg_base + GIO_IODIR(bank->id), 0); + if (err) { + dev_err(dev, "bgpio_init() failed\n"); + goto fail; + } + + gc = &bgc->gc; + gc->of_node = np; + gc->owner = THIS_MODULE; + gc->label = np->full_name; + gc->base = gpio_base; + gc->of_gpio_n_cells = 2; + gc->of_xlate = brcmstb_gpio_of_xlate; + /* not all ngpio lines are valid, will use bank width later */ + gc->ngpio = MAX_GPIO_PER_BANK; + + err = gpiochip_add(gc); + if (err) { + dev_err(dev, "Could not add gpiochip for bank %d\n", + bank->id); + goto fail; + } + gpio_base += gc->ngpio; + dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, + gc->base, gc->ngpio, bank->width); + + /* Everything looks good, so add bank to list */ + list_add(&bank->node, &priv->bank_list); + + priv->num_banks++; + } + + dev_info(dev, "Registered %d banks (GPIO(s): %d-%d)\n", + priv->num_banks, priv->gpio_base, gpio_base - 1); + + platform_set_drvdata(pdev, priv); + + return 0; + +fail: + (void) brcmstb_gpio_remove(pdev); + return err; +} + +static const struct of_device_id brcmstb_gpio_of_match[] = { + { .compatible = "brcm,brcmstb-gpio" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, brcmstb_gpio_of_match); + +static struct platform_driver brcmstb_gpio_driver = { + .driver = { + .name = "brcmstb-gpio", + .of_match_table = brcmstb_gpio_of_match, + }, + .probe = brcmstb_gpio_probe, + .remove = brcmstb_gpio_remove, +}; +module_platform_driver(brcmstb_gpio_driver); + +MODULE_AUTHOR("Gregory Fong"); +MODULE_DESCRIPTION("Driver for Broadcom BRCMSTB SoC UPG GPIO"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-55-g7522 From d342571efea8135dcf0a96dcb9e54759adefdb27 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Sat, 6 Jun 2015 22:30:40 +0200 Subject: gpio: add ETRAXFS GPIO driver Add a GPIO driver for the General I/O block on Axis ETRAX FS SoCs. Signed-off-by: Rabin Vincent Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/gpio-etraxfs.txt | 21 +++ drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-etraxfs.c | 176 +++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-etraxfs.txt create mode 100644 drivers/gpio/gpio-etraxfs.c (limited to 'drivers/gpio') diff --git a/Documentation/devicetree/bindings/gpio/gpio-etraxfs.txt b/Documentation/devicetree/bindings/gpio/gpio-etraxfs.txt new file mode 100644 index 000000000000..abf4db736c6e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-etraxfs.txt @@ -0,0 +1,21 @@ +Axis ETRAX FS General I/O controller bindings + +Required properties: + +- compatible: + - "axis,etraxfs-gio" +- reg: Physical base address and length of the controller's registers. +- #gpio-cells: Should be 3 + - The first cell is the gpio offset number. + - The second cell is reserved and is currently unused. + - The third cell is the port number (hex). +- gpio-controller: Marks the device node as a GPIO controller. + +Example: + + gio: gpio@b001a000 { + compatible = "axis,etraxfs-gio"; + reg = <0xb001a000 0x1000>; + gpio-controller; + #gpio-cells = <3>; + }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d86de6ae0a0e..ab9084648509 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -167,6 +167,14 @@ config GPIO_EP93XX depends on ARCH_EP93XX select GPIO_GENERIC +config GPIO_ETRAXFS + bool "Axis ETRAX FS General I/O" + depends on CRIS || COMPILE_TEST + depends on OF + select GPIO_GENERIC + help + Say yes here to support the GPIO controller on Axis ETRAX FS SoCs. + config GPIO_F7188X tristate "F71869, F71869A, F71882FG and F71889F GPIO support" depends on X86 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 893bbffc313a..f82cd678ce08 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o +obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o diff --git a/drivers/gpio/gpio-etraxfs.c b/drivers/gpio/gpio-etraxfs.c new file mode 100644 index 000000000000..28071f4a5672 --- /dev/null +++ b/drivers/gpio/gpio-etraxfs.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include + +#define ETRAX_FS_rw_pa_dout 0 +#define ETRAX_FS_r_pa_din 4 +#define ETRAX_FS_rw_pa_oe 8 +#define ETRAX_FS_rw_intr_cfg 12 +#define ETRAX_FS_rw_intr_mask 16 +#define ETRAX_FS_rw_ack_intr 20 +#define ETRAX_FS_r_intr 24 +#define ETRAX_FS_rw_pb_dout 32 +#define ETRAX_FS_r_pb_din 36 +#define ETRAX_FS_rw_pb_oe 40 +#define ETRAX_FS_rw_pc_dout 48 +#define ETRAX_FS_r_pc_din 52 +#define ETRAX_FS_rw_pc_oe 56 +#define ETRAX_FS_rw_pd_dout 64 +#define ETRAX_FS_r_pd_din 68 +#define ETRAX_FS_rw_pd_oe 72 +#define ETRAX_FS_rw_pe_dout 80 +#define ETRAX_FS_r_pe_din 84 +#define ETRAX_FS_rw_pe_oe 88 + +struct etraxfs_gpio_port { + const char *label; + unsigned int oe; + unsigned int dout; + unsigned int din; + unsigned int ngpio; +}; + +struct etraxfs_gpio_info { + unsigned int num_ports; + const struct etraxfs_gpio_port *ports; +}; + +static const struct etraxfs_gpio_port etraxfs_gpio_etraxfs_ports[] = { + { + .label = "A", + .ngpio = 8, + .oe = ETRAX_FS_rw_pa_oe, + .dout = ETRAX_FS_rw_pa_dout, + .din = ETRAX_FS_r_pa_din, + }, + { + .label = "B", + .ngpio = 18, + .oe = ETRAX_FS_rw_pb_oe, + .dout = ETRAX_FS_rw_pb_dout, + .din = ETRAX_FS_r_pb_din, + }, + { + .label = "C", + .ngpio = 18, + .oe = ETRAX_FS_rw_pc_oe, + .dout = ETRAX_FS_rw_pc_dout, + .din = ETRAX_FS_r_pc_din, + }, + { + .label = "D", + .ngpio = 18, + .oe = ETRAX_FS_rw_pd_oe, + .dout = ETRAX_FS_rw_pd_dout, + .din = ETRAX_FS_r_pd_din, + }, + { + .label = "E", + .ngpio = 18, + .oe = ETRAX_FS_rw_pe_oe, + .dout = ETRAX_FS_rw_pe_dout, + .din = ETRAX_FS_r_pe_din, + }, +}; + +static const struct etraxfs_gpio_info etraxfs_gpio_etraxfs = { + .num_ports = ARRAY_SIZE(etraxfs_gpio_etraxfs_ports), + .ports = etraxfs_gpio_etraxfs_ports, +}; + +static int etraxfs_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + /* + * Port numbers are A to E, and the properties are integers, so we + * specify them as 0xA - 0xE. + */ + if (gc->label[0] - 'A' + 0xA != gpiospec->args[2]) + return -EINVAL; + + return of_gpio_simple_xlate(gc, gpiospec, flags); +} + +static const struct of_device_id etraxfs_gpio_of_table[] = { + { + .compatible = "axis,etraxfs-gio", + .data = &etraxfs_gpio_etraxfs, + }, + {}, +}; + +static int etraxfs_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct etraxfs_gpio_info *info; + const struct of_device_id *match; + struct bgpio_chip *chips; + struct resource *res; + void __iomem *regs; + int ret; + int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (!regs) + return -ENOMEM; + + match = of_match_node(etraxfs_gpio_of_table, dev->of_node); + if (!match) + return -EINVAL; + + info = match->data; + + chips = devm_kzalloc(dev, sizeof(*chips) * info->num_ports, GFP_KERNEL); + if (!chips) + return -ENOMEM; + + for (i = 0; i < info->num_ports; i++) { + struct bgpio_chip *bgc = &chips[i]; + const struct etraxfs_gpio_port *port = &info->ports[i]; + + ret = bgpio_init(bgc, dev, 4, + regs + port->din, /* dat */ + regs + port->dout, /* set */ + NULL, /* clr */ + regs + port->oe, /* dirout */ + NULL, /* dirin */ + BGPIOF_UNREADABLE_REG_SET); + if (ret) + return ret; + + bgc->gc.ngpio = port->ngpio; + bgc->gc.label = port->label; + + bgc->gc.of_node = dev->of_node; + bgc->gc.of_gpio_n_cells = 3; + bgc->gc.of_xlate = etraxfs_gpio_of_xlate; + + ret = gpiochip_add(&bgc->gc); + if (ret) + dev_err(dev, "Unable to register port %s\n", + bgc->gc.label); + } + + return 0; +} + +static struct platform_driver etraxfs_gpio_driver = { + .driver = { + .name = "etraxfs-gpio", + .of_match_table = of_match_ptr(etraxfs_gpio_of_table), + }, + .probe = etraxfs_gpio_probe, +}; + +static int __init etraxfs_gpio_init(void) +{ + return platform_driver_register(&etraxfs_gpio_driver); +} + +device_initcall(etraxfs_gpio_init); -- cgit v1.2.3-55-g7522 From bdf7a4ae371894b4dc10b5820006b0a82d484929 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Thu, 4 Jun 2015 17:40:32 +0530 Subject: gpio: Added support to Zynq Ultrascale+ MPSoC Added support to Zynq Ultrascale+ MPSoC on the existing zynq gpio driver. Signed-off-by: Anurag Kumar Vulisha Acked-by: Michal Simek Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/gpio-zynq.txt | 2 +- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-zynq.c | 191 ++++++++++++++------- 3 files changed, 127 insertions(+), 68 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt index 986371a4be2c..db4c6a663c03 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt @@ -6,7 +6,7 @@ Required properties: - First cell is the GPIO line number - Second cell is used to specify optional parameters (unused) -- compatible : Should be "xlnx,zynq-gpio-1.0" +- compatible : Should be "xlnx,zynq-gpio-1.0" or "xlnx,zynqmp-gpio-1.0" - clocks : Clock specifier (see clock bindings for details) - gpio-controller : Marks the device node as a GPIO controller. - interrupts : Interrupt specifier (see interrupt bindings for diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ab9084648509..8f1fe739c985 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -540,7 +540,7 @@ config GPIO_ZEVIO config GPIO_ZYNQ tristate "Xilinx Zynq GPIO support" - depends on ARCH_ZYNQ + depends on ARCH_ZYNQ || ARCH_ZYNQMP select GPIOLIB_IRQCHIP help Say yes here to support Xilinx Zynq GPIO controller. diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 0d9663f53834..2e87c4b8da26 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -18,34 +18,47 @@ #include #include #include +#include #define DRIVER_NAME "zynq-gpio" /* Maximum banks */ #define ZYNQ_GPIO_MAX_BANK 4 +#define ZYNQMP_GPIO_MAX_BANK 6 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 #define ZYNQ_GPIO_BANK2_NGPIO 32 #define ZYNQ_GPIO_BANK3_NGPIO 32 -#define ZYNQ_GPIO_NR_GPIOS (ZYNQ_GPIO_BANK0_NGPIO + \ - ZYNQ_GPIO_BANK1_NGPIO + \ - ZYNQ_GPIO_BANK2_NGPIO + \ - ZYNQ_GPIO_BANK3_NGPIO) - -#define ZYNQ_GPIO_BANK0_PIN_MIN 0 -#define ZYNQ_GPIO_BANK0_PIN_MAX (ZYNQ_GPIO_BANK0_PIN_MIN + \ - ZYNQ_GPIO_BANK0_NGPIO - 1) -#define ZYNQ_GPIO_BANK1_PIN_MIN (ZYNQ_GPIO_BANK0_PIN_MAX + 1) -#define ZYNQ_GPIO_BANK1_PIN_MAX (ZYNQ_GPIO_BANK1_PIN_MIN + \ - ZYNQ_GPIO_BANK1_NGPIO - 1) -#define ZYNQ_GPIO_BANK2_PIN_MIN (ZYNQ_GPIO_BANK1_PIN_MAX + 1) -#define ZYNQ_GPIO_BANK2_PIN_MAX (ZYNQ_GPIO_BANK2_PIN_MIN + \ - ZYNQ_GPIO_BANK2_NGPIO - 1) -#define ZYNQ_GPIO_BANK3_PIN_MIN (ZYNQ_GPIO_BANK2_PIN_MAX + 1) -#define ZYNQ_GPIO_BANK3_PIN_MAX (ZYNQ_GPIO_BANK3_PIN_MIN + \ - ZYNQ_GPIO_BANK3_NGPIO - 1) +#define ZYNQMP_GPIO_BANK0_NGPIO 26 +#define ZYNQMP_GPIO_BANK1_NGPIO 26 +#define ZYNQMP_GPIO_BANK2_NGPIO 26 +#define ZYNQMP_GPIO_BANK3_NGPIO 32 +#define ZYNQMP_GPIO_BANK4_NGPIO 32 +#define ZYNQMP_GPIO_BANK5_NGPIO 32 + +#define ZYNQ_GPIO_NR_GPIOS 118 +#define ZYNQMP_GPIO_NR_GPIOS 174 + +#define ZYNQ_GPIO_BANK0_PIN_MIN(str) 0 +#define ZYNQ_GPIO_BANK0_PIN_MAX(str) (ZYNQ_GPIO_BANK0_PIN_MIN(str) + \ + ZYNQ##str##_GPIO_BANK0_NGPIO - 1) +#define ZYNQ_GPIO_BANK1_PIN_MIN(str) (ZYNQ_GPIO_BANK0_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK1_PIN_MAX(str) (ZYNQ_GPIO_BANK1_PIN_MIN(str) + \ + ZYNQ##str##_GPIO_BANK1_NGPIO - 1) +#define ZYNQ_GPIO_BANK2_PIN_MIN(str) (ZYNQ_GPIO_BANK1_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK2_PIN_MAX(str) (ZYNQ_GPIO_BANK2_PIN_MIN(str) + \ + ZYNQ##str##_GPIO_BANK2_NGPIO - 1) +#define ZYNQ_GPIO_BANK3_PIN_MIN(str) (ZYNQ_GPIO_BANK2_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK3_PIN_MAX(str) (ZYNQ_GPIO_BANK3_PIN_MIN(str) + \ + ZYNQ##str##_GPIO_BANK3_NGPIO - 1) +#define ZYNQ_GPIO_BANK4_PIN_MIN(str) (ZYNQ_GPIO_BANK3_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK4_PIN_MAX(str) (ZYNQ_GPIO_BANK4_PIN_MIN(str) + \ + ZYNQ##str##_GPIO_BANK4_NGPIO - 1) +#define ZYNQ_GPIO_BANK5_PIN_MIN(str) (ZYNQ_GPIO_BANK4_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK5_PIN_MAX(str) (ZYNQ_GPIO_BANK5_PIN_MIN(str) + \ + ZYNQ##str##_GPIO_BANK5_NGPIO - 1) /* Register offsets for the GPIO device */ @@ -89,12 +102,30 @@ * @base_addr: base address of the GPIO device * @clk: clock resource for this controller * @irq: interrupt for the GPIO device + * @p_data: pointer to platform data */ struct zynq_gpio { struct gpio_chip chip; void __iomem *base_addr; struct clk *clk; int irq; + const struct zynq_platform_data *p_data; +}; + +/** + * struct zynq_platform_data - zynq gpio platform data structure + * @label: string to store in gpio->label + * @ngpio: max number of gpio pins + * @max_bank: maximum number of gpio banks + * @bank_min: this array represents bank's min pin + * @bank_max: this array represents bank's max pin +*/ +struct zynq_platform_data { + const char *label; + u16 ngpio; + int max_bank; + int bank_min[ZYNQMP_GPIO_MAX_BANK]; + int bank_max[ZYNQMP_GPIO_MAX_BANK]; }; static struct irq_chip zynq_gpio_level_irqchip; @@ -112,39 +143,26 @@ static struct irq_chip zynq_gpio_edge_irqchip; */ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num, unsigned int *bank_num, - unsigned int *bank_pin_num) + unsigned int *bank_pin_num, + struct zynq_gpio *gpio) { - switch (pin_num) { - case ZYNQ_GPIO_BANK0_PIN_MIN ... ZYNQ_GPIO_BANK0_PIN_MAX: - *bank_num = 0; - *bank_pin_num = pin_num; - break; - case ZYNQ_GPIO_BANK1_PIN_MIN ... ZYNQ_GPIO_BANK1_PIN_MAX: - *bank_num = 1; - *bank_pin_num = pin_num - ZYNQ_GPIO_BANK1_PIN_MIN; - break; - case ZYNQ_GPIO_BANK2_PIN_MIN ... ZYNQ_GPIO_BANK2_PIN_MAX: - *bank_num = 2; - *bank_pin_num = pin_num - ZYNQ_GPIO_BANK2_PIN_MIN; - break; - case ZYNQ_GPIO_BANK3_PIN_MIN ... ZYNQ_GPIO_BANK3_PIN_MAX: - *bank_num = 3; - *bank_pin_num = pin_num - ZYNQ_GPIO_BANK3_PIN_MIN; - break; - default: - WARN(true, "invalid GPIO pin number: %u", pin_num); - *bank_num = 0; - *bank_pin_num = 0; - break; + int bank; + + for (bank = 0; bank < gpio->p_data->max_bank; bank++) { + if ((pin_num >= gpio->p_data->bank_min[bank]) && + (pin_num <= gpio->p_data->bank_max[bank])) { + *bank_num = bank; + *bank_pin_num = pin_num - + gpio->p_data->bank_min[bank]; + return; + } } -} -static const unsigned int zynq_gpio_bank_offset[] = { - ZYNQ_GPIO_BANK0_PIN_MIN, - ZYNQ_GPIO_BANK1_PIN_MIN, - ZYNQ_GPIO_BANK2_PIN_MIN, - ZYNQ_GPIO_BANK3_PIN_MIN, -}; + /* default */ + WARN(true, "invalid GPIO pin number: %u", pin_num); + *bank_num = 0; + *bank_pin_num = 0; +} /** * zynq_gpio_get_value - Get the state of the specified pin of GPIO device @@ -161,7 +179,7 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin) unsigned int bank_num, bank_pin_num; struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); - zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); data = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); @@ -185,7 +203,7 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, unsigned int reg_offset, bank_num, bank_pin_num; struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); - zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); if (bank_pin_num >= ZYNQ_GPIO_MID_PIN_NUM) { /* only 16 data bits in bit maskable reg */ @@ -222,7 +240,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) unsigned int bank_num, bank_pin_num; struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); - zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); /* bank 0 pins 7 and 8 are special and cannot be used as inputs */ if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8)) @@ -255,7 +273,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, unsigned int bank_num, bank_pin_num; struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); - zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); /* set the GPIO pin as output */ reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); @@ -286,7 +304,7 @@ static void zynq_gpio_irq_mask(struct irq_data *irq_data) struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data); device_pin_num = irq_data->hwirq; - zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio); writel_relaxed(BIT(bank_pin_num), gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num)); } @@ -306,7 +324,7 @@ static void zynq_gpio_irq_unmask(struct irq_data *irq_data) struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data); device_pin_num = irq_data->hwirq; - zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio); writel_relaxed(BIT(bank_pin_num), gpio->base_addr + ZYNQ_GPIO_INTEN_OFFSET(bank_num)); } @@ -325,7 +343,7 @@ static void zynq_gpio_irq_ack(struct irq_data *irq_data) struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data); device_pin_num = irq_data->hwirq; - zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio); writel_relaxed(BIT(bank_pin_num), gpio->base_addr + ZYNQ_GPIO_INTSTS_OFFSET(bank_num)); } @@ -375,7 +393,7 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type) struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data); device_pin_num = irq_data->hwirq; - zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num); + zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio); int_type = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTTYPE_OFFSET(bank_num)); @@ -470,7 +488,7 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio, unsigned int bank_num, unsigned long pending) { - unsigned int bank_offset = zynq_gpio_bank_offset[bank_num]; + unsigned int bank_offset = gpio->p_data->bank_min[bank_num]; struct irq_domain *irqdomain = gpio->chip.irqdomain; int offset; @@ -505,7 +523,7 @@ static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc) chained_irq_enter(irqchip, desc); - for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++) { + for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) { int_sts = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTSTS_OFFSET(bank_num)); int_enb = readl_relaxed(gpio->base_addr + @@ -582,6 +600,46 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { zynq_gpio_runtime_resume, NULL) }; +static const struct zynq_platform_data zynqmp_gpio_def = { + .label = "zynqmp_gpio", + .ngpio = ZYNQMP_GPIO_NR_GPIOS, + .max_bank = ZYNQMP_GPIO_MAX_BANK, + .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(MP), + .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(MP), + .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(MP), + .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(MP), + .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(MP), + .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(MP), + .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(MP), + .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(MP), + .bank_min[4] = ZYNQ_GPIO_BANK4_PIN_MIN(MP), + .bank_max[4] = ZYNQ_GPIO_BANK4_PIN_MAX(MP), + .bank_min[5] = ZYNQ_GPIO_BANK5_PIN_MIN(MP), + .bank_max[5] = ZYNQ_GPIO_BANK5_PIN_MAX(MP), +}; + +static const struct zynq_platform_data zynq_gpio_def = { + .label = "zynq_gpio", + .ngpio = ZYNQ_GPIO_NR_GPIOS, + .max_bank = ZYNQ_GPIO_MAX_BANK, + .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(), + .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(), + .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(), + .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(), + .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(), + .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(), + .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(), + .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(), +}; + +static const struct of_device_id zynq_gpio_of_match[] = { + { .compatible = "xlnx,zynq-gpio-1.0", .data = (void *)&zynq_gpio_def }, + { .compatible = "xlnx,zynqmp-gpio-1.0", + .data = (void *)&zynqmp_gpio_def }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); + /** * zynq_gpio_probe - Initialization method for a zynq_gpio device * @pdev: platform device instance @@ -599,11 +657,18 @@ static int zynq_gpio_probe(struct platform_device *pdev) struct zynq_gpio *gpio; struct gpio_chip *chip; struct resource *res; + const struct of_device_id *match; gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) return -ENOMEM; + match = of_match_node(zynq_gpio_of_match, pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "of_match_node() failed\n"); + return -EINVAL; + } + gpio->p_data = match->data; platform_set_drvdata(pdev, gpio); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -619,7 +684,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) /* configure the gpio chip */ chip = &gpio->chip; - chip->label = "zynq_gpio"; + chip->label = gpio->p_data->label; chip->owner = THIS_MODULE; chip->dev = &pdev->dev; chip->get = zynq_gpio_get_value; @@ -629,7 +694,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) chip->direction_input = zynq_gpio_dir_in; chip->direction_output = zynq_gpio_dir_out; chip->base = -1; - chip->ngpio = ZYNQ_GPIO_NR_GPIOS; + chip->ngpio = gpio->p_data->ngpio; /* Enable GPIO clock */ gpio->clk = devm_clk_get(&pdev->dev, NULL); @@ -651,7 +716,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) } /* disable interrupts for all banks */ - for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++) + for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num)); @@ -695,12 +760,6 @@ static int zynq_gpio_remove(struct platform_device *pdev) return 0; } -static struct of_device_id zynq_gpio_of_match[] = { - { .compatible = "xlnx,zynq-gpio-1.0", }, - { /* end of table */ } -}; -MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); - static struct platform_driver zynq_gpio_driver = { .driver = { .name = DRIVER_NAME, -- cgit v1.2.3-55-g7522 From 8405f2089488c39b10e63b6522fcabc60fbd025a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 5 Jun 2015 11:10:21 +0200 Subject: gpio: promote own request failure to pr_err() These error messages are helpful to see that we fail to get hogs. Promote them to real errors so they appear in the boot crawl. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8dfb54f92594..07b83a9e9821 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2131,13 +2131,13 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, local_desc = gpiochip_request_own_desc(chip, hwnum, name); if (IS_ERR(local_desc)) { - pr_debug("requesting own GPIO %s failed\n", name); + pr_err("requesting own GPIO %s failed\n", name); return PTR_ERR(local_desc); } status = gpiod_configure_flags(desc, name, lflags, dflags); if (status < 0) { - pr_debug("setup of GPIO %s failed\n", name); + pr_err("setup of GPIO %s failed\n", name); gpiochip_free_own_desc(desc); return status; } -- cgit v1.2.3-55-g7522 From a713890d32900459031d344748a23ca37758bec5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 5 Jun 2015 11:36:10 +0200 Subject: gpio: improve error reporting on own descriptors When requesting own descriptors through hogs, it is useful to get some details about what's going on if we encounter problems. Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 07b83a9e9821..7f87b9baa905 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2131,13 +2131,15 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, local_desc = gpiochip_request_own_desc(chip, hwnum, name); if (IS_ERR(local_desc)) { - pr_err("requesting own GPIO %s failed\n", name); + pr_err("requesting hog GPIO %s (chip %s, offset %d) failed\n", + name, chip->label, hwnum); return PTR_ERR(local_desc); } status = gpiod_configure_flags(desc, name, lflags, dflags); if (status < 0) { - pr_err("setup of GPIO %s failed\n", name); + pr_err("setup of hog GPIO %s (chip %s, offset %d) failed\n", + name, chip->label, hwnum); gpiochip_free_own_desc(desc); return status; } -- cgit v1.2.3-55-g7522 From 2b528fff0905497c96566d671d47f2bbf3ae295c Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Wed, 10 Jun 2015 17:12:07 +0800 Subject: GPIO / ACPI: export acpi_gpiochip_request(free)_interrupts for module use acpi_gpiochip_request(free)_interrupts can be used for modules, so export them. This also fixs a compile error when xgene-sb configured as kernel module. Fixes: 733cf014f020 "gpio: xgene: add ACPI support for APM X-Gene GPIO standby driver" Reviewed-by: Mark Brown Signed-off-by: Hanjun Guo --- drivers/gpio/gpiolib-acpi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 19b99d0c2bf0..980a996d0169 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -307,6 +307,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) acpi_walk_resources(handle, "_AEI", acpi_gpiochip_request_interrupt, acpi_gpio); } +EXPORT_SYMBOL_GPL(acpi_gpiochip_request_interrupts); /** * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts. @@ -346,6 +347,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) kfree(event); } } +EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts); int acpi_dev_add_driver_gpios(struct acpi_device *adev, const struct acpi_gpio_mapping *gpios) -- cgit v1.2.3-55-g7522 From f35bbf61ab772cc312f64dddd1c89ab161893cee Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 10 Jun 2015 16:05:05 +0300 Subject: gpio / ACPI: Return -EPROBE_DEFER if the gpiochip was not found If a driver requests a GPIO described in its _CRS but the GPIO host controller (gpiochip) driver providing the GPIO has not been loaded yet acpi_get_gpiod() returns -ENODEV which causes the calling driver to fail. If the gpiochip driver is loaded afterwards the driver requesting the GPIO will not notice this. Better approach is to return -EPROBE_DEFER in such case. Then when the gpiochip driver appears the driver requesting the GPIO will be probed again. This also aligns ACPI GPIO lookup code closer to DT as it does pretty much the same when no gpiochip driver was found. Reported-by: Tobias Diedrich Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Signed-off-by: Tobias Diedrich Reviewed-by: Amos Kong Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 980a996d0169..533fe5dbe6f8 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -114,10 +114,11 @@ static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1") * @pin: ACPI GPIO pin number (0-based, controller-relative) * - * Returns GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR - * error value + * Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR + * error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO + * controller does not have gpiochip registered at the moment. This is to + * support probe deferral. */ - static struct gpio_desc *acpi_get_gpiod(char *path, int pin) { struct gpio_chip *chip; @@ -131,7 +132,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin) chip = gpiochip_find(handle, acpi_gpiochip_find); if (!chip) - return ERR_PTR(-ENODEV); + return ERR_PTR(-EPROBE_DEFER); offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin); if (offset < 0) -- cgit v1.2.3-55-g7522 From 84f28998cc96546bb0733d2c238b23f38442ed89 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 11 Jun 2015 00:01:09 +0300 Subject: gpio: pcf857x: handle only enabled irqs Now pcf857x_irq() IRQ's dispatcher will try to run nested IRQ handlers for each GPIO pin which state has changed. Such IRQs are, actually, spurious and nested IRQ handlers have to be called only for IRQs wich were enabled by users. This is not critical issue - just /proc/interrupts will display counters for unused IRQS: 399: 4 0 pcf857x 0 Edge 428: 1 0 pcf857x 13 Edge 430: 1 0 pcf857x 15 Edge Hence, fix it by adding irq_enabled field in struct pcf857x to track enabled GPIO IRQs and corresponding callbacks in pcf857x_irq_chip. Similar functionality was presented in pcf857x driver, commit 21fd3cd1874a ('gpio: pcf857x: call the gpio user handler iff...') and then it was removed by commit a39294bdf4b0 ('gpio: pcf857x: Switch to use gpiolib irqchip...') Cc: Geert Uytterhoeven Fixes: a39294bdf4b0 ('gpio: pcf857x: Switch to use gpiolib irqchip helpers') Signed-off-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pcf857x.c | 46 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 83db0e19ec7e..404f3c61ef9b 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -92,6 +92,7 @@ struct pcf857x { unsigned out; /* software latch */ unsigned status; /* current status */ unsigned int irq_parent; + unsigned irq_enabled; /* enabled irqs */ int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -195,7 +196,7 @@ static irqreturn_t pcf857x_irq(int irq, void *data) * interrupt source, just to avoid bad irqs */ - change = (gpio->status ^ status); + change = (gpio->status ^ status) & gpio->irq_enabled; for_each_set_bit(i, &change, gpio->chip.ngpio) handle_nested_irq(irq_find_mapping(gpio->chip.irqdomain, i)); gpio->status = status; @@ -210,14 +211,10 @@ static irqreturn_t pcf857x_irq(int irq, void *data) */ static void noop(struct irq_data *data) { } -static unsigned int noop_ret(struct irq_data *data) -{ - return 0; -} - static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on) { struct pcf857x *gpio = irq_data_get_irq_chip_data(data); + int error = 0; if (gpio->irq_parent) { @@ -229,20 +226,47 @@ static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on) gpio->irq_parent = 0; } } - return error; } +static void pcf857x_irq_enable(struct irq_data *data) +{ + struct pcf857x *gpio = irq_data_get_irq_chip_data(data); + + gpio->irq_enabled |= (1 << data->hwirq); +} + +static void pcf857x_irq_disable(struct irq_data *data) +{ + struct pcf857x *gpio = irq_data_get_irq_chip_data(data); + + gpio->irq_enabled &= ~(1 << data->hwirq); +} + +static void pcf857x_irq_bus_lock(struct irq_data *data) +{ + struct pcf857x *gpio = irq_data_get_irq_chip_data(data); + + mutex_lock(&gpio->lock); +} + +static void pcf857x_irq_bus_sync_unlock(struct irq_data *data) +{ + struct pcf857x *gpio = irq_data_get_irq_chip_data(data); + + mutex_unlock(&gpio->lock); +} + static struct irq_chip pcf857x_irq_chip = { .name = "pcf857x", - .irq_startup = noop_ret, - .irq_shutdown = noop, - .irq_enable = noop, - .irq_disable = noop, + .irq_enable = pcf857x_irq_enable, + .irq_disable = pcf857x_irq_disable, .irq_ack = noop, .irq_mask = noop, .irq_unmask = noop, .irq_set_wake = pcf857x_irq_set_wake, + .irq_bus_lock = pcf857x_irq_bus_lock, + .irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock, }; /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-55-g7522 From 38e003f4b5dc405158b9ce625d8cc2b336d45497 Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Wed, 10 Jun 2015 14:26:27 +0100 Subject: gpio: Fix checkpatch.pl issues This patch fixes some issues given by checkpatch. Fixes include bracket placement, spacing and indenting. Signed-off-by: Daniel Lockyer Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpio-altera.c | 3 ++- drivers/gpio/gpio-crystalcove.c | 3 +-- drivers/gpio/gpio-f7188x.c | 4 ++-- drivers/gpio/gpio-it8761e.c | 2 +- drivers/gpio/gpiolib.c | 17 +++++++---------- 5 files changed, 13 insertions(+), 16 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 449fb46cb8a0..0f3d336d6303 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -107,7 +107,8 @@ static int altera_gpio_irq_set_type(struct irq_data *d, return -EINVAL; } -static unsigned int altera_gpio_irq_startup(struct irq_data *d) { +static unsigned int altera_gpio_irq_startup(struct irq_data *d) +{ altera_gpio_irq_unmask(d); return 0; diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 2c39811366bf..fddd204dc9b6 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -95,9 +95,8 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type) { int reg; - if (gpio == 94) { + if (gpio == 94) return GPIOPANELCTL; - } if (reg_type == CTRL_IN) { if (gpio < 8) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index dbda8433c4f7..5e3c4fa67d82 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -172,7 +172,7 @@ static struct f7188x_gpio_bank f71869a_gpio_bank[] = { }; static struct f7188x_gpio_bank f71882_gpio_bank[] = { - F7188X_GPIO_BANK(0 , 8, 0xF0), + F7188X_GPIO_BANK(0, 8, 0xF0), F7188X_GPIO_BANK(10, 8, 0xE0), F7188X_GPIO_BANK(20, 8, 0xD0), F7188X_GPIO_BANK(30, 4, 0xC0), @@ -180,7 +180,7 @@ static struct f7188x_gpio_bank f71882_gpio_bank[] = { }; static struct f7188x_gpio_bank f71889_gpio_bank[] = { - F7188X_GPIO_BANK(0 , 7, 0xF0), + F7188X_GPIO_BANK(0, 7, 0xF0), F7188X_GPIO_BANK(10, 7, 0xE0), F7188X_GPIO_BANK(20, 8, 0xD0), F7188X_GPIO_BANK(30, 8, 0xC0), diff --git a/drivers/gpio/gpio-it8761e.c b/drivers/gpio/gpio-it8761e.c index dadfc245cf09..30a8f24c92c5 100644 --- a/drivers/gpio/gpio-it8761e.c +++ b/drivers/gpio/gpio-it8761e.c @@ -123,7 +123,7 @@ static void it8761e_gpio_set(struct gpio_chip *gc, curr_vals = inb(reg); if (val) - outb(curr_vals | (1 << bit) , reg); + outb(curr_vals | (1 << bit), reg); else outb(curr_vals & ~(1 << bit), reg); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 7f87b9baa905..de56e5757d3d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1309,9 +1309,8 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, continue; } /* set outputs if the corresponding mask bit is set */ - if (__test_and_clear_bit(i, mask)) { + if (__test_and_clear_bit(i, mask)) chip->set(chip, i, test_bit(i, bits)); - } } } } @@ -1329,9 +1328,9 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep, unsigned long bits[BITS_TO_LONGS(chip->ngpio)]; int count = 0; - if (!can_sleep) { + if (!can_sleep) WARN_ON(chip->can_sleep); - } + memset(mask, 0, sizeof(mask)); do { struct gpio_desc *desc = desc_array[i]; @@ -1346,24 +1345,22 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep, * open drain and open source outputs are set individually */ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { - _gpio_set_open_drain_value(desc,value); + _gpio_set_open_drain_value(desc, value); } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { _gpio_set_open_source_value(desc, value); } else { __set_bit(hwgpio, mask); - if (value) { + if (value) __set_bit(hwgpio, bits); - } else { + else __clear_bit(hwgpio, bits); - } count++; } i++; } while ((i < array_size) && (desc_array[i]->chip == chip)); /* push collected bits to outputs */ - if (count != 0) { + if (count != 0) gpio_chip_set_multiple(chip, mask, bits); - } } } -- cgit v1.2.3-55-g7522 From e65eea54e98566de2cbb10e710873bc5e34c6680 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 16 Jun 2015 23:06:40 +0100 Subject: gpio: gpio-mxc: Fix race in installing chained IRQ handler Fix a race where a pending interrupt could be received and the handler called before the handler's data has been setup, by converting to irq_set_chained_handler_and_data(). Signed-off-by: Russell King Cc: Alexandre Courbot Cc: Hans Ulli Kroll Cc: Jason Cooper Cc: Lee Jones Cc: Linus Walleij Cc: Thierry Reding Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/E1Z4z0C-0002SX-Lj@rmk-PC.arm.linux.org.uk Signed-off-by: Thomas Gleixner --- drivers/gpio/gpio-mxc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 9f7446a7ac64..e4f42c95c320 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -437,14 +437,13 @@ static int mxc_gpio_probe(struct platform_device *pdev) irq_set_chained_handler(port->irq, mx2_gpio_irq_handler); } else { /* setup one handler for each entry */ - irq_set_chained_handler(port->irq, mx3_gpio_irq_handler); - irq_set_handler_data(port->irq, port); - if (port->irq_high > 0) { + irq_set_chained_handler_and_data(port->irq, + mx3_gpio_irq_handler, port); + if (port->irq_high > 0) /* setup handler for GPIO 16 to 31 */ - irq_set_chained_handler(port->irq_high, - mx3_gpio_irq_handler); - irq_set_handler_data(port->irq_high, port); - } + irq_set_chained_handler_and_data(port->irq_high, + mx3_gpio_irq_handler, + port); } err = bgpio_init(&port->bgc, &pdev->dev, 4, -- cgit v1.2.3-55-g7522 From a44735f421b44724d52117358573f8ded473ecc7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 16 Jun 2015 23:06:45 +0100 Subject: gpio: gpio-mxs: Fix race in installing chained IRQ handler Fix a race where a pending interrupt could be received and the handler called before the handler's data has been setup, by converting to irq_set_chained_handler_and_data(). Signed-off-by: Russell King Cc: Alexandre Courbot Cc: Hans Ulli Kroll Cc: Jason Cooper Cc: Lee Jones Cc: Linus Walleij Cc: Thierry Reding Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/E1Z4z0H-0002Sf-P9@rmk-PC.arm.linux.org.uk Signed-off-by: Thomas Gleixner --- drivers/gpio/gpio-mxs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 84cbda6acdda..eac872748ee7 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -320,8 +320,8 @@ static int mxs_gpio_probe(struct platform_device *pdev) mxs_gpio_init_gc(port, irq_base); /* setup one handler for each entry */ - irq_set_chained_handler(port->irq, mxs_gpio_irq_handler); - irq_set_handler_data(port->irq, port); + irq_set_chained_handler_and_data(port->irq, mxs_gpio_irq_handler, + port); err = bgpio_init(&port->bgc, &pdev->dev, 4, port->base + PINCTRL_DIN(port), -- cgit v1.2.3-55-g7522 From e88d251d6a4fb719f9f667a397cf5d755121e551 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 16 Jun 2015 23:06:50 +0100 Subject: gpio: gpio-tegra: Fix race in installing chained IRQ handler Fix a race where a pending interrupt could be received and the handler called before the handler's data has been setup, by converting to irq_set_chained_handler_and_data(). Signed-off-by: Russell King Cc: Alexandre Courbot Cc: Hans Ulli Kroll Cc: Jason Cooper Cc: Lee Jones Cc: Linus Walleij Cc: Thierry Reding Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/E1Z4z0M-0002Sl-Ti@rmk-PC.arm.linux.org.uk Signed-off-by: Thomas Gleixner --- drivers/gpio/gpio-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 1741981d53c8..56dcc8ed98de 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -515,8 +515,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) for (i = 0; i < tegra_gpio_bank_count; i++) { bank = &tegra_gpio_banks[i]; - irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler); - irq_set_handler_data(bank->irq, bank); + irq_set_chained_handler_and_data(bank->irq, + tegra_gpio_irq_handler, bank); for (j = 0; j < 4; j++) spin_lock_init(&bank->lvl_lock[j]); -- cgit v1.2.3-55-g7522 From c181fb3e723351e2f7a1f76b6c0627a4b8ad1723 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 22 Jun 2015 22:38:53 +0200 Subject: ACPI / OF: Rename of_node() and acpi_node() to to_of_node() and to_acpi_node() Commit 8a0662d9 introduced of_node and acpi_node symbols in global namespace but there were already ~63 of_node local variables or function parameters (no single acpi_node though, but anyway). After debugging undefined but used of_node local varible (which turned out to reference static function of_node() instead) it became clear that the names for the functions are too short and too generic for global scope. Signed-off-by: Alexander Sverdlin Signed-off-by: Rafael J. Wysocki --- drivers/base/property.c | 26 +++++++++++++------------- drivers/gpio/gpiolib.c | 4 ++-- drivers/leds/leds-gpio.c | 2 +- include/acpi/acpi_bus.h | 2 +- include/linux/acpi.h | 4 ++-- include/linux/of.h | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/base/property.c b/drivers/base/property.c index 1d0b116cae95..dfd4de69b67b 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -128,9 +128,9 @@ EXPORT_SYMBOL_GPL(device_property_present); bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) { if (is_of_node(fwnode)) - return of_property_read_bool(of_node(fwnode), propname); + return of_property_read_bool(to_of_node(fwnode), propname); else if (is_acpi_node(fwnode)) - return !acpi_dev_prop_get(acpi_node(fwnode), propname, NULL); + return !acpi_dev_prop_get(to_acpi_node(fwnode), propname, NULL); return !!pset_prop_get(to_pset(fwnode), propname); } @@ -285,10 +285,10 @@ EXPORT_SYMBOL_GPL(device_property_read_string); ({ \ int _ret_; \ if (is_of_node(_fwnode_)) \ - _ret_ = OF_DEV_PROP_READ_ARRAY(of_node(_fwnode_), _propname_, \ + _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \ _type_, _val_, _nval_); \ else if (is_acpi_node(_fwnode_)) \ - _ret_ = acpi_dev_prop_read(acpi_node(_fwnode_), _propname_, \ + _ret_ = acpi_dev_prop_read(to_acpi_node(_fwnode_), _propname_, \ _proptype_, _val_, _nval_); \ else \ _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ @@ -424,11 +424,11 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, { if (is_of_node(fwnode)) return val ? - of_property_read_string_array(of_node(fwnode), propname, - val, nval) : - of_property_count_strings(of_node(fwnode), propname); + of_property_read_string_array(to_of_node(fwnode), + propname, val, nval) : + of_property_count_strings(to_of_node(fwnode), propname); else if (is_acpi_node(fwnode)) - return acpi_dev_prop_read(acpi_node(fwnode), propname, + return acpi_dev_prop_read(to_acpi_node(fwnode), propname, DEV_PROP_STRING, val, nval); return pset_prop_read_array(to_pset(fwnode), propname, @@ -455,9 +455,9 @@ int fwnode_property_read_string(struct fwnode_handle *fwnode, const char *propname, const char **val) { if (is_of_node(fwnode)) - return of_property_read_string(of_node(fwnode), propname, val); + return of_property_read_string(to_of_node(fwnode), propname, val); else if (is_acpi_node(fwnode)) - return acpi_dev_prop_read(acpi_node(fwnode), propname, + return acpi_dev_prop_read(to_acpi_node(fwnode), propname, DEV_PROP_STRING, val, 1); return -ENXIO; @@ -475,13 +475,13 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, if (IS_ENABLED(CONFIG_OF) && dev->of_node) { struct device_node *node; - node = of_get_next_available_child(dev->of_node, of_node(child)); + node = of_get_next_available_child(dev->of_node, to_of_node(child)); if (node) return &node->fwnode; } else if (IS_ENABLED(CONFIG_ACPI)) { struct acpi_device *node; - node = acpi_get_next_child(dev, acpi_node(child)); + node = acpi_get_next_child(dev, to_acpi_node(child)); if (node) return acpi_fwnode_handle(node); } @@ -500,7 +500,7 @@ EXPORT_SYMBOL_GPL(device_get_next_child_node); void fwnode_handle_put(struct fwnode_handle *fwnode) { if (is_of_node(fwnode)) - of_node_put(of_node(fwnode)); + of_node_put(to_of_node(fwnode)); } EXPORT_SYMBOL_GPL(fwnode_handle_put); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6bc612b8a49f..5d8b2b35e2a2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2040,14 +2040,14 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, if (is_of_node(fwnode)) { enum of_gpio_flags flags; - desc = of_get_named_gpiod_flags(of_node(fwnode), propname, 0, + desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname, 0, &flags); if (!IS_ERR(desc)) active_low = flags & OF_GPIO_ACTIVE_LOW; } else if (is_acpi_node(fwnode)) { struct acpi_gpio_info info; - desc = acpi_get_gpiod_by_index(acpi_node(fwnode), propname, 0, + desc = acpi_get_gpiod_by_index(to_acpi_node(fwnode), propname, 0, &info); if (!IS_ERR(desc)) active_low = info.active_low; diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 15eb3f86f670..d2d54d62afee 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -191,7 +191,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) goto err; } - np = of_node(child); + np = to_of_node(child); if (fwnode_property_present(child, "label")) { fwnode_property_read_string(child, "label", &led.name); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8de4fa90e8c4..1224be8184fc 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -385,7 +385,7 @@ static inline bool is_acpi_node(struct fwnode_handle *fwnode) return fwnode && fwnode->type == FWNODE_ACPI; } -static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode) +static inline struct acpi_device *to_acpi_node(struct fwnode_handle *fwnode) { return is_acpi_node(fwnode) ? container_of(fwnode, struct acpi_device, fwnode) : NULL; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e35e29c..ec3c98ed9dca 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -53,7 +53,7 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) return adev ? adev->handle : NULL; } -#define ACPI_COMPANION(dev) acpi_node((dev)->fwnode) +#define ACPI_COMPANION(dev) to_acpi_node((dev)->fwnode) #define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \ acpi_fwnode_handle(adev) : NULL) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) @@ -473,7 +473,7 @@ static inline bool is_acpi_node(struct fwnode_handle *fwnode) return false; } -static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode) +static inline struct acpi_device *to_acpi_node(struct fwnode_handle *fwnode) { return NULL; } diff --git a/include/linux/of.h b/include/linux/of.h index b871ff9d81d7..f05fdcea4e66 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -128,7 +128,7 @@ static inline bool is_of_node(struct fwnode_handle *fwnode) return fwnode && fwnode->type == FWNODE_OF; } -static inline struct device_node *of_node(struct fwnode_handle *fwnode) +static inline struct device_node *to_of_node(struct fwnode_handle *fwnode) { return fwnode ? container_of(fwnode, struct device_node, fwnode) : NULL; } @@ -387,7 +387,7 @@ static inline bool is_of_node(struct fwnode_handle *fwnode) return false; } -static inline struct device_node *of_node(struct fwnode_handle *fwnode) +static inline struct device_node *to_of_node(struct fwnode_handle *fwnode) { return NULL; } -- cgit v1.2.3-55-g7522 From b34cc62084e8c44f7c85e95bfc19e21d07f25173 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 21 Jun 2015 20:16:04 +0200 Subject: gpio/bcm-kona: Fix race in installing chained IRQ handler Fix a race where a pending interrupt could be received and the handler called before the handler's data has been setup, by converting to irq_set_chained_handler_and_data(). Search and conversion was done with coccinelle: @@ expression E1, E2, E3; @@ ( -if (irq_set_chained_handler(E1, E3) != 0) - BUG(); | -irq_set_chained_handler(E1, E3); ) -irq_set_handler_data(E1, E2); +irq_set_chained_handler_and_data(E1, E3, E2); @@ expression E1, E2, E3; @@ ( -if (irq_set_chained_handler(E1, E3) != 0) - BUG(); ... | -irq_set_chained_handler(E1, E3); ... ) -irq_set_handler_data(E1, E2); +irq_set_chained_handler_and_data(E1, E3, E2); Reported-by: Russell King Signed-off-by: Thomas Gleixner Cc: Julia Lawall Cc: Ray Jui Cc: Linus Walleij Cc: Alexandre Courbot Cc: bcm-kernel-feedback-list@broadcom.com Cc: linux-gpio@vger.kernel.org --- drivers/gpio/gpio-bcm-kona.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index b164ce837b43..fc214c3dfcc4 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -651,8 +651,9 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) } for (i = 0; i < kona_gpio->num_bank; i++) { bank = &kona_gpio->banks[i]; - irq_set_chained_handler(bank->irq, bcm_kona_gpio_irq_handler); - irq_set_handler_data(bank->irq, bank); + irq_set_chained_handler_and_data(bank->irq, + bcm_kona_gpio_irq_handler, + bank); } spin_lock_init(&kona_gpio->lock); -- cgit v1.2.3-55-g7522 From 6218b88d2678523cc2f6f8e3ea1182c250d9684f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 21 Jun 2015 20:16:05 +0200 Subject: gpio/dwapb: Fix race in installing chained IRQ handler Fix a race where a pending interrupt could be received and the handler called before the handler's data has been setup, by converting to irq_set_chained_handler_and_data(). Search and conversion was done with coccinelle: @@ expression E1, E2, E3; @@ ( -if (irq_set_chained_handler(E1, E3) != 0) - BUG(); | -irq_set_chained_handler(E1, E3); ) -irq_set_handler_data(E1, E2); +irq_set_chained_handler_and_data(E1, E3, E2); @@ expression E1, E2, E3; @@ ( -if (irq_set_chained_handler(E1, E3) != 0) - BUG(); ... | -irq_set_chained_handler(E1, E3); ... ) -irq_set_handler_data(E1, E2); +irq_set_chained_handler_and_data(E1, E3, E2); Reported-by: Russell King Signed-off-by: Thomas Gleixner Cc: Julia Lawall Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org --- drivers/gpio/gpio-dwapb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 58faf04fce5d..55fa9853a7f2 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -348,8 +348,8 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio, irq_gc->chip_types[1].handler = handle_edge_irq; if (!pp->irq_shared) { - irq_set_chained_handler(pp->irq, dwapb_irq_handler); - irq_set_handler_data(pp->irq, gpio); + irq_set_chained_handler_and_data(pp->irq, dwapb_irq_handler, + gpio); } else { /* * Request a shared IRQ since where MFD would have devices -- cgit v1.2.3-55-g7522 From 3fb250ed39b2f883b566826132e04626da5910aa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 21 Jun 2015 20:16:06 +0200 Subject: gpio/msic: Fix race in installing chained IRQ handler Fix a race where a pending interrupt could be received and the handler called before the handler's data has been setup, by converting to irq_set_chained_handler_and_data(). Search and conversion was done with coccinelle: @@ expression E1, E2, E3; @@ ( -if (irq_set_chained_handler(E1, E3) != 0) - BUG(); | -irq_set_chained_handler(E1, E3); ) -irq_set_handler_data(E1, E2); +irq_set_chained_handler_and_data(E1, E3, E2); @@ expression E1, E2, E3; @@ ( -if (irq_set_chained_handler(E1, E3) != 0) - BUG(); ... | -irq_set_chained_handler(E1, E3); ... ) -irq_set_handler_data(E1, E2); +irq_set_chained_handler_and_data(E1, E3, E2); Reported-by: Russell King Signed-off-by: Thomas Gleixner Cc: Julia Lawall Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org --- drivers/gpio/gpio-msic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index 01acf0a8cdb1..7bcfb87a5fa6 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -309,8 +309,7 @@ static int platform_msic_gpio_probe(struct platform_device *pdev) &msic_irqchip, handle_simple_irq); } - irq_set_chained_handler(mg->irq, msic_gpio_irq_handler); - irq_set_handler_data(mg->irq, mg); + irq_set_chained_handler_and_data(mg->irq, msic_gpio_irq_handler, mg); return 0; err: -- cgit v1.2.3-55-g7522