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(-) 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 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(+) 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 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 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 From c61f819a45f6f71a295502e4c37d09173e74c380 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 2 May 2015 23:11:35 +0200 Subject: gpio: add DT bindings for lpc1850-gpio driver Signed-off-by: Joachim Eastwood Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/nxp,lpc1850-gpio.txt | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt new file mode 100644 index 000000000000..eb7cdd69e10b --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt @@ -0,0 +1,39 @@ +NXP LPC18xx/43xx GPIO controller Device Tree Bindings +----------------------------------------------------- + +Required properties: +- compatible : Should be "nxp,lpc1850-gpio" +- reg : Address and length of the register set for the device +- clocks : Clock specifier (see clock bindings for details) +- gpio-controller : Marks the device node as a GPIO controller. +- #gpio-cells : Should be two + - First cell is the GPIO line number + - Second cell is used to specify polarity + +Optional properties: +- gpio-ranges : Mapping between GPIO and pinctrl + +Example: +#define LPC_GPIO(port, pin) (port * 32 + pin) +#define LPC_PIN(port, pin) (0x##port * 32 + pin) + +gpio: gpio@400f4000 { + compatible = "nxp,lpc1850-gpio"; + reg = <0x400f4000 0x4000>; + clocks = <&ccu1 CLK_CPU_GPIO>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl LPC_GPIO(0,0) LPC_PIN(0,0) 2>, + ... + <&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5) 7>; +}; + +gpio_joystick { + compatible = "gpio-keys-polled"; + ... + + button@0 { + ... + gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>; + }; +}; -- cgit v1.2.3 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(-) 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 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(-) 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 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(-) 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 From a605c668cc0c0ed267c014267f04bef29460367d Mon Sep 17 00:00:00 2001 From: Kamlakant Patel Date: Tue, 28 Apr 2015 20:10:44 +0530 Subject: gpio: Add Netlogic XLP GPIO devicetree bindings documentation Provide documentation for Netlogic XLP SoCs GPIO controller DTS bindings. Signed-off-by: Kamlakant Patel Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/gpio-xlp.txt | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-xlp.txt diff --git a/Documentation/devicetree/bindings/gpio/gpio-xlp.txt b/Documentation/devicetree/bindings/gpio/gpio-xlp.txt new file mode 100644 index 000000000000..262ee4ddf2cb --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-xlp.txt @@ -0,0 +1,47 @@ +Netlogic XLP Family GPIO +======================== + +This GPIO driver is used for following Netlogic XLP SoCs: + XLP832, XLP316, XLP208, XLP980, XLP532 + +Required properties: +------------------- + +- compatible: Should be one of the following: + - "netlogic,xlp832-gpio": For Netlogic XLP832 + - "netlogic,xlp316-gpio": For Netlogic XLP316 + - "netlogic,xlp208-gpio": For Netlogic XLP208 + - "netlogic,xlp980-gpio": For Netlogic XLP980 + - "netlogic,xlp532-gpio": For Netlogic XLP532 +- reg: Physical base address and length of the controller's registers. +- #gpio-cells: Should be two. The first cell is the pin number and the second + cell is used to specify optional parameters (currently unused). +- gpio-controller: Marks the device node as a GPIO controller. +- nr-gpios: Number of GPIO pins supported by the controller. +- interrupt-cells: Should be two. The first cell is the GPIO Number. The + second cell is used to specify flags. The following subset of flags is + supported: + - trigger type: + 1 = low to high edge triggered. + 2 = high to low edge triggered. + 4 = active high level-sensitive. + 8 = active low level-sensitive. +- interrupts: Interrupt number for this device. +- interrupt-parent: phandle of the parent interrupt controller. +- interrupt-controller: Identifies the node as an interrupt controller. + +Example: + + gpio: xlp_gpio@34000 { + compatible = "netlogic,xlp316-gpio"; + reg = <0 0x34100 0x1000 + 0 0x35100 0x1000>; + #gpio-cells = <2>; + gpio-controller; + nr-gpios = <57>; + + #interrupt-cells = <2>; + interrupt-parent = <&pic>; + interrupts = <39>; + interrupt-controller; + }; -- cgit v1.2.3 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 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 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(-) 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 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(-) 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 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(-) 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 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(+) 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 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(+) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 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(-) 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 From 5b7d414ad025654516c59efd16b4a7f0d8017c1b Mon Sep 17 00:00:00 2001 From: Gregory Fong Date: Wed, 6 May 2015 01:37:55 -0700 Subject: dt-bindings: add brcmstb-gpio GPIO binding Add binding for Broadcom STB "UPG GIO" GPIO controller. Signed-off-by: Gregory Fong Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/brcm,brcmstb-gpio.txt | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt diff --git a/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt new file mode 100644 index 000000000000..435f1bcca341 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt @@ -0,0 +1,65 @@ +Broadcom STB "UPG GIO" GPIO controller + +The controller's registers are organized as sets of eight 32-bit +registers with each set controlling a bank of up to 32 pins. A single +interrupt is shared for all of the banks handled by the controller. + +Required properties: + +- compatible: + Must be "brcm,brcmstb-gpio" + +- reg: + Define the base and range of the I/O address space containing + the brcmstb GPIO controller registers + +- #gpio-cells: + Should be <2>. The first cell is the pin number (within the controller's + pin space), and the second is used for the following: + bit[0]: polarity (0 for active-high, 1 for active-low) + +- gpio-controller: + Specifies that the node is a GPIO controller. + +- brcm,gpio-bank-widths: + Number of GPIO lines for each bank. Number of elements must + correspond to number of banks suggested by the 'reg' property. + +Optional properties: + +- interrupts: + The interrupt shared by all GPIO lines for this controller. + +- interrupt-parent: + phandle of the parent interrupt controller + +- #interrupt-cells: + Should be <2>. The first cell is the GPIO number, the second should specify + flags. The following subset of flags is supported: + - bits[3:0] trigger type and level flags + 1 = low-to-high edge triggered + 2 = high-to-low edge triggered + 4 = active high level-sensitive + 8 = active low level-sensitive + Valid combinations are 1, 2, 3, 4, 8. + See also Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +- interrupt-controller: + Marks the device node as an interrupt controller + +- interrupt-names: + The name of the IRQ resource used by this controller + +Example: + upg_gio: gpio@f040a700 { + #gpio-cells = <0x2>; + #interrupt-cells = <0x2>; + compatible = "brcm,bcm7445-gpio", "brcm,brcmstb-gpio"; + gpio-controller; + interrupt-controller; + reg = <0xf040a700 0x80>; + interrupt-parent = <0xf>; + interrupts = <0x6>; + interrupt-names = "upg_gio"; + brcm,gpio-bank-widths = <0x20 0x20 0x20 0x18>; + }; -- cgit v1.2.3 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(+) 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 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(-) 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 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(-) 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 From dab472eb931bc2916fa779e56deccd0ec319cf5b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 6 May 2015 13:29:07 +0300 Subject: i2c / ACPI: Use 0 to indicate that device does not have interrupt assigned This is the convention used in most parts of the kernel including DT counterpart of I2C slave enumeration. To make things consistent do the same for ACPI I2C slave enumeration path as well. Signed-off-by: Mika Westerberg Acked-by: Wolfram Sang Signed-off-by: Linus Walleij --- drivers/i2c/i2c-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 987c124432c5..c21b3de70234 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -107,7 +107,7 @@ static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) if (sb->access_mode == ACPI_I2C_10BIT_MODE) info->flags |= I2C_CLIENT_TEN; } - } else if (info->irq < 0) { + } else if (!info->irq) { struct resource r; if (acpi_dev_resource_interrupt(ares, 0, &r)) @@ -134,7 +134,6 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, memset(&info, 0, sizeof(info)); info.fwnode = acpi_fwnode_handle(adev); - info.irq = -1; INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, -- cgit v1.2.3 From 845c877009cf014b971aab7f54613f9185a824b0 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 6 May 2015 13:29:08 +0300 Subject: i2c / ACPI: Assign IRQ for devices that have GpioInt automatically Following what DT already does. If the device does not have ACPI Interrupt resource but instead it has one or more GpioInt resources listed below it, we take the first GpioInt resource, convert it to suitable Linux IRQ number and pass it to the driver instead. This makes drivers simpler because the don't need to care about GPIOs at all if only thing they need is interrupt. Signed-off-by: Mika Westerberg Acked-by: Wolfram Sang Signed-off-by: Linus Walleij --- drivers/i2c/i2c-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index c21b3de70234..fc2ee8213fb6 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -631,8 +631,13 @@ static int i2c_device_probe(struct device *dev) if (!client) return 0; - if (!client->irq && dev->of_node) { - int irq = of_irq_get(dev->of_node, 0); + if (!client->irq) { + int irq = -ENOENT; + + if (dev->of_node) + irq = of_irq_get(dev->of_node, 0); + else if (ACPI_COMPANION(dev)) + irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0); if (irq == -EPROBE_DEFER) return irq; -- cgit v1.2.3 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(+) 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 From 3cff18732f3f9eadc68ad9b6e203646c3ba09ba7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 13 May 2015 13:03:21 +0200 Subject: gpio: discourage passing base to gpio_chip Passing a fixed base in struct gpio_chip is done for legacy systems that cannot handle dynamic allocation. Discourage this behaviour in the kerneldoc. Signed-off-by: Linus Walleij --- include/linux/gpio/driver.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 96a678842cde..cc7ec129b329 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -42,8 +42,12 @@ struct seq_file; * @dbg_show: optional routine to show contents in debugfs; default code * will be used when this is omitted, but custom code can show extra * state (such as pullup/pulldown configuration). - * @base: identifies the first GPIO number handled by this chip; or, if - * negative during registration, requests dynamic ID allocation. + * @base: identifies the first GPIO number handled by this chip; + * or, if negative during registration, requests dynamic ID allocation. + * DEPRECATION: providing anything non-negative and nailing the base + * base offset of GPIO chips is deprecated. Please pass -1 as base to + * let gpiolib select the chip base in all possible cases. We want to + * get rid of the static GPIO number space in the long run. * @ngpio: the number of GPIOs handled by this controller; the last GPIO * handled is (base + ngpio - 1). * @desc: array of ngpio descriptors. Private. -- cgit v1.2.3