From d28af35b0c91eea1030c5fef1f8c0f21b97a7e03 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 6 Aug 2018 16:30:12 +0200 Subject: gpio: tb10x: Create local helper variables Create a local struct device *dev helper variable to make the code easier to read. Most GPIO drivers use "np" (node pointer) rather than "dn" (device node) to point to the device tree node. Let's follow this convention. Cc: linux-snps-arc@lists.infradead.org Acked-by: Christian Ruppert Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tb10x.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index a12cd0b5c972..422b0ac5a9de 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -169,29 +169,30 @@ static int tb10x_gpio_probe(struct platform_device *pdev) { struct tb10x_gpio *tb10x_gpio; struct resource *mem; - struct device_node *dn = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; int ret = -EBUSY; u32 ngpio; - if (!dn) + if (!np) return -EINVAL; - if (of_property_read_u32(dn, "abilis,ngpio", &ngpio)) + if (of_property_read_u32(np, "abilis,ngpio", &ngpio)) return -EINVAL; - tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL); + tb10x_gpio = devm_kzalloc(dev, sizeof(*tb10x_gpio), GFP_KERNEL); if (tb10x_gpio == NULL) return -ENOMEM; spin_lock_init(&tb10x_gpio->spinlock); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem); + tb10x_gpio->base = devm_ioremap_resource(dev, mem); if (IS_ERR(tb10x_gpio->base)) return PTR_ERR(tb10x_gpio->base); - tb10x_gpio->gc.label = - devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOF", pdev->dev.of_node); + tb10x_gpio->gc.label = + devm_kasprintf(dev, GFP_KERNEL, "%pOF", pdev->dev.of_node); if (!tb10x_gpio->gc.label) return -ENOMEM; @@ -210,31 +211,31 @@ static int tb10x_gpio_probe(struct platform_device *pdev) ret = devm_gpiochip_add_data(&pdev->dev, &tb10x_gpio->gc, tb10x_gpio); if (ret < 0) { - dev_err(&pdev->dev, "Could not add gpiochip.\n"); + dev_err(dev, "Could not add gpiochip.\n"); return ret; } platform_set_drvdata(pdev, tb10x_gpio); - if (of_find_property(dn, "interrupt-controller", NULL)) { + if (of_find_property(np, "interrupt-controller", NULL)) { struct irq_chip_generic *gc; ret = platform_get_irq(pdev, 0); if (ret < 0) { - dev_err(&pdev->dev, "No interrupt specified.\n"); + dev_err(dev, "No interrupt specified.\n"); return ret; } tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq; tb10x_gpio->irq = ret; - ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade, + ret = devm_request_irq(dev, ret, tb10x_gpio_irq_cascade, IRQF_TRIGGER_NONE | IRQF_SHARED, - dev_name(&pdev->dev), tb10x_gpio); + dev_name(dev), tb10x_gpio); if (ret != 0) return ret; - tb10x_gpio->domain = irq_domain_add_linear(dn, + tb10x_gpio->domain = irq_domain_add_linear(np, tb10x_gpio->gc.ngpio, &irq_generic_chip_ops, NULL); if (!tb10x_gpio->domain) { -- cgit v1.2.3 From 0d1e31abcaf5a3f5ebff81fa0c1322c2b17e59dd Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 6 Aug 2018 16:59:42 +0200 Subject: gpio: tb10x: Use GENERIC_GPIO Instead of open coding logic for reading and writing GPIO lines, use the generic GPIO library. Also switch to using the spinlock from the generic GPIO to protect the registers. Cc: linux-snps-arc@lists.infradead.org Acked-by: Christian Ruppert Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-tb10x.c | 96 +++++++++++++++-------------------------------- 2 files changed, 31 insertions(+), 66 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4f52c3a8ec99..05cf02e46bef 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -480,6 +480,7 @@ config GPIO_SYSCON config GPIO_TB10X bool + select GPIO_GENERIC select GENERIC_IRQ_CHIP select OF_GPIO diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 422b0ac5a9de..d5e5d19f4c0a 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -45,14 +45,12 @@ /** - * @spinlock: used for atomic read/modify/write of registers * @base: register base address * @domain: IRQ domain of GPIO generated interrupts managed by this controller * @irq: Interrupt line of parent interrupt controller * @gc: gpio_chip structure associated to this GPIO controller */ struct tb10x_gpio { - spinlock_t spinlock; void __iomem *base; struct irq_domain *domain; int irq; @@ -76,60 +74,14 @@ static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs, u32 r; unsigned long flags; - spin_lock_irqsave(&gpio->spinlock, flags); + spin_lock_irqsave(&gpio->gc.bgpio_lock, flags); r = tb10x_reg_read(gpio, offs); r = (r & ~mask) | (val & mask); tb10x_reg_write(gpio, offs, r); - spin_unlock_irqrestore(&gpio->spinlock, flags); -} - -static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) -{ - struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip); - int mask = BIT(offset); - int val = TB10X_GPIO_DIR_IN << offset; - - tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val); - - return 0; -} - -static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip); - int val; - - val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA); - - if (val & BIT(offset)) - return 1; - else - return 0; -} - -static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip); - int mask = BIT(offset); - int val = value << offset; - - tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val); -} - -static int tb10x_gpio_direction_out(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip); - int mask = BIT(offset); - int val = TB10X_GPIO_DIR_OUT << offset; - - tb10x_gpio_set(chip, offset, value); - tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val); - - return 0; + spin_unlock_irqrestore(&gpio->gc.bgpio_lock, flags); } static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) @@ -184,8 +136,6 @@ static int tb10x_gpio_probe(struct platform_device *pdev) if (tb10x_gpio == NULL) return -ENOMEM; - spin_lock_init(&tb10x_gpio->spinlock); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); tb10x_gpio->base = devm_ioremap_resource(dev, mem); if (IS_ERR(tb10x_gpio->base)) @@ -196,20 +146,34 @@ static int tb10x_gpio_probe(struct platform_device *pdev) if (!tb10x_gpio->gc.label) return -ENOMEM; - tb10x_gpio->gc.parent = &pdev->dev; - tb10x_gpio->gc.owner = THIS_MODULE; - tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in; - tb10x_gpio->gc.get = tb10x_gpio_get; - tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out; - tb10x_gpio->gc.set = tb10x_gpio_set; - tb10x_gpio->gc.request = gpiochip_generic_request; - tb10x_gpio->gc.free = gpiochip_generic_free; - tb10x_gpio->gc.base = -1; - tb10x_gpio->gc.ngpio = ngpio; - tb10x_gpio->gc.can_sleep = false; - - - ret = devm_gpiochip_add_data(&pdev->dev, &tb10x_gpio->gc, tb10x_gpio); + /* + * Initialize generic GPIO with one single register for reading and setting + * the lines, no special set or clear registers and a data direction register + * wher 1 means "output". + */ + ret = bgpio_init(&tb10x_gpio->gc, dev, 4, + tb10x_gpio->base + OFFSET_TO_REG_DATA, + NULL, + NULL, + tb10x_gpio->base + OFFSET_TO_REG_DDR, + NULL, + 0); + if (ret) { + dev_err(dev, "unable to init generic GPIO\n"); + return ret; + } + tb10x_gpio->gc.base = -1; + tb10x_gpio->gc.parent = dev; + tb10x_gpio->gc.owner = THIS_MODULE; + /* + * ngpio is set by bgpio_init() but we override it, this .request() + * callback also overrides the one set up by generic GPIO. + */ + tb10x_gpio->gc.ngpio = ngpio; + tb10x_gpio->gc.request = gpiochip_generic_request; + tb10x_gpio->gc.free = gpiochip_generic_free; + + ret = devm_gpiochip_add_data(dev, &tb10x_gpio->gc, tb10x_gpio); if (ret < 0) { dev_err(dev, "Could not add gpiochip.\n"); return ret; -- cgit v1.2.3 From 4c2baed3a7e6fd72fb967d55dc67719e7f6ebcf6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 22 Aug 2018 22:41:01 +0200 Subject: ARM/gpio: ep93xx: build standalone As a preparation for multiplatform support, this ensures that the ep93xx gpio driver can be built without any of the platform specific header files. We pass the IRQ numbers as a resource now, and use the virtual mmio base from the already existing resource, rather than relying on the hardwired virtual address from the header file. Some numbers are now hardcoded that came from macros in the past, but for all I can tell, the driver already relied on the specific values. Cc: arm@kernel.org Signed-off-by: Arnd Bergmann Acked-by: Olof Johansson Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- arch/arm/mach-ep93xx/core.c | 9 +++++++++ drivers/gpio/gpio-ep93xx.c | 48 ++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 25 deletions(-) (limited to 'drivers/gpio') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index faf48a3b1fea..706515faee06 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -141,6 +141,15 @@ EXPORT_SYMBOL_GPL(ep93xx_chip_revision); *************************************************************************/ static struct resource ep93xx_gpio_resource[] = { DEFINE_RES_MEM(EP93XX_GPIO_PHYS_BASE, 0xcc), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO_AB), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO0MUX), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO1MUX), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO2MUX), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO3MUX), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO4MUX), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO5MUX), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO6MUX), + DEFINE_RES_IRQ(IRQ_EP93XX_GPIO7MUX), }; static struct platform_device ep93xx_gpio_device = { diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 45d384039e9b..654525d6a9f1 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -22,11 +22,20 @@ /* FIXME: this is here for gpio_to_irq() - get rid of this! */ #include -#include -#include - #define irq_to_gpio(irq) ((irq) - gpio_to_irq(0)) +void __iomem *ep93xx_gpio_base; /* FIXME: put this into irq_data */ +#define EP93XX_GPIO_REG(x) (ep93xx_gpio_base + (x)) +#define EP93XX_GPIO_F_INT_STATUS EP93XX_GPIO_REG(0x5c) +#define EP93XX_GPIO_A_INT_STATUS EP93XX_GPIO_REG(0xa0) +#define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc) + +/* Maximum value for gpio line identifiers */ +#define EP93XX_GPIO_LINE_MAX 63 + +/* Maximum value for irq capable line identifiers */ +#define EP93XX_GPIO_LINE_MAX_IRQ 23 + struct ep93xx_gpio { void __iomem *mmio_base; struct gpio_chip gc[8]; @@ -87,7 +96,7 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) status = readb(EP93XX_GPIO_A_INT_STATUS); for (i = 0; i < 8; i++) { if (status & (1 << i)) { - int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_A(0)) + i; + int gpio_irq = gpio_to_irq(0) + i; generic_handle_irq(gpio_irq); } } @@ -95,7 +104,7 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) status = readb(EP93XX_GPIO_B_INT_STATUS); for (i = 0; i < 8; i++) { if (status & (1 << i)) { - int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_B(0)) + i; + int gpio_irq = gpio_to_irq(8) + i; generic_handle_irq(gpio_irq); } } @@ -110,7 +119,7 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) */ unsigned int irq = irq_desc_get_irq(desc); int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */ - int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_F(0)) + port_f_idx; + int gpio_irq = gpio_to_irq(16) + port_f_idx; generic_handle_irq(gpio_irq); } @@ -228,9 +237,10 @@ static struct irq_chip ep93xx_gpio_irq_chip = { .irq_set_type = ep93xx_gpio_irq_type, }; -static void ep93xx_gpio_init_irq(void) +static void ep93xx_gpio_init_irq(struct platform_device *pdev) { int gpio_irq; + int i; for (gpio_irq = gpio_to_irq(0); gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) { @@ -239,24 +249,11 @@ static void ep93xx_gpio_init_irq(void) irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); } - irq_set_chained_handler(IRQ_EP93XX_GPIO_AB, + irq_set_chained_handler(platform_get_irq(pdev, 0), ep93xx_gpio_ab_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO0MUX, - ep93xx_gpio_f_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO1MUX, - ep93xx_gpio_f_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO2MUX, - ep93xx_gpio_f_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO3MUX, - ep93xx_gpio_f_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO4MUX, - ep93xx_gpio_f_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO5MUX, - ep93xx_gpio_f_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO6MUX, - ep93xx_gpio_f_irq_handler); - irq_set_chained_handler(IRQ_EP93XX_GPIO7MUX, - ep93xx_gpio_f_irq_handler); + for (i = 1; i <= 8; i++) + irq_set_chained_handler(platform_get_irq(pdev, i), + ep93xx_gpio_f_irq_handler); } @@ -362,6 +359,7 @@ static int ep93xx_gpio_probe(struct platform_device *pdev) ep93xx_gpio->mmio_base = devm_ioremap_resource(dev, res); if (IS_ERR(ep93xx_gpio->mmio_base)) return PTR_ERR(ep93xx_gpio->mmio_base); + ep93xx_gpio_base = ep93xx_gpio->mmio_base; for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { struct gpio_chip *gc = &ep93xx_gpio->gc[i]; @@ -373,7 +371,7 @@ static int ep93xx_gpio_probe(struct platform_device *pdev) bank->label); } - ep93xx_gpio_init_irq(); + ep93xx_gpio_init_irq(pdev); return 0; } -- cgit v1.2.3 From 1d2bb17aaf11b0470336cdafd02241a5d6355215 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:02 +0200 Subject: gpio: ep93xx: Cut down variable names In order to clean up the driver I need to cut a few trees, sorry, variable names, so I can see the forest, sorry driver properly. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 654525d6a9f1..3bfd0e46f7ed 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -37,7 +37,7 @@ void __iomem *ep93xx_gpio_base; /* FIXME: put this into irq_data */ #define EP93XX_GPIO_LINE_MAX_IRQ 23 struct ep93xx_gpio { - void __iomem *mmio_base; + void __iomem *base; struct gpio_chip gc[8]; }; @@ -323,10 +323,10 @@ static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) } static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, - void __iomem *mmio_base, struct ep93xx_gpio_bank *bank) + void __iomem *base, struct ep93xx_gpio_bank *bank) { - void __iomem *data = mmio_base + bank->data; - void __iomem *dir = mmio_base + bank->dir; + void __iomem *data = base + bank->data; + void __iomem *dir = base + bank->dir; int err; err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0); @@ -346,27 +346,27 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, static int ep93xx_gpio_probe(struct platform_device *pdev) { - struct ep93xx_gpio *ep93xx_gpio; + struct ep93xx_gpio *epg; struct resource *res; int i; struct device *dev = &pdev->dev; - ep93xx_gpio = devm_kzalloc(dev, sizeof(struct ep93xx_gpio), GFP_KERNEL); - if (!ep93xx_gpio) + epg = devm_kzalloc(dev, sizeof(*epg), GFP_KERNEL); + if (!epg) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ep93xx_gpio->mmio_base = devm_ioremap_resource(dev, res); - if (IS_ERR(ep93xx_gpio->mmio_base)) - return PTR_ERR(ep93xx_gpio->mmio_base); - ep93xx_gpio_base = ep93xx_gpio->mmio_base; + epg->base = devm_ioremap_resource(dev, res); + if (IS_ERR(epg->base)) + return PTR_ERR(epg->base); + ep93xx_gpio_base = epg->base; for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { - struct gpio_chip *gc = &ep93xx_gpio->gc[i]; + struct gpio_chip *gc = &epg->gc[i]; struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i]; if (ep93xx_gpio_add_bank(gc, &pdev->dev, - ep93xx_gpio->mmio_base, bank)) + epg->base, bank)) dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", bank->label); } -- cgit v1.2.3 From 68b6493185f7e2600af82e211f147dd1dc1af4c6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:03 +0200 Subject: gpio: ep93xx: Switch to SPDX license tag The subject says it all. Cut down on boilerplate. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 3bfd0e46f7ed..3822d11e90ac 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Generic EP93xx GPIO handling * @@ -6,10 +7,6 @@ * * Based on code originally from: * linux/arch/arm/mach-ep93xx/core.c - * - * 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 -- cgit v1.2.3 From 991ce74eeb561650705766bbbcc1a6e3a49f358e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:04 +0200 Subject: gpio: ep93xx: Pass around struct gpio_chip Instead of using a global variable, pass around the struct gpio_chip * pointer and dereference to the state container struct ep93xx_gpio as needed, like all other drivers do. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 111 +++++++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 38 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 3822d11e90ac..379f2573f794 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -21,11 +21,9 @@ #define irq_to_gpio(irq) ((irq) - gpio_to_irq(0)) -void __iomem *ep93xx_gpio_base; /* FIXME: put this into irq_data */ -#define EP93XX_GPIO_REG(x) (ep93xx_gpio_base + (x)) -#define EP93XX_GPIO_F_INT_STATUS EP93XX_GPIO_REG(0x5c) -#define EP93XX_GPIO_A_INT_STATUS EP93XX_GPIO_REG(0xa0) -#define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc) +#define EP93XX_GPIO_F_INT_STATUS 0x5c +#define EP93XX_GPIO_A_INT_STATUS 0xa0 +#define EP93XX_GPIO_B_INT_STATUS 0xbc /* Maximum value for gpio line identifiers */ #define EP93XX_GPIO_LINE_MAX 63 @@ -54,23 +52,24 @@ static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 }; static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x58 }; static const u8 int_debounce_register_offset[3] = { 0xa8, 0xc4, 0x64 }; -static void ep93xx_gpio_update_int_params(unsigned port) +static void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg, unsigned port) { BUG_ON(port > 2); - writeb_relaxed(0, EP93XX_GPIO_REG(int_en_register_offset[port])); + writeb_relaxed(0, epg->base + int_en_register_offset[port]); writeb_relaxed(gpio_int_type2[port], - EP93XX_GPIO_REG(int_type2_register_offset[port])); + epg->base + int_type2_register_offset[port]); writeb_relaxed(gpio_int_type1[port], - EP93XX_GPIO_REG(int_type1_register_offset[port])); + epg->base + int_type1_register_offset[port]); writeb(gpio_int_unmasked[port] & gpio_int_enabled[port], - EP93XX_GPIO_REG(int_en_register_offset[port])); + epg->base + int_en_register_offset[port]); } -static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable) +static void ep93xx_gpio_int_debounce(struct ep93xx_gpio *epg, + unsigned int irq, bool enable) { int line = irq_to_gpio(irq); int port = line >> 3; @@ -82,15 +81,17 @@ static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable) gpio_int_debounce[port] &= ~port_mask; writeb(gpio_int_debounce[port], - EP93XX_GPIO_REG(int_debounce_register_offset[port])); + epg->base + int_debounce_register_offset[port]); } static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) { + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct ep93xx_gpio *epg = gpiochip_get_data(gc); unsigned char status; int i; - status = readb(EP93XX_GPIO_A_INT_STATUS); + status = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); for (i = 0; i < 8; i++) { if (status & (1 << i)) { int gpio_irq = gpio_to_irq(0) + i; @@ -98,7 +99,7 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) } } - status = readb(EP93XX_GPIO_B_INT_STATUS); + status = readb(epg->base + EP93XX_GPIO_B_INT_STATUS); for (i = 0; i < 8; i++) { if (status & (1 << i)) { int gpio_irq = gpio_to_irq(8) + i; @@ -123,20 +124,24 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) static void ep93xx_gpio_irq_ack(struct irq_data *d) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ep93xx_gpio *epg = gpiochip_get_data(gc); int line = irq_to_gpio(d->irq); int port = line >> 3; int port_mask = 1 << (line & 7); if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { gpio_int_type2[port] ^= port_mask; /* switch edge direction */ - ep93xx_gpio_update_int_params(port); + ep93xx_gpio_update_int_params(epg, port); } - writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); + writeb(port_mask, epg->base + eoi_register_offset[port]); } static void ep93xx_gpio_irq_mask_ack(struct irq_data *d) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ep93xx_gpio *epg = gpiochip_get_data(gc); int line = irq_to_gpio(d->irq); int port = line >> 3; int port_mask = 1 << (line & 7); @@ -145,27 +150,31 @@ static void ep93xx_gpio_irq_mask_ack(struct irq_data *d) gpio_int_type2[port] ^= port_mask; /* switch edge direction */ gpio_int_unmasked[port] &= ~port_mask; - ep93xx_gpio_update_int_params(port); + ep93xx_gpio_update_int_params(epg, port); - writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); + writeb(port_mask, epg->base + eoi_register_offset[port]); } static void ep93xx_gpio_irq_mask(struct irq_data *d) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ep93xx_gpio *epg = gpiochip_get_data(gc); int line = irq_to_gpio(d->irq); int port = line >> 3; gpio_int_unmasked[port] &= ~(1 << (line & 7)); - ep93xx_gpio_update_int_params(port); + ep93xx_gpio_update_int_params(epg, port); } static void ep93xx_gpio_irq_unmask(struct irq_data *d) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ep93xx_gpio *epg = gpiochip_get_data(gc); int line = irq_to_gpio(d->irq); int port = line >> 3; gpio_int_unmasked[port] |= 1 << (line & 7); - ep93xx_gpio_update_int_params(port); + ep93xx_gpio_update_int_params(epg, port); } /* @@ -175,6 +184,8 @@ static void ep93xx_gpio_irq_unmask(struct irq_data *d) */ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ep93xx_gpio *epg = gpiochip_get_data(gc); const int gpio = irq_to_gpio(d->irq); const int port = gpio >> 3; const int port_mask = 1 << (gpio & 7); @@ -220,7 +231,7 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) gpio_int_enabled[port] |= port_mask; - ep93xx_gpio_update_int_params(port); + ep93xx_gpio_update_int_params(epg, port); return 0; } @@ -234,23 +245,47 @@ static struct irq_chip ep93xx_gpio_irq_chip = { .irq_set_type = ep93xx_gpio_irq_type, }; -static void ep93xx_gpio_init_irq(struct platform_device *pdev) +static void ep93xx_gpio_init_irq(struct platform_device *pdev, + struct ep93xx_gpio *epg) { int gpio_irq; int i; + /* The A bank */ for (gpio_irq = gpio_to_irq(0); - gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) { + gpio_irq < gpio_to_irq(8); + gpio_irq++) { + irq_set_chip_data(gpio_irq, &epg->gc[0]); + irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, + handle_level_irq); + irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); + } + /* The B bank */ + for (gpio_irq = gpio_to_irq(8); + gpio_irq < gpio_to_irq(16); + gpio_irq++) { + irq_set_chip_data(gpio_irq, &epg->gc[1]); + irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, + handle_level_irq); + irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); + } + /* The F bank */ + for (gpio_irq = gpio_to_irq(16); + gpio_irq < gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); + gpio_irq++) { + irq_set_chip_data(gpio_irq, &epg->gc[5]); irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, handle_level_irq); irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); } - irq_set_chained_handler(platform_get_irq(pdev, 0), - ep93xx_gpio_ab_irq_handler); + irq_set_chained_handler_and_data(platform_get_irq(pdev, 0), + ep93xx_gpio_ab_irq_handler, + &epg->gc[0]); for (i = 1; i <= 8; i++) - irq_set_chained_handler(platform_get_irq(pdev, i), - ep93xx_gpio_f_irq_handler); + irq_set_chained_handler_and_data(platform_get_irq(pdev, i), + ep93xx_gpio_f_irq_handler, + &epg->gc[i]); } @@ -285,10 +320,11 @@ static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false), }; -static int ep93xx_gpio_set_config(struct gpio_chip *chip, unsigned offset, +static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { - int gpio = chip->base + offset; + struct ep93xx_gpio *epg = gpiochip_get_data(gc); + int gpio = gc->base + offset; int irq = gpio_to_irq(gpio); u32 debounce; @@ -299,7 +335,7 @@ static int ep93xx_gpio_set_config(struct gpio_chip *chip, unsigned offset, return -EINVAL; debounce = pinconf_to_config_argument(config); - ep93xx_gpio_int_debounce(irq, debounce ? true : false); + ep93xx_gpio_int_debounce(epg, irq, debounce ? true : false); return 0; } @@ -320,10 +356,11 @@ static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) } static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, - void __iomem *base, struct ep93xx_gpio_bank *bank) + struct ep93xx_gpio *epg, + struct ep93xx_gpio_bank *bank) { - void __iomem *data = base + bank->data; - void __iomem *dir = base + bank->dir; + void __iomem *data = epg->base + bank->data; + void __iomem *dir = epg->base + bank->dir; int err; err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0); @@ -338,7 +375,7 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, gc->to_irq = ep93xx_gpio_to_irq; } - return devm_gpiochip_add_data(dev, gc, NULL); + return devm_gpiochip_add_data(dev, gc, epg); } static int ep93xx_gpio_probe(struct platform_device *pdev) @@ -356,19 +393,17 @@ static int ep93xx_gpio_probe(struct platform_device *pdev) epg->base = devm_ioremap_resource(dev, res); if (IS_ERR(epg->base)) return PTR_ERR(epg->base); - ep93xx_gpio_base = epg->base; for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { struct gpio_chip *gc = &epg->gc[i]; struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i]; - if (ep93xx_gpio_add_bank(gc, &pdev->dev, - epg->base, bank)) + if (ep93xx_gpio_add_bank(gc, &pdev->dev, epg, bank)) dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", bank->label); } - ep93xx_gpio_init_irq(pdev); + ep93xx_gpio_init_irq(pdev, epg); return 0; } -- cgit v1.2.3 From 3c38b3a3065eef10a12f5cf6c711f640d53ddb1a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:05 +0200 Subject: gpio: ep93xx: Rename has_debounce to has_irq This is closer to what the variable (per bank) actually means. We have the .gpio_to_irq() hook registered only when this is true. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 379f2573f794..a81d1e796912 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -297,25 +297,25 @@ struct ep93xx_gpio_bank { int data; int dir; int base; - bool has_debounce; + bool has_irq; }; -#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _debounce) \ +#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _has_irq) \ { \ .label = _label, \ .data = _data, \ .dir = _dir, \ .base = _base, \ - .has_debounce = _debounce, \ + .has_irq = _has_irq, \ } static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { - EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true), - EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true), + EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true), /* Bank A has 8 IRQs */ + EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true), /* Bank B has 8 IRQs */ EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false), EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false), EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false), - EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true), + EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true), /* Bank F has 8 IRQs */ EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false), EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false), }; @@ -370,7 +370,7 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, gc->label = bank->label; gc->base = bank->base; - if (bank->has_debounce) { + if (bank->has_irq) { gc->set_config = ep93xx_gpio_set_config; gc->to_irq = ep93xx_gpio_to_irq; } -- cgit v1.2.3 From 99399f40d896077ec175d42722d37e625056707e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:06 +0200 Subject: gpio: ep93xx: Properly call the chained IRQ handler The chained irq handler should call chained_irq_enter() and chained_irq_exit() before/after handling the chained IRQ. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index a81d1e796912..ce7e88df9cc5 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -88,9 +88,12 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct ep93xx_gpio *epg = gpiochip_get_data(gc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); unsigned char status; int i; + chained_irq_enter(irqchip, desc); + status = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); for (i = 0; i < 8; i++) { if (status & (1 << i)) { @@ -106,6 +109,8 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) generic_handle_irq(gpio_irq); } } + + chained_irq_exit(irqchip, desc); } static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) @@ -115,11 +120,14 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) * * IRQ_EP93XX_GPIO{0..7}MUX -> gpio_to_irq(EP93XX_GPIO_LINE_F({0..7}) */ + struct irq_chip *irqchip = irq_desc_get_chip(desc); unsigned int irq = irq_desc_get_irq(desc); int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */ int gpio_irq = gpio_to_irq(16) + port_f_idx; + chained_irq_enter(irqchip, desc); generic_handle_irq(gpio_irq); + chained_irq_exit(irqchip, desc); } static void ep93xx_gpio_irq_ack(struct irq_data *d) -- cgit v1.2.3 From fd935fc421e74b9777370a0175bc011b780173d3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:07 +0200 Subject: gpio: ep93xx: Do not pingpong irq numbers For setting debounce config we want to write an offset in a per-gpiochip register, and we know which gpiochip we are on. Instead of a roundtrip over the IRQ number, figure out what port we are on for this GPIO chip, then index to the right register and write the value. This adds the ep93xx_gpio_port() that finds the port index from a struct gpio_chip * that we can later exploit to simplify more code. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index ce7e88df9cc5..3b235b25c028 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -68,12 +68,29 @@ static void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg, unsigned port epg->base + int_en_register_offset[port]); } -static void ep93xx_gpio_int_debounce(struct ep93xx_gpio *epg, - unsigned int irq, bool enable) +static int ep93xx_gpio_port(struct gpio_chip *gc) { - int line = irq_to_gpio(irq); - int port = line >> 3; - int port_mask = 1 << (line & 7); + struct ep93xx_gpio *epg = gpiochip_get_data(gc); + int port = 0; + + while (gc != &epg->gc[port] && port < sizeof(epg->gc)) + port++; + + /* This should not happen but is there as a last safeguard */ + if (gc != &epg->gc[port]) { + pr_crit("can't find the GPIO port\n"); + return 0; + } + + return port; +} + +static void ep93xx_gpio_int_debounce(struct gpio_chip *gc, + unsigned int offset, bool enable) +{ + struct ep93xx_gpio *epg = gpiochip_get_data(gc); + int port = ep93xx_gpio_port(gc); + int port_mask = BIT(offset); if (enable) gpio_int_debounce[port] |= port_mask; @@ -331,19 +348,13 @@ static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { - struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int gpio = gc->base + offset; - int irq = gpio_to_irq(gpio); u32 debounce; if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) return -ENOTSUPP; - if (irq < 0) - return -EINVAL; - debounce = pinconf_to_config_argument(config); - ep93xx_gpio_int_debounce(epg, irq, debounce ? true : false); + ep93xx_gpio_int_debounce(gc, offset, debounce ? true : false); return 0; } -- cgit v1.2.3 From 51ba88e32ff5b3caee39862c862a7a1fbae78b2b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:08 +0200 Subject: gpio: ep93xx: Use the hwirq and port In the IRQ-related functions, switch to using the hwirq and port number found from the current struct gpio_chip * As the lower 3 bits of the IRQ number is identical to the lower 3 bits of the GPIO number we can cut some corners. Call directly into the gpiochip to set up the direction and read the input instead of using the consumer API. This enabled us to cut the confusing irq_to_gpio() macro that is a remnant of the old generic GPIO API as well. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 3b235b25c028..b2139ec43ce2 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -16,11 +16,10 @@ #include #include #include +#include /* FIXME: this is here for gpio_to_irq() - get rid of this! */ #include -#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0)) - #define EP93XX_GPIO_F_INT_STATUS 0x5c #define EP93XX_GPIO_A_INT_STATUS 0xa0 #define EP93XX_GPIO_B_INT_STATUS 0xbc @@ -151,9 +150,8 @@ static void ep93xx_gpio_irq_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int line = irq_to_gpio(d->irq); - int port = line >> 3; - int port_mask = 1 << (line & 7); + int port = ep93xx_gpio_port(gc); + int port_mask = BIT(d->irq & 7); if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { gpio_int_type2[port] ^= port_mask; /* switch edge direction */ @@ -167,9 +165,8 @@ static void ep93xx_gpio_irq_mask_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int line = irq_to_gpio(d->irq); - int port = line >> 3; - int port_mask = 1 << (line & 7); + int port = ep93xx_gpio_port(gc); + int port_mask = BIT(d->irq & 7); if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) gpio_int_type2[port] ^= port_mask; /* switch edge direction */ @@ -184,10 +181,9 @@ static void ep93xx_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int line = irq_to_gpio(d->irq); - int port = line >> 3; + int port = ep93xx_gpio_port(gc); - gpio_int_unmasked[port] &= ~(1 << (line & 7)); + gpio_int_unmasked[port] &= ~BIT(d->irq & 7); ep93xx_gpio_update_int_params(epg, port); } @@ -195,10 +191,9 @@ static void ep93xx_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int line = irq_to_gpio(d->irq); - int port = line >> 3; + int port = ep93xx_gpio_port(gc); - gpio_int_unmasked[port] |= 1 << (line & 7); + gpio_int_unmasked[port] |= BIT(d->irq & 7); ep93xx_gpio_update_int_params(epg, port); } @@ -211,12 +206,12 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio *epg = gpiochip_get_data(gc); - const int gpio = irq_to_gpio(d->irq); - const int port = gpio >> 3; - const int port_mask = 1 << (gpio & 7); + int port = ep93xx_gpio_port(gc); + int offset = d->irq & 7; + int port_mask = BIT(offset); irq_flow_handler_t handler; - gpio_direction_input(gpio); + gc->direction_input(gc, offset); switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -242,7 +237,7 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) case IRQ_TYPE_EDGE_BOTH: gpio_int_type1[port] |= port_mask; /* set initial polarity based on current input level */ - if (gpio_get_value(gpio)) + if (gc->get(gc, offset)) gpio_int_type2[port] &= ~port_mask; /* falling */ else gpio_int_type2[port] |= port_mask; /* rising */ -- cgit v1.2.3 From 68491b075db2e905d7df1594cf127da9bcd05cab Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:09 +0200 Subject: gpio: ep93xx: Use for_each_set_bit() in IRQ handler This simplifies and standardizes the AB IRQ handler by using the for_each_set_bit() library function. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index b2139ec43ce2..1248d83f860b 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -105,25 +105,21 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct ep93xx_gpio *epg = gpiochip_get_data(gc); struct irq_chip *irqchip = irq_desc_get_chip(desc); - unsigned char status; - int i; + unsigned long stat; + int offset; chained_irq_enter(irqchip, desc); - status = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); - for (i = 0; i < 8; i++) { - if (status & (1 << i)) { - int gpio_irq = gpio_to_irq(0) + i; - generic_handle_irq(gpio_irq); - } + stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); + for_each_set_bit(offset, &stat, 8) { + int gpio_irq = gpio_to_irq(0) + offset; + generic_handle_irq(gpio_irq); } - status = readb(epg->base + EP93XX_GPIO_B_INT_STATUS); - for (i = 0; i < 8; i++) { - if (status & (1 << i)) { - int gpio_irq = gpio_to_irq(8) + i; - generic_handle_irq(gpio_irq); - } + stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS); + for_each_set_bit(offset, &stat, 8) { + int gpio_irq = gpio_to_irq(8) + offset; + generic_handle_irq(gpio_irq); } chained_irq_exit(irqchip, desc); -- cgit v1.2.3 From d875cc27e2399a4b5d190db0a949570fc4276fec Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:10 +0200 Subject: gpio: ep93xx: Cut gpio_to_irq() usage This removes the callback into the gpiolib creating a circular call to convert between GPIO numbers and IRQs and pushes the whole business into the driver, just using an array of IRQ bases for the three IRQ capable ports. This way we get rid of including that no driver should include. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 1248d83f860b..d45d8ac3b525 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -17,8 +17,6 @@ #include #include #include -/* FIXME: this is here for gpio_to_irq() - get rid of this! */ -#include #define EP93XX_GPIO_F_INT_STATUS 0x5c #define EP93XX_GPIO_A_INT_STATUS 0xa0 @@ -30,6 +28,15 @@ /* Maximum value for irq capable line identifiers */ #define EP93XX_GPIO_LINE_MAX_IRQ 23 +/* + * IRQ numbers used by this driver is 64 ..87 + * + * Map GPIO A0..A7 (0..7) to irq 64..71, + * B0..B7 (7..15) to irq 72..79, and + * F0..F7 (16..24) to irq 80..87. + */ +static unsigned int ep93xx_gpio_irq_base[3] = { 64, 72, 80 }; + struct ep93xx_gpio { void __iomem *base; struct gpio_chip gc[8]; @@ -112,13 +119,13 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); for_each_set_bit(offset, &stat, 8) { - int gpio_irq = gpio_to_irq(0) + offset; + int gpio_irq = ep93xx_gpio_irq_base[0] + offset; generic_handle_irq(gpio_irq); } stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS); for_each_set_bit(offset, &stat, 8) { - int gpio_irq = gpio_to_irq(8) + offset; + int gpio_irq = ep93xx_gpio_irq_base[1] + offset; generic_handle_irq(gpio_irq); } @@ -130,12 +137,12 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) /* * map discontiguous hw irq range to continuous sw irq range: * - * IRQ_EP93XX_GPIO{0..7}MUX -> gpio_to_irq(EP93XX_GPIO_LINE_F({0..7}) + * IRQ_EP93XX_GPIO{0..7}MUX -> EP93XX_GPIO_LINE_F{0..7} */ struct irq_chip *irqchip = irq_desc_get_chip(desc); unsigned int irq = irq_desc_get_irq(desc); int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */ - int gpio_irq = gpio_to_irq(16) + port_f_idx; + int gpio_irq = ep93xx_gpio_irq_base[2] + port_f_idx; chained_irq_enter(irqchip, desc); generic_handle_irq(gpio_irq); @@ -268,27 +275,24 @@ static void ep93xx_gpio_init_irq(struct platform_device *pdev, int i; /* The A bank */ - for (gpio_irq = gpio_to_irq(0); - gpio_irq < gpio_to_irq(8); - gpio_irq++) { + for (i = 0; i < 8; i++) { + gpio_irq = ep93xx_gpio_irq_base[0] + i; irq_set_chip_data(gpio_irq, &epg->gc[0]); irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, handle_level_irq); irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); } /* The B bank */ - for (gpio_irq = gpio_to_irq(8); - gpio_irq < gpio_to_irq(16); - gpio_irq++) { + for (i = 0; i < 8; i++) { + gpio_irq = ep93xx_gpio_irq_base[1] + i; irq_set_chip_data(gpio_irq, &epg->gc[1]); irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, handle_level_irq); irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); } /* The F bank */ - for (gpio_irq = gpio_to_irq(16); - gpio_irq < gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); - gpio_irq++) { + for (i = 0; i < 8; i++) { + gpio_irq = ep93xx_gpio_irq_base[2] + i; irq_set_chip_data(gpio_irq, &epg->gc[5]); irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, handle_level_irq); @@ -350,19 +354,15 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, return 0; } -/* - * Map GPIO A0..A7 (0..7) to irq 64..71, - * B0..B7 (7..15) to irq 72..79, and - * F0..F7 (16..24) to irq 80..87. - */ -static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +static int ep93xx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { - int gpio = chip->base + offset; + int port = ep93xx_gpio_port(gc); - if (gpio > EP93XX_GPIO_LINE_MAX_IRQ) + /* Those are the ports supporting IRQ */ + if (port != 0 && port != 1 && port != 5) return -EINVAL; - return 64 + gpio; + return ep93xx_gpio_irq_base[port] + offset; } static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, -- cgit v1.2.3 From a419a3d92aab88dba1bf151f98f93c49d0e34206 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Aug 2018 22:41:11 +0200 Subject: gpio: ep93xx: Switch A and B to use GPIOLIB_IRQCHIP We can quite easily switch banks/ports A and B to use GPIOLIB_IRQCHIP which is code that will be more careful about handling interrupt descriptors and use a proper irqdomain for translating the IRQs. This cuts down some code in favor of using the implementation inside gpiolib. Acked-by: Alexander Sverdlin Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-ep93xx.c | 95 ++++++++++++++++++++++++---------------------- 2 files changed, 51 insertions(+), 45 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 05cf02e46bef..8887ac4734ab 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -200,6 +200,7 @@ config GPIO_EP93XX def_bool y depends on ARCH_EP93XX select GPIO_GENERIC + select GPIOLIB_IRQCHIP config GPIO_EXAR tristate "Support for GPIO pins on XR17V352/354/358" diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index d45d8ac3b525..68a416fc3141 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -29,13 +29,10 @@ #define EP93XX_GPIO_LINE_MAX_IRQ 23 /* - * IRQ numbers used by this driver is 64 ..87 - * - * Map GPIO A0..A7 (0..7) to irq 64..71, - * B0..B7 (7..15) to irq 72..79, and - * F0..F7 (16..24) to irq 80..87. + * Static mapping of GPIO bank F IRQS: + * F0..F7 (16..24) to irq 80..87. */ -static unsigned int ep93xx_gpio_irq_base[3] = { 64, 72, 80 }; +#define EP93XX_GPIO_F_IRQ_BASE 80 struct ep93xx_gpio { void __iomem *base; @@ -117,17 +114,21 @@ static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) chained_irq_enter(irqchip, desc); + /* + * Dispatch the IRQs to the irqdomain of each A and B + * gpiochip irqdomains depending on what has fired. + * The tricky part is that the IRQ line is shared + * between bank A and B and each has their own gpiochip. + */ stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); - for_each_set_bit(offset, &stat, 8) { - int gpio_irq = ep93xx_gpio_irq_base[0] + offset; - generic_handle_irq(gpio_irq); - } + for_each_set_bit(offset, &stat, 8) + generic_handle_irq(irq_find_mapping(epg->gc[0].irq.domain, + offset)); stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS); - for_each_set_bit(offset, &stat, 8) { - int gpio_irq = ep93xx_gpio_irq_base[1] + offset; - generic_handle_irq(gpio_irq); - } + for_each_set_bit(offset, &stat, 8) + generic_handle_irq(irq_find_mapping(epg->gc[1].irq.domain, + offset)); chained_irq_exit(irqchip, desc); } @@ -142,7 +143,7 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) struct irq_chip *irqchip = irq_desc_get_chip(desc); unsigned int irq = irq_desc_get_irq(desc); int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */ - int gpio_irq = ep93xx_gpio_irq_base[2] + port_f_idx; + int gpio_irq = EP93XX_GPIO_F_IRQ_BASE + port_f_idx; chained_irq_enter(irqchip, desc); generic_handle_irq(gpio_irq); @@ -268,44 +269,53 @@ static struct irq_chip ep93xx_gpio_irq_chip = { .irq_set_type = ep93xx_gpio_irq_type, }; -static void ep93xx_gpio_init_irq(struct platform_device *pdev, - struct ep93xx_gpio *epg) +static int ep93xx_gpio_init_irq(struct platform_device *pdev, + struct ep93xx_gpio *epg) { + int ab_parent_irq = platform_get_irq(pdev, 0); + struct device *dev = &pdev->dev; int gpio_irq; + int ret; int i; /* The A bank */ - for (i = 0; i < 8; i++) { - gpio_irq = ep93xx_gpio_irq_base[0] + i; - irq_set_chip_data(gpio_irq, &epg->gc[0]); - irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, - handle_level_irq); - irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); + ret = gpiochip_irqchip_add(&epg->gc[0], &ep93xx_gpio_irq_chip, + 64, handle_level_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(dev, "Could not add irqchip 0\n"); + return ret; } + gpiochip_set_chained_irqchip(&epg->gc[0], &ep93xx_gpio_irq_chip, + ab_parent_irq, + ep93xx_gpio_ab_irq_handler); + /* The B bank */ - for (i = 0; i < 8; i++) { - gpio_irq = ep93xx_gpio_irq_base[1] + i; - irq_set_chip_data(gpio_irq, &epg->gc[1]); - irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, - handle_level_irq); - irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); + ret = gpiochip_irqchip_add(&epg->gc[1], &ep93xx_gpio_irq_chip, + 72, handle_level_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(dev, "Could not add irqchip 1\n"); + return ret; } + gpiochip_set_chained_irqchip(&epg->gc[1], &ep93xx_gpio_irq_chip, + ab_parent_irq, + ep93xx_gpio_ab_irq_handler); + /* The F bank */ for (i = 0; i < 8; i++) { - gpio_irq = ep93xx_gpio_irq_base[2] + i; + gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i; irq_set_chip_data(gpio_irq, &epg->gc[5]); irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, handle_level_irq); irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); } - irq_set_chained_handler_and_data(platform_get_irq(pdev, 0), - ep93xx_gpio_ab_irq_handler, - &epg->gc[0]); for (i = 1; i <= 8; i++) irq_set_chained_handler_and_data(platform_get_irq(pdev, i), ep93xx_gpio_f_irq_handler, &epg->gc[i]); + return 0; } @@ -354,15 +364,9 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, return 0; } -static int ep93xx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +static int ep93xx_gpio_f_to_irq(struct gpio_chip *gc, unsigned offset) { - int port = ep93xx_gpio_port(gc); - - /* Those are the ports supporting IRQ */ - if (port != 0 && port != 1 && port != 5) - return -EINVAL; - - return ep93xx_gpio_irq_base[port] + offset; + return EP93XX_GPIO_F_IRQ_BASE + offset; } static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, @@ -380,10 +384,8 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, gc->label = bank->label; gc->base = bank->base; - if (bank->has_irq) { + if (bank->has_irq) gc->set_config = ep93xx_gpio_set_config; - gc->to_irq = ep93xx_gpio_to_irq; - } return devm_gpiochip_add_data(dev, gc, epg); } @@ -410,7 +412,10 @@ static int ep93xx_gpio_probe(struct platform_device *pdev) if (ep93xx_gpio_add_bank(gc, &pdev->dev, epg, bank)) dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", - bank->label); + bank->label); + /* Only bank F has especially funky IRQ handling */ + if (i == 5) + gc->to_irq = ep93xx_gpio_f_to_irq; } ep93xx_gpio_init_irq(pdev, epg); -- cgit v1.2.3 From 496069b87eea631274c2c35fb6f8c45ad838436b Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 7 Aug 2018 08:57:02 +0100 Subject: gpio: rcar: Add GPIO hole support GPIO hole is present in RZ/G1C SoC. Valid GPIO pins on bank3 are in the range GP3_0 to GP3_16 and GP3_27 to GP3_29. The GPIO pins between GP3_17 to GP3_26 are unused. Add support for handling unused GPIO's. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/gpio/gpio-rcar.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 55cc61086d99..3c82bb3c2030 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -321,6 +321,9 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, u32 val, bankmask; bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); + if (chip->valid_mask) + bankmask &= chip->valid_mask[0]; + if (!bankmask) return; @@ -558,6 +561,9 @@ static int gpio_rcar_resume(struct device *dev) u32 mask; for (offset = 0; offset < p->gpio_chip.ngpio; offset++) { + if (!gpiochip_line_is_valid(&p->gpio_chip, offset)) + continue; + mask = BIT(offset); /* I/O pin */ if (!(p->bank_info.iointsel & mask)) { -- cgit v1.2.3 From be887843f275d5a57db7033ea8ed2d7637475b66 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 14 Aug 2018 13:29:18 +0200 Subject: drivers: gpio: Update MODULE AUTHOR email address no functional changes Signed-off-by: Michael Hennerich Signed-off-by: Linus Walleij --- drivers/gpio/gpio-adp5520.c | 2 +- drivers/gpio/gpio-adp5588.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c index 21452622d954..e321955782a1 100644 --- a/drivers/gpio/gpio-adp5520.c +++ b/drivers/gpio/gpio-adp5520.c @@ -172,7 +172,7 @@ static struct platform_driver adp5520_gpio_driver = { module_platform_driver(adp5520_gpio_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("GPIO ADP5520 Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:adp5520-gpio"); diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index 3530ccd17e04..054feb17d3a0 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -478,6 +478,6 @@ static struct i2c_driver adp5588_gpio_driver = { module_i2c_driver(adp5588_gpio_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("GPIO ADP5588 Driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9ce3ebe973bf4073426f35f282c6b955ed802765 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 25 Aug 2018 10:44:17 +0200 Subject: gpio: pxa: handle corner case of unprobed device In the corner case where the gpio driver probe fails, for whatever reason, the suspend and resume handlers will still be called as they have to be registered as syscore operations. This applies as well when no probe was called while the driver has been built in the kernel. Nicolas tracked this in : https://bugzilla.kernel.org/show_bug.cgi?id=200905 Therefore, add a failsafe in these function, and test if a proper probe succeeded and the driver is functional. Signed-off-by: Robert Jarzmik Reported-by: Nicolas Chauvet Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pxa.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index c18712dabf93..bfe4c5c9f41c 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -776,6 +776,9 @@ static int pxa_gpio_suspend(void) struct pxa_gpio_bank *c; int gpio; + if (!pchip) + return 0; + for_each_gpio_bank(gpio, c, pchip) { c->saved_gplr = readl_relaxed(c->regbase + GPLR_OFFSET); c->saved_gpdr = readl_relaxed(c->regbase + GPDR_OFFSET); @@ -794,6 +797,9 @@ static void pxa_gpio_resume(void) struct pxa_gpio_bank *c; int gpio; + if (!pchip) + return; + for_each_gpio_bank(gpio, c, pchip) { /* restore level with set/clear */ writel_relaxed(c->saved_gplr, c->regbase + GPSR_OFFSET); -- cgit v1.2.3 From 62cdcb6c572ef0f55f4d6c06f008289f50eb516a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Aug 2018 20:52:19 -0500 Subject: gpio: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Linus Walleij Cc: linux-gpio@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a4f1157d6aa0..089783b11e87 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -348,8 +348,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, else if (of_property_read_bool(np, "output-high")) *dflags |= GPIOD_OUT_HIGH; else { - pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n", - desc_to_gpio(desc), np->name); + pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n", + desc_to_gpio(desc), np); return ERR_PTR(-EINVAL); } -- cgit v1.2.3 From da02d79452ea34e7ab22bd97d2fc7383bc00e16b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 27 Aug 2018 22:15:40 +0200 Subject: gpio: ftgpio: Support optional silicon clock The GPIO silicon is clocked with a PCLK (peripheral clock) on all systems, however not all platforms model it and include it in e.g. the device tree, so add clock handling but make it optional so we bail out safely if it is e.g. always on. Reviewed-by: Andrew Lunn Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ftgpio010.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index 868bf8501560..f548bc92e1d1 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -15,6 +15,7 @@ #include #include #include +#include /* GPIO registers definition */ #define GPIO_DATA_OUT 0x00 @@ -40,11 +41,14 @@ * struct ftgpio_gpio - Gemini GPIO state container * @dev: containing device for this instance * @gc: gpiochip for this instance + * @base: remapped I/O-memory base + * @clk: silicon clock */ struct ftgpio_gpio { struct device *dev; struct gpio_chip gc; void __iomem *base; + struct clk *clk; }; static void ftgpio_gpio_ack_irq(struct irq_data *d) @@ -180,6 +184,19 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) if (irq <= 0) return irq ? irq : -EINVAL; + g->clk = devm_clk_get(dev, NULL); + if (!IS_ERR(g->clk)) { + ret = clk_prepare_enable(g->clk); + if (ret) + return ret; + } else if (PTR_ERR(g->clk) == -EPROBE_DEFER) { + /* + * Percolate deferrals, for anything else, + * just live without the clocking. + */ + return PTR_ERR(g->clk); + } + ret = bgpio_init(&g->gc, dev, 4, g->base + GPIO_DATA_IN, g->base + GPIO_DATA_SET, @@ -189,7 +206,7 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) 0); if (ret) { dev_err(dev, "unable to init generic GPIO\n"); - return ret; + goto dis_clk; } g->gc.label = "FTGPIO010"; g->gc.base = -1; @@ -199,7 +216,7 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) ret = devm_gpiochip_add_data(dev, &g->gc, g); if (ret) - return ret; + goto dis_clk; /* Disable, unmask and clear all interrupts */ writel(0x0, g->base + GPIO_INT_EN); @@ -211,14 +228,29 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) IRQ_TYPE_NONE); if (ret) { dev_info(dev, "could not add irqchip\n"); - return ret; + goto dis_clk; } gpiochip_set_chained_irqchip(&g->gc, &ftgpio_gpio_irqchip, irq, ftgpio_gpio_irq_handler); + platform_set_drvdata(pdev, g); dev_info(dev, "FTGPIO010 @%p registered\n", g->base); return 0; + +dis_clk: + if (!IS_ERR(g->clk)) + clk_disable_unprepare(g->clk); + return ret; +} + +static int ftgpio_gpio_remove(struct platform_device *pdev) +{ + struct ftgpio_gpio *g = platform_get_drvdata(pdev); + + if (!IS_ERR(g->clk)) + clk_disable_unprepare(g->clk); + return 0; } static const struct of_device_id ftgpio_gpio_of_match[] = { @@ -239,6 +271,7 @@ static struct platform_driver ftgpio_gpio_driver = { .name = "ftgpio010-gpio", .of_match_table = of_match_ptr(ftgpio_gpio_of_match), }, - .probe = ftgpio_gpio_probe, + .probe = ftgpio_gpio_probe, + .remove = ftgpio_gpio_remove, }; builtin_platform_driver(ftgpio_gpio_driver); -- cgit v1.2.3 From 36f3f19a82152d558ee6846898b9480409b57e9d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 27 Aug 2018 22:15:51 +0200 Subject: gpio: ftgpio: Support debounce timer The FTGPIO010 has a debounce timer or rather prescaler that will affect interrupts fireing off the block. We can support this to get proper debounce on e.g. keypresses. Since the same prescaler is used across all GPIO lines of the silicon block, we need to bail out if the prescaler is already set and in use by another line. If the prescaler is already set to what we need, fine, we reuse it. This happens more often than not when the same debounce time is set for several GPIO keys, so we support that usecase easily with this code. Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ftgpio010.c | 74 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index f548bc92e1d1..95f578804b0e 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -161,6 +161,73 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(irqchip, desc); } +static int ftgpio_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + enum pin_config_param param = pinconf_to_config_param(config); + u32 arg = pinconf_to_config_argument(config); + struct ftgpio_gpio *g = gpiochip_get_data(gc); + unsigned long pclk_freq; + u32 deb_div; + u32 val; + + if (param != PIN_CONFIG_INPUT_DEBOUNCE) + return -ENOTSUPP; + + /* + * Debounce only works if interrupts are enabled. The manual + * states that if PCLK is 66 MHz, and this is set to 0x7D0, then + * PCLK is divided down to 33 kHz for the debounce timer. 0x7D0 is + * 2000 decimal, so what they mean is simply that the PCLK is + * divided by this value. + * + * As we get a debounce setting in microseconds, we calculate the + * desired period time and see if we can get a suitable debounce + * time. + */ + pclk_freq = clk_get_rate(g->clk); + deb_div = DIV_ROUND_CLOSEST(pclk_freq, arg); + + /* This register is only 24 bits wide */ + if (deb_div > (1 << 24)) + return -ENOTSUPP; + + dev_dbg(g->dev, "prescale divisor: %08x, resulting frequency %lu Hz\n", + deb_div, (pclk_freq/deb_div)); + + val = readl(g->base + GPIO_DEBOUNCE_PRESCALE); + if (val == deb_div) { + /* + * The debounce timer happens to already be set to the + * desireable value, what a coincidence! We can just enable + * debounce on this GPIO line and return. This happens more + * often than you think, for example when all GPIO keys + * on a system are requesting the same debounce interval. + */ + val = readl(g->base + GPIO_DEBOUNCE_EN); + val |= BIT(offset); + writel(val, g->base + GPIO_DEBOUNCE_EN); + return 0; + } + + val = readl(g->base + GPIO_DEBOUNCE_EN); + if (val) { + /* + * Oh no! Someone is already using the debounce with + * another setting than what we need. Bummer. + */ + return -ENOTSUPP; + } + + /* First come, first serve */ + writel(deb_div, g->base + GPIO_DEBOUNCE_PRESCALE); + /* Enable debounce */ + val |= BIT(offset); + writel(val, g->base + GPIO_DEBOUNCE_EN); + + return 0; +} + static int ftgpio_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -214,6 +281,10 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) g->gc.owner = THIS_MODULE; /* ngpio is set by bgpio_init() */ + /* We need a silicon clock to do debounce */ + if (!IS_ERR(g->clk)) + g->gc.set_config = ftgpio_gpio_set_config; + ret = devm_gpiochip_add_data(dev, &g->gc, g); if (ret) goto dis_clk; @@ -223,6 +294,9 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) writel(0x0, g->base + GPIO_INT_MASK); writel(~0x0, g->base + GPIO_INT_CLR); + /* Clear any use of debounce */ + writel(0x0, g->base + GPIO_DEBOUNCE_EN); + ret = gpiochip_irqchip_add(&g->gc, &ftgpio_gpio_irqchip, 0, handle_bad_irq, IRQ_TYPE_NONE); -- cgit v1.2.3 From bfba223dcc4548632d8f3bfd15690a86d4c68504 Mon Sep 17 00:00:00 2001 From: Justin Chen Date: Fri, 17 Aug 2018 16:47:39 -0700 Subject: gpio: brcmstb: allow 0 width GPIO banks Sometimes we have empty banks within the GPIO block. This commit allows proper handling of 0 width GPIO banks. We handle 0 width GPIO banks by incrementing the bank and number of GPIOs, but not initializing them. This will mean a call into the non-existent GPIOs will return an error. Also remove "GPIO registered" dev print. This information is misleading since the incremented banks and gpio_base do not reflect the actual GPIOs that get initialized. We leave this information out since it is already printed with dev_dbg. Signed-off-by: Justin Chen Acked-by: Florian Fainelli Signed-off-by: Linus Walleij --- drivers/gpio/gpio-brcmstb.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 16c7f9f49416..af936dcca659 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -664,6 +664,18 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) struct brcmstb_gpio_bank *bank; struct gpio_chip *gc; + /* + * If bank_width is 0, then there is an empty bank in the + * register block. Special handling for this case. + */ + if (bank_width == 0) { + dev_dbg(dev, "Width 0 found: Empty bank @ %d\n", + num_banks); + num_banks++; + gpio_base += MAX_GPIO_PER_BANK; + continue; + } + bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL); if (!bank) { err = -ENOMEM; @@ -740,9 +752,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) goto fail; } - dev_info(dev, "Registered %d banks (GPIO(s): %d-%d)\n", - num_banks, priv->gpio_base, gpio_base - 1); - if (priv->parent_wake_irq && need_wakeup_event) pm_wakeup_event(dev, 0); -- cgit v1.2.3 From 5d75683e2b875fd8f6826d17b68ec665ca379a14 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2018 16:45:30 +0200 Subject: gpio: tpx65xxx: Include the right header These are drivers so include only . Acked-by: Andrew F. Davis Reviewed-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tps65086.c | 2 +- drivers/gpio/gpio-tps6586x.c | 2 +- drivers/gpio/gpio-tps65910.c | 2 +- drivers/gpio/gpio-tps65912.c | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index b23c4d2429be..47a8b209183c 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -14,7 +14,7 @@ * Based on the TPS65912 driver */ -#include +#include #include #include diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index 042b9a20781a..f7404a6de55a 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -22,7 +22,7 @@ */ #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index e63d7dabf78b..55c745b53d0f 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index abc0798ef843..e492c669b10f 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -17,7 +17,7 @@ * Margarita Olaya Cabrera */ -#include +#include #include #include @@ -40,9 +40,9 @@ static int tps65912_gpio_get_direction(struct gpio_chip *gc, return ret; if (val & GPIO_CFG_MASK) - return GPIOF_DIR_OUT; + return 0; else - return GPIOF_DIR_IN; + return 1; } static int tps65912_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -- cgit v1.2.3 From d5a4da159ed32034be1c7a3c905159b97eb54c5d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2018 16:49:14 +0200 Subject: gpio: tps65xxx: Use SPDX license tag I'm tired of boilerplate, use the SPDX tag. Acked-by: Andrew F. Davis Reviewed-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tps65086.c | 10 +--------- drivers/gpio/gpio-tps6586x.c | 13 +------------ drivers/gpio/gpio-tps65910.c | 7 +------ drivers/gpio/gpio-tps65912.c | 10 +--------- 4 files changed, 4 insertions(+), 36 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 47a8b209183c..2eea98ff4ea3 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis * - * 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 "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65912 driver */ diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index f7404a6de55a..9b6cc74f47c8 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * TI TPS6586x GPIO driver * @@ -7,18 +8,6 @@ * Based on tps6586x.c * Copyright (c) 2010 CompuLab Ltd. * Mike Rapoport - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ #include diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 55c745b53d0f..0c785b0fd161 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI TPS6591x GPIO driver * @@ -5,12 +6,6 @@ * * Author: Graeme Gregory * Author: Jorge Eduardo Candelaria - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ #include diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index e492c669b10f..3ad68bd78282 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * GPIO driver for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis * - * 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 "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the Arizona GPIO driver and the previous TPS65912 driver by * Margarita Olaya Cabrera */ -- cgit v1.2.3 From 833eacc7b5913da9896bacd30db7d490aa777868 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2018 17:02:16 +0200 Subject: gpio: mxs: Get rid of external API call The MXS driver was calling back into the GPIO API from its irqchip. This is not very elegant, as we are a driver, let's just shortcut back into the gpio_chip .get() function instead. This is a tricky case since the .get() callback is not in this file, instead assigned by bgpio_init(). Calling the function direcly in the gpio_chip is however the lesser evil. Cc: Sascha Hauer Cc: Janusz Uzycki Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mxs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index df30490da820..ea874fd033a5 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -18,8 +18,6 @@ #include #include #include -/* FIXME: for gpio_get_value(), replace this by direct register read */ -#include #include #define MXS_SET 0x4 @@ -86,7 +84,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) port->both_edges &= ~pin_mask; switch (type) { case IRQ_TYPE_EDGE_BOTH: - val = gpio_get_value(port->gc.base + d->hwirq); + val = port->gc.get(&port->gc, d->hwirq); if (val) edge = GPIO_INT_FALL_EDGE; else -- cgit v1.2.3 From c99601f672fdb057955a5359ab25958c054b07c5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2018 17:07:29 +0200 Subject: gpio: ts5500: Include the right header This is a GPIO driver so include only . Cc: kernel@savoirfairelinux.com Cc: Vivien Didelot Cc: Jerome Oufella Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ts5500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index 6cfeba07f882..dd378813f412 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -23,7 +23,7 @@ */ #include -#include +#include #include #include #include -- cgit v1.2.3 From bf97279079be21fc843d90489846d2c73c77ed09 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2018 17:08:53 +0200 Subject: gpio: ts5500: Use SPDX header Cut some boilerplate, use the SPDX license identifier. Cc: kernel@savoirfairelinux.com Cc: Vivien Didelot Cc: Jerome Oufella Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ts5500.c | 5 +---- include/linux/platform_data/gpio-ts5500.h | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index dd378813f412..d4ea3ee23b9c 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Digital I/O driver for Technologic Systems TS-5500 * @@ -16,10 +17,6 @@ * TS-5600: * Documentation: http://wiki.embeddedarm.com/wiki/TS-5600 * Blocks: LCD port (identical to TS-5500 LCD). - * - * 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 diff --git a/include/linux/platform_data/gpio-ts5500.h b/include/linux/platform_data/gpio-ts5500.h index b10d11c9bb49..94346d4504cb 100644 --- a/include/linux/platform_data/gpio-ts5500.h +++ b/include/linux/platform_data/gpio-ts5500.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * GPIO (DIO) header for Technologic Systems TS-5500 * * Copyright (c) 2012 Savoir-faire Linux Inc. * Vivien Didelot - * - * 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. */ #ifndef _PDATA_GPIO_TS5500_H -- cgit v1.2.3 From 97feacc05dfb601273fd680d7b805b8e09c1bec7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2018 17:16:18 +0200 Subject: gpio: ts5500: Delete platform data handling The TS5500 GPIO driver apparently supports platform data without making any use of it whatsoever. Delete this code, last chance to speak up if you think it is needed. Cc: kernel@savoirfairelinux.com Cc: Vivien Didelot Cc: Jerome Oufella Signed-off-by: Linus Walleij --- arch/x86/platform/ts5500/ts5500.c | 1 - drivers/gpio/gpio-ts5500.c | 6 ------ include/linux/platform_data/gpio-ts5500.h | 24 ------------------------ 3 files changed, 31 deletions(-) delete mode 100644 include/linux/platform_data/gpio-ts5500.h (limited to 'drivers/gpio') diff --git a/arch/x86/platform/ts5500/ts5500.c b/arch/x86/platform/ts5500/ts5500.c index fd39301f25ac..7e56fc74093c 100644 --- a/arch/x86/platform/ts5500/ts5500.c +++ b/arch/x86/platform/ts5500/ts5500.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index d4ea3ee23b9c..c91890488402 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -315,7 +314,6 @@ static void ts5500_disable_irq(struct ts5500_priv *priv) static int ts5500_dio_probe(struct platform_device *pdev) { enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data; - struct ts5500_dio_platform_data *pdata = dev_get_platdata(&pdev->dev); struct device *dev = &pdev->dev; const char *name = dev_name(dev); struct ts5500_priv *priv; @@ -346,10 +344,6 @@ static int ts5500_dio_probe(struct platform_device *pdev) priv->gpio_chip.set = ts5500_gpio_set; priv->gpio_chip.to_irq = ts5500_gpio_to_irq; priv->gpio_chip.base = -1; - if (pdata) { - priv->gpio_chip.base = pdata->base; - priv->strap = pdata->strap; - } switch (block) { case TS5500_DIO1: diff --git a/include/linux/platform_data/gpio-ts5500.h b/include/linux/platform_data/gpio-ts5500.h deleted file mode 100644 index 94346d4504cb..000000000000 --- a/include/linux/platform_data/gpio-ts5500.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * GPIO (DIO) header for Technologic Systems TS-5500 - * - * Copyright (c) 2012 Savoir-faire Linux Inc. - * Vivien Didelot - */ - -#ifndef _PDATA_GPIO_TS5500_H -#define _PDATA_GPIO_TS5500_H - -/** - * struct ts5500_dio_platform_data - TS-5500 pin block configuration - * @base: The GPIO base number to use. - * @strap: The only pin connected to an interrupt in a block is input-only. - * If you need a bidirectional line which can trigger an IRQ, you - * may strap it with an in/out pin. This flag indicates this case. - */ -struct ts5500_dio_platform_data { - int base; - bool strap; -}; - -#endif /* _PDATA_GPIO_TS5500_H */ -- cgit v1.2.3 From f0916167d7e5467fbd1b3654aab396773e8172ae Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 00:18:51 +0200 Subject: gpio: zevio: Include the right header This is a driver so include only . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-zevio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index 3926ce9c2840..57432397e5e5 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include /* * Memory layout: -- cgit v1.2.3 From ba21d55f6ad19cbb466b2429829900de5efac513 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 08:15:50 +0200 Subject: gpio: twl4030: Include the right header This is a GPIO driver so include only . Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl4030.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 9b511df5450e..50b17814bd91 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From ecb07684a58fb4139719e96efad516d05c1a33e5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 08:20:59 +0200 Subject: gpio: twl4030: Cut down boilerplate Use the SPDX header to indicate the license for this driver. Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl4030.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 50b17814bd91..1e47ddff33f6 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Access to GPIOs on TWL4030/TPS659x0 chips * @@ -9,20 +10,6 @@ * * Initial Code: * Andy Lowe / Nishanth Menon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -- cgit v1.2.3 From ab8c1e82935b8cd034d18c5d0cf944a532a83aea Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 09:52:10 +0200 Subject: gpio: twl4030: Implement .get_direction() It's nice to be able to read back the direction of the GPIO line from the hardware so implement .get_direction() for twl4030. Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl4030.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 1e47ddff33f6..fbfb648d3502 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -154,6 +154,23 @@ static int twl4030_set_gpio_direction(int gpio, int is_input) return ret; } +static int twl4030_get_gpio_direction(int gpio) +{ + u8 d_bnk = gpio >> 3; + u8 d_msk = BIT(gpio & 0x7); + u8 base = REG_GPIODATADIR1 + d_bnk; + int ret = 0; + + ret = gpio_twl4030_read(base); + if (ret < 0) + return ret; + + /* 1 = output, but gpiolib semantics are inverse so invert */ + ret = !(ret & d_msk); + + return ret; +} + static int twl4030_set_gpio_dataout(int gpio, int enable) { u8 d_bnk = gpio >> 3; @@ -359,6 +376,28 @@ static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) return ret; } +static int twl_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); + /* + * Default 0 = output + * LED GPIOs >= TWL4030_GPIO_MAX are always output + */ + int ret = 0; + + mutex_lock(&priv->mutex); + if (offset < TWL4030_GPIO_MAX) { + ret = twl4030_get_gpio_direction(offset); + if (ret) { + mutex_unlock(&priv->mutex); + return ret; + } + } + mutex_unlock(&priv->mutex); + + return ret; +} + static int twl_to_irq(struct gpio_chip *chip, unsigned offset) { struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); @@ -374,8 +413,9 @@ static const struct gpio_chip template_chip = { .request = twl_request, .free = twl_free, .direction_input = twl_direction_in, - .get = twl_get, .direction_output = twl_direction_out, + .get_direction = twl_get_direction, + .get = twl_get, .set = twl_set, .to_irq = twl_to_irq, .can_sleep = true, -- cgit v1.2.3 From fc4f8f322f6e588edbecfaa82a987d46125aeb1f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 09:54:55 +0200 Subject: gpio: twl6040: Include the right header This is a GPIO driver so include only . Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl6040.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index dadeacf43e0c..095b20fd0315 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From b4f53ed9633cf3801d7f5a9aa3c317a4e3d2d0e3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 09:55:50 +0200 Subject: gpio: twl6040: Cut down boilerplate Use the SPDX header to indicate the license for this driver. Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl6040.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index 095b20fd0315..c0c18c80abbd 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Access to GPOs on TWL6040 chip * @@ -6,20 +7,6 @@ * Authors: * Sergio Aguirre * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -- cgit v1.2.3 From 4bef8bf20b69c3a98dbb9c8e406c22adb8fd3903 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 09:59:57 +0200 Subject: gpio: twl6040: Use bitops It's nice to use BIT() macros rather than open coding the same. It's good practice as sometimes people use BIT(31) and forget that the constant must be cast unsigned long. Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl6040.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index c0c18c80abbd..77d50542bf61 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -28,7 +29,7 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset) if (ret < 0) return ret; - return (ret >> offset) & 1; + return !!(ret & BIT(offset)); } static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, @@ -49,9 +50,9 @@ static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) return; if (value) - gpoctl = ret | (1 << offset); + gpoctl = ret | BIT(offset); else - gpoctl = ret & ~(1 << offset); + gpoctl = ret & ~BIT(offset); twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); } -- cgit v1.2.3 From ba74bd5d5b5b063d50891af16d5c14c29bcc52a6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Sep 2018 10:04:01 +0200 Subject: gpio: twl6040: Implement .get_direction() The gpiolib cannot deduce the fact that every line is output by itself, implement a .get_direction() callback so we can inspect this. Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl6040.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index 77d50542bf61..c845b2ff1f43 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -32,6 +32,12 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset) return !!(ret & BIT(offset)); } +static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset) +{ + /* This means "out" */ + return 0; +} + static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, int value) { @@ -62,6 +68,7 @@ static struct gpio_chip twl6040gpo_chip = { .owner = THIS_MODULE, .get = twl6040gpo_get, .direction_output = twl6040gpo_direction_out, + .get_direction = twl6040gpo_get_direction, .set = twl6040gpo_set, .can_sleep = true, }; -- cgit v1.2.3 From f40f73075c59b0d847eadbada9ec5c0d0142b107 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 6 Sep 2018 12:58:30 +0100 Subject: gpio: ep93xx: fix incorrect array element size check Currently the while loop checks for the end of the array using the size of egp->gc rather that the number of elements in the array, so fix this. Also, perform the array size check first as stylistically it is always good to bounds check on an array first before referencing the array (in this case, we're just computing the address of an element in an array so this is a moot point). Fixes: fd935fc421e7 ("gpio: ep93xx: Do not pingpong irq numbers") Signed-off-by: Colin Ian King Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 68a416fc3141..dd22ea19c3ed 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -76,7 +76,7 @@ static int ep93xx_gpio_port(struct gpio_chip *gc) struct ep93xx_gpio *epg = gpiochip_get_data(gc); int port = 0; - while (gc != &epg->gc[port] && port < sizeof(epg->gc)) + while (port < ARRAY_SIZE(epg->gc) && gc != &epg->gc[port]) port++; /* This should not happen but is there as a last safeguard */ -- cgit v1.2.3 From f6d9af4770995d1f58be57139bce7974868231e8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 6 Sep 2018 16:33:48 +0300 Subject: gpio: ep93xx: fix test for end of loop The problem is that if port == ARRAY_SIZE() and "gc == &epg->gc[port]" then that should be treated as invalid. Fixes: fd935fc421e7 ("gpio: ep93xx: Do not pingpong irq numbers") Signed-off-by: Dan Carpenter Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ep93xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index dd22ea19c3ed..71728d6e0bca 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -80,7 +80,7 @@ static int ep93xx_gpio_port(struct gpio_chip *gc) port++; /* This should not happen but is there as a last safeguard */ - if (gc != &epg->gc[port]) { + if (port == ARRAY_SIZE(epg->gc)) { pr_crit("can't find the GPIO port\n"); return 0; } -- cgit v1.2.3 From 4e6b823867e2b8afc2b33740ba930e50b1f92421 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Sep 2018 11:23:14 +0200 Subject: gpiolib: export gpiochip_irq_reqres/relres() GPIO drivers that do not use GPIOLIB_IRQCHIP can hook these into the irq_request_resource and irq_release_resource callbacks of the irq_chip so they correctly 'get' the module and lock the gpio line for IRQ use. This will simplify driver code. Signed-off-by: Hans Verkuil Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 55 +++++++++++++++++++++++++++------------------ include/linux/gpio/driver.h | 2 ++ 2 files changed, 35 insertions(+), 22 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e8f8a1999393..cbab0e744de0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1804,39 +1804,26 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; -static int gpiochip_irq_reqres(struct irq_data *d) +static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { - struct gpio_chip *chip = irq_data_get_irq_chip_data(d); - int ret; - - if (!try_module_get(chip->gpiodev->owner)) - return -ENODEV; + if (!gpiochip_irqchip_irq_valid(chip, offset)) + return -ENXIO; - ret = gpiochip_lock_as_irq(chip, d->hwirq); - if (ret) { - chip_err(chip, - "unable to lock HW IRQ %lu for IRQ\n", - d->hwirq); - module_put(chip->gpiodev->owner); - return ret; - } - return 0; + return irq_create_mapping(chip->irq.domain, offset); } -static void gpiochip_irq_relres(struct irq_data *d) +static int gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); - gpiochip_unlock_as_irq(chip, d->hwirq); - module_put(chip->gpiodev->owner); + return gpiochip_reqres_irq(chip, d->hwirq); } -static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) +static void gpiochip_irq_relres(struct irq_data *d) { - if (!gpiochip_irqchip_irq_valid(chip, offset)) - return -ENXIO; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); - return irq_create_mapping(chip->irq.domain, offset); + gpiochip_relres_irq(chip, d->hwirq); } /** @@ -3338,6 +3325,30 @@ bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_line_is_irq); +int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + + if (!try_module_get(chip->gpiodev->owner)) + return -ENODEV; + + ret = gpiochip_lock_as_irq(chip, offset); + if (ret) { + chip_err(chip, "unable to lock HW IRQ %u for IRQ\n", offset); + module_put(chip->gpiodev->owner); + return ret; + } + return 0; +} +EXPORT_SYMBOL_GPL(gpiochip_reqres_irq); + +void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset) +{ + gpiochip_unlock_as_irq(chip, offset); + module_put(chip->gpiodev->owner); +} +EXPORT_SYMBOL_GPL(gpiochip_relres_irq); + bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset) { if (offset >= chip->ngpio) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 4a4f410b5285..479f1ccaaebd 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -401,6 +401,8 @@ extern struct gpio_chip *gpiochip_find(void *data, int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset); void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset); +int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset); +void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset); /* Line status inquiry for drivers */ bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset); -- cgit v1.2.3 From ca620f2de153d690d6d6f50f32b8a14dd46107f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Sep 2018 11:23:15 +0200 Subject: gliolib: set hooks in gpiochip_set_irq_hooks() Centralize setting the irq_request/release_resources callbacks in one function since we'll be adding more callbacks to that. Also fix the removal of the callback overrides: this should only be done if we actually installed our own callback there. Signed-off-by: Hans Verkuil Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index cbab0e744de0..b17f1142c70b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1826,6 +1826,17 @@ static void gpiochip_irq_relres(struct irq_data *d) gpiochip_relres_irq(chip, d->hwirq); } +static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip) +{ + struct irq_chip *irqchip = gpiochip->irq.chip; + + if (!irqchip->irq_request_resources && + !irqchip->irq_release_resources) { + irqchip->irq_request_resources = gpiochip_irq_reqres; + irqchip->irq_release_resources = gpiochip_irq_relres; + } +} + /** * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip * @gpiochip: the GPIO chip to add the IRQ chip to @@ -1884,16 +1895,6 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, if (!gpiochip->irq.domain) return -EINVAL; - /* - * It is possible for a driver to override this, but only if the - * alternative functions are both implemented. - */ - if (!irqchip->irq_request_resources && - !irqchip->irq_release_resources) { - irqchip->irq_request_resources = gpiochip_irq_reqres; - irqchip->irq_release_resources = gpiochip_irq_relres; - } - if (gpiochip->irq.parent_handler) { void *data = gpiochip->irq.parent_handler_data ?: gpiochip; @@ -1909,6 +1910,8 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, } } + gpiochip_set_irq_hooks(gpiochip); + acpi_gpiochip_request_interrupts(gpiochip); return 0; @@ -1922,11 +1925,12 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, */ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) { + struct irq_chip *irqchip = gpiochip->irq.chip; unsigned int offset; acpi_gpiochip_free_interrupts(gpiochip); - if (gpiochip->irq.chip && gpiochip->irq.parent_handler) { + if (irqchip && gpiochip->irq.parent_handler) { struct gpio_irq_chip *irq = &gpiochip->irq; unsigned int i; @@ -1950,11 +1954,12 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) irq_domain_remove(gpiochip->irq.domain); } - if (gpiochip->irq.chip) { - gpiochip->irq.chip->irq_request_resources = NULL; - gpiochip->irq.chip->irq_release_resources = NULL; - gpiochip->irq.chip = NULL; + if (irqchip && + irqchip->irq_request_resources == gpiochip_irq_reqres) { + irqchip->irq_request_resources = NULL; + irqchip->irq_release_resources = NULL; } + gpiochip->irq.chip = NULL; gpiochip_irqchip_free_valid_mask(gpiochip); } @@ -2043,15 +2048,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, return -EINVAL; } - /* - * It is possible for a driver to override this, but only if the - * alternative functions are both implemented. - */ - if (!irqchip->irq_request_resources && - !irqchip->irq_release_resources) { - irqchip->irq_request_resources = gpiochip_irq_reqres; - irqchip->irq_release_resources = gpiochip_irq_relres; - } + gpiochip_set_irq_hooks(gpiochip); acpi_gpiochip_request_interrupts(gpiochip); -- cgit v1.2.3 From 4e9439ddacea06f35acce4d374bf6bd0acf99bc8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Sep 2018 11:23:16 +0200 Subject: gpiolib: add flag to indicate if the irq is disabled GPIO drivers call gpiochip_(un)lock_as_irq whenever they want to use a gpio as an interrupt. This is done when the irq is requested and it marks the gpio as in use by an interrupt. This is problematic for cases where a gpio pin is used as an interrupt pin, then, after the irq is disabled, is used as a regular gpio pin. Currently it is not possible to do this other than by first freeing the interrupt so gpiochip_unlock_as_irq is called, since an attempt to switch the gpio direction for output will fail since gpiolib believes that the gpio is in use for an interrupt and it does not know that it the irq is actually disabled. There are currently two drivers that would like to be able to do this: the tda998x_drv.c driver where a regular gpio pin needs to be temporarily reconfigured as an interrupt pin during CEC calibration, and the cec-gpio driver where you want to configure the gpio pin as an interrupt while waiting for traffic over the CEC bus, or as a regular pin when receiving or transmitting a CEC message. The solution is to add a new flag that is set when the irq is enabled, and have gpiod_direction_output check for that flag. We also add functions that drivers that do not use GPIOLIB_IRQCHIP can call when they enable/disable the irq. Signed-off-by: Hans Verkuil Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 29 +++++++++++++++++++++++++++-- drivers/gpio/gpiolib.h | 1 + include/linux/gpio/driver.h | 2 ++ 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b17f1142c70b..e52fa72f13d7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2588,8 +2588,9 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) else value = !!value; - /* GPIOs used for IRQs shall not be set as output */ - if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) { + /* GPIOs used for enabled IRQs shall not be set as output */ + if (test_bit(FLAG_USED_AS_IRQ, &desc->flags) && + test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) { gpiod_err(desc, "%s: tried to set a GPIO tied to an IRQ as output\n", __func__); @@ -3276,6 +3277,7 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset) } set_bit(FLAG_USED_AS_IRQ, &desc->flags); + set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); /* * If the consumer has not set up a label (such as when the @@ -3306,6 +3308,7 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) return; clear_bit(FLAG_USED_AS_IRQ, &desc->flags); + clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); /* If we only had this marking, erase it */ if (desc->label && !strcmp(desc->label, "interrupt")) @@ -3313,6 +3316,28 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); +void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct gpio_desc *desc = gpiochip_get_desc(chip, offset); + + if (!IS_ERR(desc) && + !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) + clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); +} +EXPORT_SYMBOL_GPL(gpiochip_disable_irq); + +void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct gpio_desc *desc = gpiochip_get_desc(chip, offset); + + if (!IS_ERR(desc) && + !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) { + WARN_ON(test_bit(FLAG_IS_OUT, &desc->flags)); + set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); + } +} +EXPORT_SYMBOL_GPL(gpiochip_enable_irq); + bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset) { if (offset >= chip->ngpio) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index a7e49fef73d4..e9a86f4b00e3 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -214,6 +214,7 @@ 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_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 479f1ccaaebd..9052ccd399fd 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -403,6 +403,8 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset); int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset); void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset); +void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset); +void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset); /* Line status inquiry for drivers */ bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset); -- cgit v1.2.3 From 461c1a7d4733d1dfd5c47b040cf32a5e7eefbc6c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Sep 2018 11:23:17 +0200 Subject: gpiolib: override irq_enable/disable When using the gpiolib irqchip helpers install irq_enable/disable hooks for the irqchip to ensure that gpiolib knows when the irq is enabled or disabled, allowing drivers to disable the irq and then use it as an output pin, and later switch the direction to input and re-enable the irq. Signed-off-by: Hans Verkuil Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 43 +++++++++++++++++++++++++++++++++++++++---- include/linux/gpio/driver.h | 14 ++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e52fa72f13d7..efce534a269b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1826,6 +1826,28 @@ static void gpiochip_irq_relres(struct irq_data *d) gpiochip_relres_irq(chip, d->hwirq); } +static void gpiochip_irq_enable(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + gpiochip_enable_irq(chip, d->hwirq); + if (chip->irq.irq_enable) + chip->irq.irq_enable(d); + else + chip->irq.chip->irq_unmask(d); +} + +static void gpiochip_irq_disable(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + if (chip->irq.irq_disable) + chip->irq.irq_disable(d); + else + chip->irq.chip->irq_mask(d); + gpiochip_disable_irq(chip, d->hwirq); +} + static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip) { struct irq_chip *irqchip = gpiochip->irq.chip; @@ -1835,6 +1857,12 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip) irqchip->irq_request_resources = gpiochip_irq_reqres; irqchip->irq_release_resources = gpiochip_irq_relres; } + if (WARN_ON(gpiochip->irq.irq_enable)) + return; + gpiochip->irq.irq_enable = irqchip->irq_enable; + gpiochip->irq.irq_disable = irqchip->irq_disable; + irqchip->irq_enable = gpiochip_irq_enable; + irqchip->irq_disable = gpiochip_irq_disable; } /** @@ -1954,11 +1982,18 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) irq_domain_remove(gpiochip->irq.domain); } - if (irqchip && - irqchip->irq_request_resources == gpiochip_irq_reqres) { - irqchip->irq_request_resources = NULL; - irqchip->irq_release_resources = NULL; + if (irqchip) { + if (irqchip->irq_request_resources == gpiochip_irq_reqres) { + irqchip->irq_request_resources = NULL; + irqchip->irq_release_resources = NULL; + } + if (irqchip->irq_enable == gpiochip_irq_enable) { + irqchip->irq_enable = gpiochip->irq.irq_enable; + irqchip->irq_disable = gpiochip->irq.irq_disable; + } } + gpiochip->irq.irq_enable = NULL; + gpiochip->irq.irq_disable = NULL; gpiochip->irq.chip = NULL; gpiochip_irqchip_free_valid_mask(gpiochip); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 9052ccd399fd..d8dcd0e44cab 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -144,6 +144,20 @@ struct gpio_irq_chip { * will allocate and map all IRQs during initialization. */ unsigned int first; + + /** + * @irq_enable: + * + * Store old irq_chip irq_enable callback + */ + void (*irq_enable)(struct irq_data *data); + + /** + * @irq_disable: + * + * Store old irq_chip irq_disable callback + */ + void (*irq_disable)(struct irq_data *data); }; static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) -- cgit v1.2.3 From 1c939cb556b99519cc5e75b66543ac499acfd781 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Sep 2018 11:23:19 +0200 Subject: gpio-bcm-kona: use new req/relres and dis/enable_irq funcs Since this driver does not use the gpiolib irqchip helpers it will have to allocate the irq resources and irq_en/disable itself. Use the new gpiochip_req/relres_irq helpers to request/release all the resources. Signed-off-by: Hans Verkuil Cc: Ray Jui Signed-off-by: Linus Walleij --- drivers/gpio/gpio-bcm-kona.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index d0707fc23afd..c5536a509b59 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -373,6 +373,7 @@ static void bcm_kona_gpio_irq_mask(struct irq_data *d) val = readl(reg_base + GPIO_INT_MASK(bank_id)); val |= BIT(bit); writel(val, reg_base + GPIO_INT_MASK(bank_id)); + gpiochip_disable_irq(&kona_gpio->gpio_chip, gpio); raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } @@ -394,6 +395,7 @@ static void bcm_kona_gpio_irq_unmask(struct irq_data *d) val = readl(reg_base + GPIO_INT_MSKCLR(bank_id)); val |= BIT(bit); writel(val, reg_base + GPIO_INT_MSKCLR(bank_id)); + gpiochip_enable_irq(&kona_gpio->gpio_chip, gpio); raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } @@ -485,23 +487,15 @@ static void bcm_kona_gpio_irq_handler(struct irq_desc *desc) static int bcm_kona_gpio_irq_reqres(struct irq_data *d) { struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d); - int ret; - ret = gpiochip_lock_as_irq(&kona_gpio->gpio_chip, d->hwirq); - if (ret) { - dev_err(kona_gpio->gpio_chip.parent, - "unable to lock HW IRQ %lu for IRQ\n", - d->hwirq); - return ret; - } - return 0; + return gpiochip_reqres_irq(&kona_gpio->gpio_chip, d->hwirq); } static void bcm_kona_gpio_irq_relres(struct irq_data *d) { struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d); - gpiochip_unlock_as_irq(&kona_gpio->gpio_chip, d->hwirq); + gpiochip_relres_irq(&kona_gpio->gpio_chip, d->hwirq); } static struct irq_chip bcm_gpio_irq_chip = { -- cgit v1.2.3 From 6953c57ab1721ce57914fc5741d0ce0568756bb0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 4 Sep 2018 11:01:58 +0200 Subject: gpio: of: Handle SPI chipselect legacy bindings The SPI chipselects are assumed to be active low in the current binding, so when we want to use GPIO descriptors and handle the active low/high semantics in gpiolib, we need a special parsing quirk to deal with this. We check for the property "spi-cs-high" and if that is NOT present we assume the CS line is active low. If the line is tagged as active low in the device tree and has no "spi-cs-high" property all is fine, the device tree and the SPI bindings are in agreement. If the line is tagged as active high in the device tree with the second cell flag and has no "spi-cs-high" property we enforce active low semantics (as this is the exception we can just tag on the flag). If the line is tagged as active low with the second cell flag AND tagged with "spi-cs-high" the SPI active high property takes precedence and we print a warning. Cc: Mark Brown Cc: linux-spi@vger.kernel.org Cc: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 50 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 089783b11e87..9985b748b14d 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -57,7 +57,8 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, } static void of_gpio_flags_quirks(struct device_node *np, - enum of_gpio_flags *flags) + enum of_gpio_flags *flags, + int index) { /* * Some GPIO fixed regulator quirks. @@ -91,6 +92,51 @@ static void of_gpio_flags_quirks(struct device_node *np, pr_info("%s uses legacy open drain flag - update the DTS if you can\n", of_node_full_name(np)); } + + /* + * Legacy handling of SPI active high chip select. If we have a + * property named "cs-gpios" we need to inspect the child node + * to determine if the flags should have inverted semantics. + */ + if (IS_ENABLED(CONFIG_SPI_MASTER) && + of_property_read_bool(np, "cs-gpios")) { + struct device_node *child; + u32 cs; + int ret; + + for_each_child_of_node(np, child) { + ret = of_property_read_u32(child, "reg", &cs); + if (!ret) + continue; + if (cs == index) { + /* + * SPI children have active low chip selects + * by default. This can be specified negatively + * by just omitting "spi-cs-high" in the + * device node, or actively by tagging on + * GPIO_ACTIVE_LOW as flag in the device + * tree. If the line is simultaneously + * tagged as active low in the device tree + * and has the "spi-cs-high" set, we get a + * conflict and the "spi-cs-high" flag will + * take precedence. + */ + if (of_property_read_bool(np, "spi-cs-high")) { + if (*flags & OF_GPIO_ACTIVE_LOW) { + pr_warn("%s GPIO handle specifies active low - ignored\n", + of_node_full_name(np)); + *flags &= ~OF_GPIO_ACTIVE_LOW; + } + } else { + if (!(*flags & OF_GPIO_ACTIVE_LOW)) + pr_info("%s enforce active low on chipselect handle\n", + of_node_full_name(np)); + *flags |= OF_GPIO_ACTIVE_LOW; + } + break; + } + } + } } /** @@ -131,7 +177,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, goto out; if (flags) - of_gpio_flags_quirks(np, flags); + of_gpio_flags_quirks(np, flags, index); pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n", __func__, propname, np, index, -- cgit v1.2.3 From 45e8296cc9a25e048c682794b11b27a0813a956f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 7 Sep 2018 22:03:07 +0200 Subject: gpio: vf610: Include the right header This is a GPIO driver so only include . Acked-by: Stefan Agner Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vf610.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index d4ad6d0e02a2..93eed646d92f 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 36e2add18225e33518a3383d8379edad7bc959f5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 7 Sep 2018 22:04:50 +0200 Subject: gpio: vf610: Cut down on boilerplate Just use the SPDX identifier for the license. Acked-by: Stefan Agner Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vf610.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 93eed646d92f..5960396c8d9a 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -1,20 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale vf610 GPIO support through PORT and GPIO * * Copyright (c) 2014 Toradex AG. * * Author: Stefan Agner . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * 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 -- cgit v1.2.3 From fa38869b0161484f28a7e1f34535d5825dca6488 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 20 Aug 2018 08:32:53 +0200 Subject: gpiolib: Don't support irq sharing for userspace This concerns gpio edge detection for GPIO IRQs used from userspace for GPIO event listeners. Trying to work out the right event if it's not sure that the examined gpio actually moved is impossible. Consider two gpios "gpioA" and "gpioB" that share an interrupt. gpioA's irq should trigger on any edge, gpioB's on a falling edge. If now the common irq fires and both gpio lines are high, there are several possibilities that could have happend: a) gpioA just had a low-to-high edge b) gpioB just had a high-to-low-to-high spike c) a combination of both a) and b) While c) is unlikely (in most setups) a) and b) alone are bad enough. Currently the code assumes case a) unconditionally and doesn't report an event for gpioB. Note that even if there is no irq sharing involved a spike for a gpio might not result in an event if it's configured to trigger for a single edge only. The only way to improve this is to drop support for interrupt sharing. This way a spike results in an event for the right gpio at least. Note that apart from dropping IRQF_SHARED this effectively undoes commit df1e76f28ffe ("gpiolib: skip unwanted events, don't convert them to opposite edge"). This obviously breaks setups that rely on interrupt sharing, but given that this cannot be reliable, this is probably an acceptable trade-off. Signed-off-by: Uwe Kleine-König [Assuming there are no users of interrupt sharing yet] Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index efce534a269b..8fbaea52bc1b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -812,26 +812,26 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) { struct lineevent_state *le = p; struct gpioevent_data ge; - int ret, level; + int ret; /* Do not leak kernel stack to userspace */ memset(&ge, 0, sizeof(ge)); ge.timestamp = le->timestamp; - level = gpiod_get_value_cansleep(le->desc); if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + int level = gpiod_get_value_cansleep(le->desc); if (level) /* Emit low-to-high event */ ge.id = GPIOEVENT_EVENT_RISING_EDGE; else /* Emit high-to-low event */ ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE && level) { + } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { /* Emit low-to-high event */ ge.id = GPIOEVENT_EVENT_RISING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE && !level) { + } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { /* Emit high-to-low event */ ge.id = GPIOEVENT_EVENT_FALLING_EDGE; } else { @@ -942,7 +942,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) irqflags |= IRQF_TRIGGER_FALLING; irqflags |= IRQF_ONESHOT; - irqflags |= IRQF_SHARED; INIT_KFIFO(le->events); init_waitqueue_head(&le->wait); -- cgit v1.2.3 From b9762bebc6332b40c33e03dea03e30fa12d9e3ed Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 5 Sep 2018 23:50:05 +0200 Subject: gpiolib: Pass bitmaps, not integer arrays, to get/set array Most users of get/set array functions iterate consecutive bits of data, usually a single integer, while processing array of results obtained from, or building an array of values to be passed to those functions. Save time wasted on those iterations by changing the functions' API to accept bitmaps. All current users are updated as well. More benefits from the change are expected as soon as planned support for accepting/passing those bitmaps directly from/to respective GPIO chip callbacks if applicable is implemented. Cc: Jonathan Corbet Cc: Miguel Ojeda Sandonis Cc: Sebastien Bourdelin Cc: Lukas Wunner Cc: Peter Korsgaard Cc: Peter Rosin Cc: Andrew Lunn Cc: Florian Fainelli Cc: "David S. Miller" Cc: Rojhalat Ibrahim Cc: Dominik Brodowski Cc: Russell King Cc: Kishon Vijay Abraham I Cc: Tony Lindgren Cc: Lars-Peter Clausen Cc: Michael Hennerich Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Peter Meerwald-Stadler Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Yegor Yefremov Cc: Uwe Kleine-König Signed-off-by: Janusz Krzysztofik Acked-by: Ulf Hansson Reviewed-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/consumer.rst | 22 ++++---- drivers/auxdisplay/hd44780.c | 59 +++++++-------------- drivers/bus/ts-nbus.c | 15 ++---- drivers/gpio/gpio-max3191x.c | 10 ++-- drivers/gpio/gpiolib.c | 82 +++++++++++++++-------------- drivers/gpio/gpiolib.h | 4 +- drivers/i2c/muxes/i2c-mux-gpio.c | 13 ++--- drivers/mmc/core/pwrseq_simple.c | 13 ++--- drivers/mux/gpio.c | 13 ++--- drivers/net/phy/mdio-mux-gpio.c | 11 ++-- drivers/pcmcia/soc_common.c | 7 +-- drivers/phy/motorola/phy-mapphone-mdm6600.c | 15 +++--- drivers/staging/iio/adc/ad7606.c | 9 ++-- drivers/tty/serial/serial_mctrl_gpio.c | 7 +-- include/linux/gpio/consumer.h | 34 ++++++------ 15 files changed, 137 insertions(+), 177 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index aa03f389d41d..ed68042ddccf 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -323,29 +323,29 @@ The following functions get or set the values of an array of GPIOs:: int gpiod_get_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) void gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) void gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) The array can be an arbitrary set of GPIOs. The functions will try to access GPIOs belonging to the same bank or chip simultaneously if supported by the @@ -356,8 +356,8 @@ accessed sequentially. The functions take three arguments: * array_size - the number of array elements * desc_array - an array of GPIO descriptors - * value_array - an array to store the GPIOs' values (get) or - an array of values to assign to the GPIOs (set) + * value_bitmap - a bitmap to store the GPIOs' values (get) or + a bitmap of values to assign to the GPIOs (set) The descriptor array can be obtained using the gpiod_get_array() function or one of its variants. If the group of descriptors returned by that function @@ -366,7 +366,7 @@ the struct gpio_descs returned by gpiod_get_array():: struct gpio_descs *my_gpio_descs = gpiod_get_array(...); gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc, - my_gpio_values); + my_gpio_value_bitmap); It is also possible to access a completely arbitrary array of descriptors. The descriptors may be obtained using any combination of gpiod_get() and diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c index f1a42f0f1ded..e9a893384362 100644 --- a/drivers/auxdisplay/hd44780.c +++ b/drivers/auxdisplay/hd44780.c @@ -62,17 +62,12 @@ static void hd44780_strobe_gpio(struct hd44780 *hd) /* write to an LCD panel register in 8 bit GPIO mode */ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs) { - int values[10]; /* for DATA[0-7], RS, RW */ - unsigned int i, n; - - for (i = 0; i < 8; i++) - values[PIN_DATA0 + i] = !!(val & BIT(i)); - values[PIN_CTRL_RS] = rs; - n = 9; - if (hd->pins[PIN_CTRL_RW]) { - values[PIN_CTRL_RW] = 0; - n++; - } + DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */ + unsigned int n; + + values[0] = val; + __assign_bit(8, values, rs); + n = hd->pins[PIN_CTRL_RW] ? 10 : 9; /* Present the data to the port */ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values); @@ -83,32 +78,25 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs) /* write to an LCD panel register in 4 bit GPIO mode */ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs) { - int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */ - unsigned int i, n; + DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */ + unsigned int n; /* High nibble + RS, RW */ - for (i = 4; i < 8; i++) - values[PIN_DATA0 + i] = !!(val & BIT(i)); - values[PIN_CTRL_RS] = rs; - n = 5; - if (hd->pins[PIN_CTRL_RW]) { - values[PIN_CTRL_RW] = 0; - n++; - } + values[0] = val >> 4; + __assign_bit(4, values, rs); + n = hd->pins[PIN_CTRL_RW] ? 6 : 5; /* Present the data to the port */ - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], - &values[PIN_DATA4]); + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values); hd44780_strobe_gpio(hd); /* Low nibble */ - for (i = 0; i < 4; i++) - values[PIN_DATA4 + i] = !!(val & BIT(i)); + values[0] &= ~0x0fUL; + values[0] |= val & 0x0f; /* Present the data to the port */ - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], - &values[PIN_DATA4]); + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values); hd44780_strobe_gpio(hd); } @@ -155,23 +143,16 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd) /* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd) { - int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */ + DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */ struct hd44780 *hd = lcd->drvdata; - unsigned int i, n; + unsigned int n; /* Command nibble + RS, RW */ - for (i = 0; i < 4; i++) - values[PIN_DATA4 + i] = !!(cmd & BIT(i)); - values[PIN_CTRL_RS] = 0; - n = 5; - if (hd->pins[PIN_CTRL_RW]) { - values[PIN_CTRL_RW] = 0; - n++; - } + values[0] = cmd & 0x0f; + n = hd->pins[PIN_CTRL_RW] ? 6 : 5; /* Present the data to the port */ - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], - &values[PIN_DATA4]); + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values); hd44780_strobe_gpio(hd); } diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c index 073fd9011154..8dde7c77f62c 100644 --- a/drivers/bus/ts-nbus.c +++ b/drivers/bus/ts-nbus.c @@ -110,11 +110,9 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction) */ static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus) { - int i; - int values[8]; + DECLARE_BITMAP(values, 8); - for (i = 0; i < 8; i++) - values[i] = 0; + values[0] = 0; gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values); gpiod_set_value_cansleep(ts_nbus->csn, 0); @@ -157,14 +155,9 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val) static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte) { struct gpio_descs *gpios = ts_nbus->data; - int i; - int values[8]; + DECLARE_BITMAP(values, 8); - for (i = 0; i < 8; i++) - if (byte & BIT(i)) - values[i] = 1; - else - values[i] = 0; + values[0] = byte; gpiod_set_array_value_cansleep(8, gpios->desc, values); } diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index b5b9cb1fda50..bd4a245fc5a0 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -315,14 +315,16 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs, struct gpio_desc **desc, int value) { - int i, *values; + unsigned long *values; - values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL); + values = bitmap_alloc(ndescs, GFP_KERNEL); if (!values) return; - for (i = 0; i < ndescs; i++) - values[i] = value; + if (value) + bitmap_fill(values, ndescs); + else + bitmap_zero(values, ndescs); gpiod_set_array_value_cansleep(ndescs, desc, values); kfree(values); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e8f8a1999393..b66b2191c5c5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -427,7 +427,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, struct linehandle_state *lh = filep->private_data; void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; - int vals[GPIOHANDLES_MAX]; + DECLARE_BITMAP(vals, GPIOHANDLES_MAX); int i; if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { @@ -442,7 +442,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, memset(&ghd, 0, sizeof(ghd)); for (i = 0; i < lh->numdescs; i++) - ghd.values[i] = vals[i]; + ghd.values[i] = test_bit(i, vals); if (copy_to_user(ip, &ghd, sizeof(ghd))) return -EFAULT; @@ -461,7 +461,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, /* Clamp all values to [0,1] */ for (i = 0; i < lh->numdescs; i++) - vals[i] = !!ghd.values[i]; + __assign_bit(i, vals, ghd.values[i]); /* Reuse the array setting function */ return gpiod_set_array_value_complex(false, @@ -2784,7 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip, int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { int i = 0; @@ -2835,7 +2835,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; - value_array[j] = value; + __assign_bit(j, value_bitmap, value); trace_gpio_value(desc_to_gpio(desc), 1, value); } @@ -2895,9 +2895,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value); /** * gpiod_get_raw_array_value() - read raw values from an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read - * @value_array: array to store the read values + * @value_bitmap: bitmap to store the read values * * Read the raw values of the GPIOs, i.e. the values of the physical lines * without regard for their ACTIVE_LOW status. Return 0 in case of success, @@ -2907,20 +2907,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value); * and it will complain if the GPIO chip functions potentially sleep. */ int gpiod_get_raw_array_value(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array) + struct gpio_desc **desc_array, + unsigned long *value_bitmap) { if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, false, array_size, - desc_array, value_array); + desc_array, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value); /** * gpiod_get_array_value() - read values from an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read - * @value_array: array to store the read values + * @value_bitmap: bitmap to store the read values * * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status * into account. Return 0 in case of success, else an error code. @@ -2929,12 +2930,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value); * and it will complain if the GPIO chip functions potentially sleep. */ int gpiod_get_array_value(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array) + struct gpio_desc **desc_array, + unsigned long *value_bitmap) { if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, false, array_size, - desc_array, value_array); + desc_array, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_array_value); @@ -3027,7 +3029,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { int i = 0; @@ -3056,7 +3058,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, do { struct gpio_desc *desc = desc_array[i]; int hwgpio = gpio_chip_hwgpio(desc); - int value = value_array[i]; + int value = test_bit(i, value_bitmap); if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; @@ -3152,9 +3154,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); /** * gpiod_set_raw_array_value() - assign values to an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned - * @value_array: array of values to assign + * @value_bitmap: bitmap of values to assign * * Set the raw values of the GPIOs, i.e. the values of the physical lines * without regard for their ACTIVE_LOW status. @@ -3163,20 +3165,21 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); * complain if the GPIO chip functions potentially sleep. */ int gpiod_set_raw_array_value(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array) + struct gpio_desc **desc_array, + unsigned long *value_bitmap) { if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, false, array_size, - desc_array, value_array); + desc_array, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); /** * gpiod_set_array_value() - assign values to an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned - * @value_array: array of values to assign + * @value_bitmap: bitmap of values to assign * * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status * into account. @@ -3185,12 +3188,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); * complain if the GPIO chip functions potentially sleep. */ void gpiod_set_array_value(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array) + struct gpio_desc **desc_array, + unsigned long *value_bitmap) { if (!desc_array) return; gpiod_set_array_value_complex(false, false, array_size, desc_array, - value_array); + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_array_value); @@ -3410,9 +3414,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); /** * gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read - * @value_array: array to store the read values + * @value_bitmap: bitmap to store the read values * * Read the raw values of the GPIOs, i.e. the values of the physical lines * without regard for their ACTIVE_LOW status. Return 0 in case of success, @@ -3422,21 +3426,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); */ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, true, array_size, - desc_array, value_array); + desc_array, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep); /** * gpiod_get_array_value_cansleep() - read values from an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read - * @value_array: array to store the read values + * @value_bitmap: bitmap to store the read values * * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status * into account. Return 0 in case of success, else an error code. @@ -3445,13 +3449,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep); */ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, true, array_size, - desc_array, value_array); + desc_array, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); @@ -3493,9 +3497,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); /** * gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned - * @value_array: array of values to assign + * @value_bitmap: bitmap of values to assign * * Set the raw values of the GPIOs, i.e. the values of the physical lines * without regard for their ACTIVE_LOW status. @@ -3504,13 +3508,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); */ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, true, array_size, desc_array, - value_array); + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep); @@ -3533,9 +3537,9 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) /** * gpiod_set_array_value_cansleep() - assign values to an array of GPIOs - * @array_size: number of elements in the descriptor / value arrays + * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned - * @value_array: array of values to assign + * @value_bitmap: bitmap of values to assign * * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status * into account. @@ -3544,13 +3548,13 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) */ void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return; gpiod_set_array_value_complex(false, true, array_size, desc_array, - value_array); + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index a7e49fef73d4..11e83d2eef89 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -187,11 +187,11 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); /* This is just passed between gpiolib and devres */ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 401308e3d036..d835857bb094 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -22,18 +22,15 @@ struct gpiomux { struct i2c_mux_gpio_platform_data data; unsigned gpio_base; struct gpio_desc **gpios; - int *values; }; static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) { - int i; + DECLARE_BITMAP(values, BITS_PER_TYPE(val)); - for (i = 0; i < mux->data.n_gpios; i++) - mux->values[i] = (val >> i) & 1; + values[0] = val; - gpiod_set_array_value_cansleep(mux->data.n_gpios, - mux->gpios, mux->values); + gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, values); } static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) @@ -182,15 +179,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) return -EPROBE_DEFER; muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, - mux->data.n_gpios * sizeof(*mux->gpios) + - mux->data.n_gpios * sizeof(*mux->values), 0, + mux->data.n_gpios * sizeof(*mux->gpios), 0, i2c_mux_gpio_select, NULL); if (!muxc) { ret = -ENOMEM; goto alloc_failed; } mux->gpios = muxc->priv; - mux->values = (int *)(mux->gpios + mux->data.n_gpios); muxc->priv = mux; platform_set_drvdata(pdev, muxc); diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index a8b9fee4d62a..902476ef9a0e 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -40,18 +40,13 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, struct gpio_descs *reset_gpios = pwrseq->reset_gpios; if (!IS_ERR(reset_gpios)) { - int i, *values; + DECLARE_BITMAP(values, BITS_PER_TYPE(value)); int nvalues = reset_gpios->ndescs; - values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL); - if (!values) - return; + values[0] = value; - for (i = 0; i < nvalues; i++) - values[i] = value; - - gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values); - kfree(values); + gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, + values); } } diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c index 6fdd9316db8b..46c44532fbd5 100644 --- a/drivers/mux/gpio.c +++ b/drivers/mux/gpio.c @@ -17,20 +17,17 @@ struct mux_gpio { struct gpio_descs *gpios; - int *val; }; static int mux_gpio_set(struct mux_control *mux, int state) { struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip); - int i; + DECLARE_BITMAP(values, BITS_PER_TYPE(state)); - for (i = 0; i < mux_gpio->gpios->ndescs; i++) - mux_gpio->val[i] = (state >> i) & 1; + values[0] = state; gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs, - mux_gpio->gpios->desc, - mux_gpio->val); + mux_gpio->gpios->desc, values); return 0; } @@ -58,13 +55,11 @@ static int mux_gpio_probe(struct platform_device *pdev) if (pins < 0) return pins; - mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) + - pins * sizeof(*mux_gpio->val)); + mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio)); if (IS_ERR(mux_chip)) return PTR_ERR(mux_chip); mux_gpio = mux_chip_priv(mux_chip); - mux_gpio->val = (int *)(mux_gpio + 1); mux_chip->ops = &mux_gpio_ops; mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW); diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index bc90764a8b8d..e25ccfc8c070 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -20,23 +20,21 @@ struct mdio_mux_gpio_state { struct gpio_descs *gpios; void *mux_handle; - int values[]; }; static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, void *data) { struct mdio_mux_gpio_state *s = data; - unsigned int n; + DECLARE_BITMAP(values, BITS_PER_TYPE(desired_child)); if (current_child == desired_child) return 0; - for (n = 0; n < s->gpios->ndescs; n++) - s->values[n] = (desired_child >> n) & 1; + values[0] = desired_child; gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc, - s->values); + values); return 0; } @@ -51,8 +49,7 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev) if (IS_ERR(gpios)) return PTR_ERR(gpios); - s = devm_kzalloc(&pdev->dev, struct_size(s, values, gpios->ndescs), - GFP_KERNEL); + s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); if (!s) { gpiod_put_array(gpios); return -ENOMEM; diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index c5f2344c189b..ac033d555700 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -351,15 +351,16 @@ static int soc_common_pcmcia_config_skt( if (ret == 0) { struct gpio_desc *descs[2]; - int values[2], n = 0; + DECLARE_BITMAP(values, 2); + int n = 0; if (skt->gpio_reset) { descs[n] = skt->gpio_reset; - values[n++] = !!(state->flags & SS_RESET); + __assign_bit(n++, values, state->flags & SS_RESET); } if (skt->gpio_bus_enable) { descs[n] = skt->gpio_bus_enable; - values[n++] = !!(state->flags & SS_OUTPUT_ENA); + __assign_bit(n++, values, state->flags & SS_OUTPUT_ENA); } if (n) diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 0075fb0bef8c..9162b61ddb95 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -157,12 +157,9 @@ static const struct phy_ops gpio_usb_ops = { */ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) { - int values[PHY_MDM6600_NR_CMD_LINES]; - int i; + DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES); - val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1; - for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++) - values[i] = (val & BIT(i)) >> i; + values[0] = val; gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES, ddata->cmd_gpios->desc, values); @@ -176,7 +173,7 @@ static void phy_mdm6600_status(struct work_struct *work) { struct phy_mdm6600 *ddata; struct device *dev; - int values[PHY_MDM6600_NR_STATUS_LINES]; + DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES); int error, i, val = 0; ddata = container_of(work, struct phy_mdm6600, status_work.work); @@ -189,11 +186,11 @@ static void phy_mdm6600_status(struct work_struct *work) return; for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) { - val |= values[i] << i; + val |= test_bit(i, values) << i; dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n", - __func__, i, values[i], val); + __func__, i, test_bit(i, values), val); } - ddata->status = val; + ddata->status = values[0]; dev_info(dev, "modem status: %i %s\n", ddata->status, diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c index 25b9fcd5e3a4..9c1d77d48700 100644 --- a/drivers/staging/iio/adc/ad7606.c +++ b/drivers/staging/iio/adc/ad7606.c @@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, long mask) { struct ad7606_state *st = iio_priv(indio_dev); - int values[3]; + DECLARE_BITMAP(values, 3); int ret, i; switch (mask) { @@ -227,13 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - values[0] = (ret >> 0) & 1; - values[1] = (ret >> 1) & 1; - values[2] = (ret >> 2) & 1; + values[0] = ret; mutex_lock(&st->lock); - gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc, - values); + gpiod_set_array_value(3, st->gpio_os->desc, values); st->oversampling = val; mutex_unlock(&st->lock); diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 1c06325beaca..7d9d2c7b6c39 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) { enum mctrl_gpio_idx i; struct gpio_desc *desc_array[UART_GPIO_MAX]; - int value_array[UART_GPIO_MAX]; + DECLARE_BITMAP(values, UART_GPIO_MAX); unsigned int count = 0; if (gpios == NULL) @@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) for (i = 0; i < UART_GPIO_MAX; i++) if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) { desc_array[count] = gpios->gpio[i]; - value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl); + __assign_bit(count, values, + mctrl & mctrl_gpios_desc[i].mctrl); count++; } - gpiod_set_array_value(count, desc_array, value_array); + gpiod_set_array_value(count, desc_array, values); } EXPORT_SYMBOL_GPL(mctrl_gpio_set); diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 21ddbe440030..2b0389f0bec4 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -104,36 +104,38 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value); /* Value get/set from non-sleeping context */ int gpiod_get_value(const struct gpio_desc *desc); int gpiod_get_array_value(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array); + struct gpio_desc **desc_array, + unsigned long *value_bitmap); void gpiod_set_value(struct gpio_desc *desc, int value); void gpiod_set_array_value(unsigned int array_size, - struct gpio_desc **desc_array, int *value_array); + struct gpio_desc **desc_array, + unsigned long *value_bitmap); int gpiod_get_raw_value(const struct gpio_desc *desc); int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); void gpiod_set_raw_value(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); /* Value get/set from sleeping context */ int gpiod_get_value_cansleep(const struct gpio_desc *desc); int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc); int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array); + unsigned long *value_bitmap); int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); @@ -330,7 +332,7 @@ static inline int gpiod_get_value(const struct gpio_desc *desc) } static inline int gpiod_get_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); @@ -343,7 +345,7 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value) } static inline void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); @@ -356,7 +358,7 @@ static inline int gpiod_get_raw_value(const struct gpio_desc *desc) } static inline int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); @@ -369,7 +371,7 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) } static inline int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); @@ -384,7 +386,7 @@ static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc) } static inline int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); @@ -397,7 +399,7 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) } static inline void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); @@ -410,7 +412,7 @@ static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) } static inline int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); @@ -424,7 +426,7 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, } static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, - int *value_array) + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); -- cgit v1.2.3 From bf9346f5d47b4506aafbc384dfb5796b0adc3f8d Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 5 Sep 2018 23:50:06 +0200 Subject: gpiolib: Identify arrays matching GPIO hardware Certain GPIO array lookup results may map directly to GPIO pins of a single GPIO chip in hardware order. If that condition is recognized and handled efficiently, significant performance gain of get/set array functions may be possible. While processing a request for an array of GPIO descriptors, identify those which represent corresponding pins of a single GPIO chip. Skip over pins which require open source or open drain special processing. Moreover, identify pins which require inversion. Pass a pointer to that information with the array to the caller so it can benefit from enhanced performance as soon as get/set array functions can accept and make efficient use of it. Cc: Jonathan Corbet Signed-off-by: Janusz Krzysztofik Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/consumer.rst | 4 +- drivers/gpio/gpiolib.c | 72 +++++++++++++++++++++++++++++- drivers/gpio/gpiolib.h | 9 ++++ include/linux/gpio/consumer.h | 9 ++++ 4 files changed, 92 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index ed68042ddccf..7e0298b9a7b9 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call:: enum gpiod_flags flags) This function returns a struct gpio_descs which contains an array of -descriptors:: +descriptors. It also contains a pointer to a gpiolib private structure which, +if passed back to get/set array functions, may speed up I/O proocessing:: struct gpio_descs { + struct gpio_array *info; unsigned int ndescs; struct gpio_desc *desc[]; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b66b2191c5c5..141f39308a53 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, { struct gpio_desc *desc; struct gpio_descs *descs; - int count; + struct gpio_array *array_info = NULL; + struct gpio_chip *chip; + int count, bitmap_size; count = gpiod_count(dev, con_id); if (count < 0) @@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, gpiod_put_array(descs); return ERR_CAST(desc); } + descs->desc[descs->ndescs] = desc; + + chip = gpiod_to_chip(desc); + /* + * Select a chip of first array member + * whose index matches its pin hardware number + * as a candidate for fast bitmap processing. + */ + if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) { + struct gpio_descs *array; + + bitmap_size = BITS_TO_LONGS(chip->ngpio > count ? + chip->ngpio : count); + + array = kzalloc(struct_size(descs, desc, count) + + struct_size(array_info, invert_mask, + 3 * bitmap_size), GFP_KERNEL); + if (!array) { + gpiod_put_array(descs); + return ERR_PTR(-ENOMEM); + } + + memcpy(array, descs, + struct_size(descs, desc, descs->ndescs + 1)); + kfree(descs); + + descs = array; + array_info = (void *)(descs->desc + count); + array_info->get_mask = array_info->invert_mask + + bitmap_size; + array_info->set_mask = array_info->get_mask + + bitmap_size; + + array_info->desc = descs->desc; + array_info->size = count; + array_info->chip = chip; + bitmap_set(array_info->get_mask, descs->ndescs, + count - descs->ndescs); + bitmap_set(array_info->set_mask, descs->ndescs, + count - descs->ndescs); + descs->info = array_info; + } + /* + * Unmark members which don't qualify for fast bitmap + * processing (different chip, not in hardware order) + */ + if (array_info && (chip != array_info->chip || + gpio_chip_hwgpio(desc) != descs->ndescs)) { + __clear_bit(descs->ndescs, array_info->get_mask); + __clear_bit(descs->ndescs, array_info->set_mask); + } else if (array_info) { + /* Exclude open drain or open source from fast output */ + if (gpiochip_line_is_open_drain(chip, descs->ndescs) || + gpiochip_line_is_open_source(chip, descs->ndescs)) + __clear_bit(descs->ndescs, + array_info->set_mask); + /* Identify 'fast' pins which require invertion */ + if (gpiod_is_active_low(desc)) + __set_bit(descs->ndescs, + array_info->invert_mask); + } + descs->ndescs++; } + if (array_info) + dev_dbg(dev, + "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n", + array_info->chip->label, array_info->size, + *array_info->get_mask, *array_info->set_mask, + *array_info->invert_mask); return descs; } EXPORT_SYMBOL_GPL(gpiod_get_array); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 11e83d2eef89..b60905d558b1 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev, } #endif +struct gpio_array { + struct gpio_desc **desc; + unsigned int size; + struct gpio_chip *chip; + unsigned long *get_mask; + unsigned long *set_mask; + unsigned long invert_mask[]; +}; + struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 2b0389f0bec4..0ffd71c0a77c 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -17,11 +17,20 @@ struct device; */ struct gpio_desc; +/** + * Opaque descriptor for a structure of GPIO array attributes. This structure + * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be + * passed back to get/set array functions in order to activate fast processing + * path if applicable. + */ +struct gpio_array; + /** * Struct containing an array of descriptors that can be obtained using * gpiod_get_array(). */ struct gpio_descs { + struct gpio_array *info; unsigned int ndescs; struct gpio_desc *desc[]; }; -- cgit v1.2.3 From 77588c14ac868caece82fddbfae7de03b2cec941 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 5 Sep 2018 23:50:07 +0200 Subject: gpiolib: Pass array info to get/set array functions In order to make use of array info obtained from gpiod_get_array() and speed up processing of arrays matching single GPIO chip layout, that information must be passed to get/set array functions. Extend the functions' API with that additional parameter and update all users. Pass NULL if a user builds an array itself from single GPIOs. Cc: Jonathan Corbet Cc: Miguel Ojeda Sandonis Cc: Geert Uytterhoeven Cc: Sebastien Bourdelin Cc: Lukas Wunner Cc: Peter Korsgaard Cc: Peter Rosin Cc: Andrew Lunn Cc: Florian Fainelli Cc: "David S. Miller" Cc: Rojhalat Ibrahim Cc: Dominik Brodowski Cc: Russell King Cc: Kishon Vijay Abraham I Cc: Tony Lindgren Cc: Lars-Peter Clausen Cc: Michael Hennerich Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Peter Meerwald-Stadler Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Yegor Yefremov Cc: Uwe Kleine-König Signed-off-by: Janusz Krzysztofik Acked-by: Ulf Hansson Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/consumer.rst | 14 ++++++++-- drivers/auxdisplay/hd44780.c | 8 +++--- drivers/bus/ts-nbus.c | 5 ++-- drivers/gpio/gpio-max3191x.c | 6 +++-- drivers/gpio/gpiolib.c | 40 +++++++++++++++++++++++------ drivers/gpio/gpiolib.h | 2 ++ drivers/i2c/muxes/i2c-mux-gpio.c | 3 ++- drivers/mmc/core/pwrseq_simple.c | 2 +- drivers/mux/gpio.c | 3 ++- drivers/net/phy/mdio-mux-gpio.c | 2 +- drivers/pcmcia/soc_common.c | 2 +- drivers/phy/motorola/phy-mapphone-mdm6600.c | 4 ++- drivers/staging/iio/adc/ad7606.c | 3 ++- drivers/tty/serial/serial_mctrl_gpio.c | 2 +- include/linux/gpio/consumer.h | 16 ++++++++++++ 15 files changed, 86 insertions(+), 26 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index 7e0298b9a7b9..0afd95a12b10 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -325,28 +325,36 @@ The following functions get or set the values of an array of GPIOs:: int gpiod_get_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) void gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) void gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) The array can be an arbitrary set of GPIOs. The functions will try to access @@ -358,6 +366,7 @@ accessed sequentially. The functions take three arguments: * array_size - the number of array elements * desc_array - an array of GPIO descriptors + * array_info - optional information obtained from gpiod_array_get() * value_bitmap - a bitmap to store the GPIOs' values (get) or a bitmap of values to assign to the GPIOs (set) @@ -368,12 +377,13 @@ the struct gpio_descs returned by gpiod_get_array():: struct gpio_descs *my_gpio_descs = gpiod_get_array(...); gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc, - my_gpio_value_bitmap); + my_gpio_descs->info, my_gpio_value_bitmap); It is also possible to access a completely arbitrary array of descriptors. The descriptors may be obtained using any combination of gpiod_get() and gpiod_get_array(). Afterwards the array of descriptors has to be setup -manually before it can be passed to one of the above functions. +manually before it can be passed to one of the above functions. In that case, +array_info should be set to NULL. Note that for optimal performance GPIOs belonging to the same chip should be contiguous within the array of descriptors. diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c index e9a893384362..9ad93ea42fdc 100644 --- a/drivers/auxdisplay/hd44780.c +++ b/drivers/auxdisplay/hd44780.c @@ -70,7 +70,7 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs) n = hd->pins[PIN_CTRL_RW] ? 10 : 9; /* Present the data to the port */ - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values); + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL, values); hd44780_strobe_gpio(hd); } @@ -87,7 +87,7 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs) n = hd->pins[PIN_CTRL_RW] ? 6 : 5; /* Present the data to the port */ - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values); + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values); hd44780_strobe_gpio(hd); @@ -96,7 +96,7 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs) values[0] |= val & 0x0f; /* Present the data to the port */ - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values); + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values); hd44780_strobe_gpio(hd); } @@ -152,7 +152,7 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd) n = hd->pins[PIN_CTRL_RW] ? 6 : 5; /* Present the data to the port */ - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values); + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values); hd44780_strobe_gpio(hd); } diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c index 8dde7c77f62c..9989ce904a37 100644 --- a/drivers/bus/ts-nbus.c +++ b/drivers/bus/ts-nbus.c @@ -114,7 +114,8 @@ static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus) values[0] = 0; - gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values); + gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, + ts_nbus->data->info, values); gpiod_set_value_cansleep(ts_nbus->csn, 0); gpiod_set_value_cansleep(ts_nbus->strobe, 0); gpiod_set_value_cansleep(ts_nbus->ale, 0); @@ -159,7 +160,7 @@ static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte) values[0] = byte; - gpiod_set_array_value_cansleep(8, gpios->desc, values); + gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values); } /* diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index bd4a245fc5a0..9a8876abeb57 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -313,6 +313,7 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset, static void gpiod_set_array_single_value_cansleep(unsigned int ndescs, struct gpio_desc **desc, + struct gpio_array *info, int value) { unsigned long *values; @@ -326,7 +327,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs, else bitmap_zero(values, ndescs); - gpiod_set_array_value_cansleep(ndescs, desc, values); + gpiod_set_array_value_cansleep(ndescs, desc, info, values); kfree(values); } @@ -399,7 +400,8 @@ static int max3191x_probe(struct spi_device *spi) if (max3191x->modesel_pins) gpiod_set_array_single_value_cansleep( max3191x->modesel_pins->ndescs, - max3191x->modesel_pins->desc, max3191x->mode); + max3191x->modesel_pins->desc, + max3191x->modesel_pins->info, max3191x->mode); max3191x->ignore_uv = device_property_read_bool(dev, "maxim,ignore-undervoltage"); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 141f39308a53..cd7c1deed132 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -436,6 +436,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, true, lh->numdescs, lh->descs, + NULL, vals); if (ret) return ret; @@ -468,6 +469,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, true, lh->numdescs, lh->descs, + NULL, vals); } return -EINVAL; @@ -2784,6 +2786,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip, int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { int i = 0; @@ -2897,6 +2900,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value); * gpiod_get_raw_array_value() - read raw values from an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap to store the read values * * Read the raw values of the GPIOs, i.e. the values of the physical lines @@ -2908,12 +2912,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_value); */ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, false, array_size, - desc_array, value_bitmap); + desc_array, array_info, + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value); @@ -2921,6 +2927,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value); * gpiod_get_array_value() - read values from an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap to store the read values * * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status @@ -2931,12 +2938,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value); */ int gpiod_get_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, false, array_size, - desc_array, value_bitmap); + desc_array, array_info, + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_array_value); @@ -3029,6 +3038,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { int i = 0; @@ -3156,6 +3166,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); * gpiod_set_raw_array_value() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap of values to assign * * Set the raw values of the GPIOs, i.e. the values of the physical lines @@ -3166,12 +3177,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); */ int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, false, array_size, - desc_array, value_bitmap); + desc_array, array_info, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); @@ -3179,6 +3191,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); * gpiod_set_array_value() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap of values to assign * * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status @@ -3189,12 +3202,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); */ void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { if (!desc_array) return; gpiod_set_array_value_complex(false, false, array_size, desc_array, - value_bitmap); + array_info, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_array_value); @@ -3416,6 +3430,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); * gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap to store the read values * * Read the raw values of the GPIOs, i.e. the values of the physical lines @@ -3426,13 +3441,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); */ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(true, true, array_size, - desc_array, value_bitmap); + desc_array, array_info, + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep); @@ -3440,6 +3457,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep); * gpiod_get_array_value_cansleep() - read values from an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be read + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap to store the read values * * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status @@ -3449,13 +3467,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep); */ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; return gpiod_get_array_value_complex(false, true, array_size, - desc_array, value_bitmap); + desc_array, array_info, + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); @@ -3499,6 +3519,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); * gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap of values to assign * * Set the raw values of the GPIOs, i.e. the values of the physical lines @@ -3508,13 +3529,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); */ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return -EINVAL; return gpiod_set_array_value_complex(true, true, array_size, desc_array, - value_bitmap); + array_info, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep); @@ -3539,6 +3561,7 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) * gpiod_set_array_value_cansleep() - assign values to an array of GPIOs * @array_size: number of elements in the descriptor array / value bitmap * @desc_array: array of GPIO descriptors whose values will be assigned + * @array_info: information on applicability of fast bitmap processing path * @value_bitmap: bitmap of values to assign * * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status @@ -3548,13 +3571,14 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) */ void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) return; gpiod_set_array_value_complex(false, true, array_size, desc_array, - value_bitmap); + array_info, value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b60905d558b1..b65ca896b24d 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -196,10 +196,12 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); /* This is just passed between gpiolib and devres */ diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index d835857bb094..13882a2a4f60 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -30,7 +30,8 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) values[0] = val; - gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, values); + gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, NULL, + values); } static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 902476ef9a0e..7f882a2bb872 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -46,7 +46,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, values[0] = value; gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, - values); + reset_gpios->info, values); } } diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c index 46c44532fbd5..02c1f2c014e8 100644 --- a/drivers/mux/gpio.c +++ b/drivers/mux/gpio.c @@ -27,7 +27,8 @@ static int mux_gpio_set(struct mux_control *mux, int state) values[0] = state; gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs, - mux_gpio->gpios->desc, values); + mux_gpio->gpios->desc, + mux_gpio->gpios->info, values); return 0; } diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index e25ccfc8c070..fe34576262bd 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -34,7 +34,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, values[0] = desired_child; gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc, - values); + s->gpios->info, values); return 0; } diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index ac033d555700..3a8c84bb174d 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -364,7 +364,7 @@ static int soc_common_pcmcia_config_skt( } if (n) - gpiod_set_array_value_cansleep(n, descs, values); + gpiod_set_array_value_cansleep(n, descs, NULL, values); /* * This really needs a better solution. The IRQ diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 9162b61ddb95..25d456a323c2 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -162,7 +162,8 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) values[0] = val; gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES, - ddata->cmd_gpios->desc, values); + ddata->cmd_gpios->desc, + ddata->cmd_gpios->info, values); } /** @@ -181,6 +182,7 @@ static void phy_mdm6600_status(struct work_struct *work) error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES, ddata->status_gpios->desc, + ddata->status_gpios->info, values); if (error) return; diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c index 9c1d77d48700..b7810b1aad07 100644 --- a/drivers/staging/iio/adc/ad7606.c +++ b/drivers/staging/iio/adc/ad7606.c @@ -230,7 +230,8 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, values[0] = ret; mutex_lock(&st->lock); - gpiod_set_array_value(3, st->gpio_os->desc, values); + gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info, + values); st->oversampling = val; mutex_unlock(&st->lock); diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 7d9d2c7b6c39..39ed56214cd3 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -53,7 +53,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) mctrl & mctrl_gpios_desc[i].mctrl); count++; } - gpiod_set_array_value(count, desc_array, values); + gpiod_set_array_value(count, desc_array, NULL, values); } EXPORT_SYMBOL_GPL(mctrl_gpio_set); diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 0ffd71c0a77c..d7fbe30ece84 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -114,36 +114,44 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value); int gpiod_get_value(const struct gpio_desc *desc); int gpiod_get_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); void gpiod_set_value(struct gpio_desc *desc, int value); void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_get_raw_value(const struct gpio_desc *desc); int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); void gpiod_set_raw_value(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); /* Value get/set from sleeping context */ int gpiod_get_value_cansleep(const struct gpio_desc *desc); int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc); int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); @@ -341,6 +349,7 @@ static inline int gpiod_get_value(const struct gpio_desc *desc) } static inline int gpiod_get_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ @@ -354,6 +363,7 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value) } static inline void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ @@ -367,6 +377,7 @@ static inline int gpiod_get_raw_value(const struct gpio_desc *desc) } static inline int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ @@ -380,6 +391,7 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) } static inline int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ @@ -395,6 +407,7 @@ static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc) } static inline int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ @@ -408,6 +421,7 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) } static inline void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ @@ -421,6 +435,7 @@ static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) } static inline int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ @@ -435,6 +450,7 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, } static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, + struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ -- cgit v1.2.3 From b17566a6b08be18ded5472a2e4bbc066593b3662 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 5 Sep 2018 23:50:08 +0200 Subject: gpiolib: Implement fast processing path in get/set array Certain GPIO descriptor arrays returned by gpio_get_array() may contain information on direct mapping of array members to pins of a single GPIO chip in hardware order. In such cases, bitmaps of values can be passed directly from/to the chip's .get/set_multiple() callbacks without wasting time on iterations. Add respective code to gpiod_get/set_array_bitmap_complex() functions. Pins not applicable for fast path are processed as before, skipping over the 'fast' ones. Cc: Jonathan Corbet Signed-off-by: Janusz Krzysztofik Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/board.rst | 15 ++++++ Documentation/driver-api/gpio/consumer.rst | 8 +++ drivers/gpio/gpiolib.c | 87 ++++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index 2c112553df84..c66821e033c2 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -193,3 +193,18 @@ And the table can be added to the board code as follows:: The line will be hogged as soon as the gpiochip is created or - in case the chip was created earlier - when the hog table is registered. + +Arrays of pins +-------------- +In addition to requesting pins belonging to a function one by one, a device may +also request an array of pins assigned to the function. The way those pins are +mapped to the device determines if the array qualifies for fast bitmap +processing. If yes, a bitmap is passed over get/set array functions directly +between a caller and a respective .get/set_multiple() callback of a GPIO chip. + +In order to qualify for fast bitmap processing, the pin mapping must meet the +following requirements: +- it must belong to the same chip as other 'fast' pins of the function, +- its index within the function must match its hardware number within the chip. + +Open drain and open source pins are excluded from fast bitmap output processing. diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index 0afd95a12b10..cf992e5ab976 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -388,6 +388,14 @@ array_info should be set to NULL. Note that for optimal performance GPIOs belonging to the same chip should be contiguous within the array of descriptors. +Still better performance may be achieved if array indexes of the descriptors +match hardware pin numbers of a single chip. If an array passed to a get/set +array function matches the one obtained from gpiod_get_array() and array_info +associated with the array is also passed, the function may take a fast bitmap +processing path, passing the value_bitmap argument directly to the respective +.get/set_multiple() callback of the chip. That allows for utilization of GPIO +banks as data I/O ports without much loss of performance. + The return value of gpiod_get_array_value() and its variants is 0 on success or negative on error. Note the difference to gpiod_get_value(), which returns 0 or 1 on success to convey the GPIO value. With the array functions, the GPIO diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index cd7c1deed132..d7532aa6c0bc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2789,7 +2789,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, struct gpio_array *array_info, unsigned long *value_bitmap) { - int i = 0; + int err, i = 0; + + /* + * Validate array_info against desc_array and its size. + * It should immediately follow desc_array if both + * have been obtained from the same gpiod_get_array() call. + */ + if (array_info && array_info->desc == desc_array && + array_size <= array_info->size && + (void *)array_info == desc_array + array_info->size) { + if (!can_sleep) + WARN_ON(array_info->chip->can_sleep); + + err = gpio_chip_get_multiple(array_info->chip, + array_info->get_mask, + value_bitmap); + if (err) + return err; + + if (!raw && !bitmap_empty(array_info->invert_mask, array_size)) + bitmap_xor(value_bitmap, value_bitmap, + array_info->invert_mask, array_size); + + if (bitmap_full(array_info->get_mask, array_size)) + return 0; + + i = find_first_zero_bit(array_info->get_mask, array_size); + } else { + array_info = NULL; + } while (i < array_size) { struct gpio_chip *chip = desc_array[i]->gdev->chip; @@ -2820,7 +2849,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, int hwgpio = gpio_chip_hwgpio(desc); __set_bit(hwgpio, mask); - i++; + + if (array_info) + find_next_zero_bit(array_info->get_mask, + array_size, i); + else + i++; } while ((i < array_size) && (desc_array[i]->gdev->chip == chip)); @@ -2831,7 +2865,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, return ret; } - for (j = first; j < i; j++) { + for (j = first; j < i; ) { const struct gpio_desc *desc = desc_array[j]; int hwgpio = gpio_chip_hwgpio(desc); int value = test_bit(hwgpio, bits); @@ -2840,6 +2874,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, value = !value; __assign_bit(j, value_bitmap, value); trace_gpio_value(desc_to_gpio(desc), 1, value); + + if (array_info) + find_next_zero_bit(array_info->get_mask, i, j); + else + j++; } if (mask != fastpath) @@ -3043,6 +3082,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, { int i = 0; + /* + * Validate array_info against desc_array and its size. + * It should immediately follow desc_array if both + * have been obtained from the same gpiod_get_array() call. + */ + if (array_info && array_info->desc == desc_array && + array_size <= array_info->size && + (void *)array_info == desc_array + array_info->size) { + if (!can_sleep) + WARN_ON(array_info->chip->can_sleep); + + if (!raw && !bitmap_empty(array_info->invert_mask, array_size)) + bitmap_xor(value_bitmap, value_bitmap, + array_info->invert_mask, array_size); + + gpio_chip_set_multiple(array_info->chip, array_info->set_mask, + value_bitmap); + + if (bitmap_full(array_info->set_mask, array_size)) + return 0; + + i = find_first_zero_bit(array_info->set_mask, array_size); + } else { + array_info = NULL; + } + while (i < array_size) { struct gpio_chip *chip = desc_array[i]->gdev->chip; unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)]; @@ -3070,7 +3135,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, int hwgpio = gpio_chip_hwgpio(desc); int value = test_bit(i, value_bitmap); - if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + /* + * Pins applicable for fast input but not for + * fast output processing may have been already + * inverted inside the fast path, skip them. + */ + if (!raw && !(array_info && + test_bit(i, array_info->invert_mask)) && + test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; trace_gpio_value(desc_to_gpio(desc), 0, value); /* @@ -3089,7 +3161,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, __clear_bit(hwgpio, bits); count++; } - i++; + + if (array_info) + find_next_zero_bit(array_info->set_mask, + array_size, i); + else + i++; } while ((i < array_size) && (desc_array[i]->gdev->chip == chip)); /* push collected bits to outputs */ -- cgit v1.2.3 From d0121b8548bcb9a640de38c13e239493c87c44f4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 11 Jul 2018 18:33:19 +0200 Subject: gpiolib: use better errno if get_direction is not available EINVAL is very generic, use ENOTSUPP in case the gpiochip does not provide this function. While removing the assignment from the 'status' variable, use better indentation in the declaration block. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8fbaea52bc1b..74f3af2d8ca0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -210,15 +210,15 @@ static int gpiochip_find_base(int ngpio) */ int gpiod_get_direction(struct gpio_desc *desc) { - struct gpio_chip *chip; - unsigned offset; - int status = -EINVAL; + struct gpio_chip *chip; + unsigned offset; + int status; chip = gpiod_to_chip(desc); offset = gpio_chip_hwgpio(desc); if (!chip->get_direction) - return status; + return -ENOTSUPP; status = chip->get_direction(chip, offset); if (status > 0) { -- cgit v1.2.3 From 171948ea33e14dd1e49ba6439eabe822449ffd21 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 14 Sep 2018 10:36:39 +0200 Subject: gpiolib: check if irqchip already has the irq hook replacements Some drivers use a single irqchip for multiple gpiochips. As a result the irqchip hooks are overridden for the first gpiochip that was added, but for the other gpiochip instances this should not happen again, otherwise we would go into an infinite recursion. Check for this, but also log a message that the driver should be fixed since this is bad practice. Signed-off-by: Hans Verkuil Tested-by: Heikki Krogerus Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 74f3af2d8ca0..940180b5704e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1858,6 +1858,16 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip) } if (WARN_ON(gpiochip->irq.irq_enable)) return; + /* Check if the irqchip already has this hook... */ + if (irqchip->irq_enable == gpiochip_irq_enable) { + /* + * ...and if so, give a gentle warning that this is bad + * practice. + */ + chip_info(gpiochip, + "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n"); + return; + } gpiochip->irq.irq_enable = irqchip->irq_enable; gpiochip->irq.irq_disable = irqchip->irq_disable; irqchip->irq_enable = gpiochip_irq_enable; -- cgit v1.2.3 From 3b3001cacdf4c95d611657c61e194f1da3c577f5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 13:14:33 +0200 Subject: gpio: vr41xx: Include the right header This is a GPIO driver so include only . Cc: Yoichi Yuasa Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vr41xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c index 027699cec911..7ffb58b0d239 100644 --- a/drivers/gpio/gpio-vr41xx.c +++ b/drivers/gpio/gpio-vr41xx.c @@ -21,7 +21,7 @@ */ #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 2abfb31410fa9e742b333b907a1ed86c4aa1ff63 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 13:15:50 +0200 Subject: gpio: vr41xx: Cut down on boilerplate This switches this file to use the SPDX license tag. Cc: Yoichi Yuasa Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vr41xx.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c index 7ffb58b0d239..7d40104b8586 100644 --- a/drivers/gpio/gpio-vr41xx.c +++ b/drivers/gpio/gpio-vr41xx.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for NEC VR4100 series General-purpose I/O Unit. * * Copyright (C) 2002 MontaVista Software Inc. * Author: Yoichi Yuasa * Copyright (C) 2003-2009 Yoichi Yuasa - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include -- cgit v1.2.3 From 2ab4a93980e4831ec901d040f792f72f84205c16 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 13:22:15 +0200 Subject: gpio: vr41xx: Delete vr41xx_gpio_pullupdown() callback This API is not used anywhere in the kernel and has remained unused for years after being introduced. Over time, we have developed a subsystem to deal with pin control and this now managed pull up/down. Delete the old and unused API. If this platform needs it, we should implement a proper pin controller for it instead. Cc: Yoichi Yuasa Cc: Ralf Baechle Acked-by: Paul Burton Signed-off-by: Linus Walleij --- arch/mips/include/asm/vr41xx/giu.h | 8 -------- drivers/gpio/gpio-vr41xx.c | 38 -------------------------------------- 2 files changed, 46 deletions(-) (limited to 'drivers/gpio') diff --git a/arch/mips/include/asm/vr41xx/giu.h b/arch/mips/include/asm/vr41xx/giu.h index 6a90bc1d916b..ecda4cf300de 100644 --- a/arch/mips/include/asm/vr41xx/giu.h +++ b/arch/mips/include/asm/vr41xx/giu.h @@ -51,12 +51,4 @@ typedef enum { extern void vr41xx_set_irq_level(unsigned int pin, irq_level_t level); -typedef enum { - GPIO_PULL_DOWN, - GPIO_PULL_UP, - GPIO_PULL_DISABLE, -} gpio_pull_t; - -extern int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull); - #endif /* __NEC_VR41XX_GIU_H */ diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c index 7d40104b8586..b13a49c89cc1 100644 --- a/drivers/gpio/gpio-vr41xx.c +++ b/drivers/gpio/gpio-vr41xx.c @@ -371,44 +371,6 @@ static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir) return 0; } -int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull) -{ - u16 reg, mask; - unsigned long flags; - - if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO) - return -EPERM; - - if (pin >= 15) - return -EINVAL; - - mask = 1 << pin; - - spin_lock_irqsave(&giu_lock, flags); - - if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) { - reg = giu_read(GIUTERMUPDN); - if (pull == GPIO_PULL_UP) - reg |= mask; - else - reg &= ~mask; - giu_write(GIUTERMUPDN, reg); - - reg = giu_read(GIUUSEUPDN); - reg |= mask; - giu_write(GIUUSEUPDN, reg); - } else { - reg = giu_read(GIUUSEUPDN); - reg &= ~mask; - giu_write(GIUUSEUPDN, reg); - } - - spin_unlock_irqrestore(&giu_lock, flags); - - return 0; -} -EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown); - static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin) { u16 reg, mask; -- cgit v1.2.3 From 83a4e2c50ef031a1f7a5d16434618d0d0c085334 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 13:34:40 +0200 Subject: gpio: xtensa: Include the right header This is a GPIO driver so include only . Acked-by: Baruch Siach Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xtensa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index f16c0427952e..d3834d727d3e 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include -- cgit v1.2.3 From 3edc688328127dea7c8b2d8bec24512ee070db1d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 13:36:08 +0200 Subject: gpio: xtensa: Cut down on boilerplate Just use the SPDX header for the license. Acked-by: Baruch Siach Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xtensa.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index d3834d727d3e..43d3fa5f511a 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2013 TangoTec Ltd. * Author: Baruch Siach * - * 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. - * * Driver for the Xtensa LX4 GPIO32 Option * * Documentation: Xtensa LX4 Microprocessor Data Book, Section 2.22 -- cgit v1.2.3 From d3007ecb5b87bc033b0e31d38931212bafb06077 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 13:47:35 +0200 Subject: gpio: viperboard: Include the right header This is a GPIO driver so include only . Cc: Lars Poeschel Signed-off-by: Linus Walleij --- drivers/gpio/gpio-viperboard.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index e6d1328dddfa..76c4aa0da5d4 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -19,9 +19,8 @@ #include #include #include - #include -#include +#include #include -- cgit v1.2.3 From 751107ef790ad3a7524d9b3aac74865ca2564e5e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 13:49:13 +0200 Subject: gpio: viperboard: Cut down on boilerplate Just use the SPDX header for the license. Cc: Lars Poeschel Signed-off-by: Linus Walleij --- drivers/gpio/gpio-viperboard.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index 76c4aa0da5d4..9b604f13e302 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -1,15 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Nano River Technologies viperboard GPIO lib driver * * (C) 2012 by Lemonage GmbH * Author: Lars Poeschel * 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 as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ #include -- cgit v1.2.3 From 3bca2d4e4e7b771135456f99aea5bfc86aa4667c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 14:11:59 +0200 Subject: gpio: vx855: Include the right header This is a GPIO driver so include only . Cc: Daniel Drake Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vx855.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 98a6f1fcc561..d229640b5865 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include -- cgit v1.2.3 From c872a20f5ba212b3c6729ea2d13a7c270fc49fe9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 14:13:17 +0200 Subject: gpio: vx855: Cut down on boilerplate Just use the SPDX header for the license. Cc: Daniel Drake Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vx855.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index d229640b5865..4ff146ca32fe 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO * @@ -5,24 +6,7 @@ * Copyright (C) 2010 One Laptop per Child * Author: Harald Welte * 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 as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * */ - #include #include #include -- cgit v1.2.3 From 121111d78431f503c21152e039b737ff44e3eec1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 14:30:59 +0200 Subject: gpio: xlp: Include the right header This is a GPIO driver so include only . Cc: Kamlakant Patel Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xlp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 8e4275eaa7d7..3335f6a090d7 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -12,7 +12,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include -- cgit v1.2.3 From 130a990b724cb9bb7fe783148b27c968a8a2505f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Sep 2018 14:32:07 +0200 Subject: gpio: xlp: Cut down on boilerplate Just use the SPDX license tag for this file. Cc: Kamlakant Patel Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xlp.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 3335f6a090d7..0a3607fd21af 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * 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 -- cgit v1.2.3 From 10833c4b6ec0bbb233521440911983db1cac1e94 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 13 Sep 2018 13:25:58 +0200 Subject: gpio: wm8xxx: Use the right header These are GPIO drivers so just include . Cc: patches@opensource.cirrus.com Cc: Charles Keepax Cc: Mark Brown Acked-by: Richard Fitzgerald Signed-off-by: Linus Walleij --- drivers/gpio/gpio-wm831x.c | 2 +- drivers/gpio/gpio-wm8350.c | 2 +- drivers/gpio/gpio-wm8994.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 324813e8304e..85cc11132f95 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c index e46752e73dd9..fc844f142842 100644 --- a/drivers/gpio/gpio-wm8350.c +++ b/drivers/gpio/gpio-wm8350.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index 1e35756ac55b..b78c1c6f1b96 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 66dbe750a96fe10c7270593cbffeb4701cfcd180 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 13 Sep 2018 13:28:13 +0200 Subject: gpio: wm8xxx: Cut down on boilerplate Just use the SPDX license tag for these drivers. Cc: patches@opensource.cirrus.com Cc: Charles Keepax Cc: Mark Brown Acked-by: Richard Fitzgerald Signed-off-by: Linus Walleij --- drivers/gpio/gpio-wm831x.c | 6 +----- drivers/gpio/gpio-wm8350.c | 6 +----- drivers/gpio/gpio-wm8994.c | 6 +----- 3 files changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 85cc11132f95..a3a32a77041f 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * gpiolib support for Wolfson WM831x PMICs * @@ -5,11 +6,6 @@ * * Author: Mark Brown * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ #include diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c index fc844f142842..460f0a4b04bd 100644 --- a/drivers/gpio/gpio-wm8350.c +++ b/drivers/gpio/gpio-wm8350.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * gpiolib support for Wolfson WM835x PMICs * @@ -5,11 +6,6 @@ * * Author: Mark Brown * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ #include diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index b78c1c6f1b96..9af89cf7f6bc 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * gpiolib support for Wolfson WM8994 * @@ -5,11 +6,6 @@ * * Author: Mark Brown * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ #include -- cgit v1.2.3 From f13a0b0bb46f07e5c154ae7944202d754007d362 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 13 Sep 2018 14:05:26 +0200 Subject: gpio: Get rid of legacy header A bunch of core gpiolib files still include the legacy API header for no good reason. After this only the gpiolib-legacy.c file includes it, which is fine. The sysfs ABI code has a pointless wrapper function around gpio_to_desc() we can just loose. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 1 - drivers/gpio/gpiolib-sysfs.c | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c48ed9d89ff5..79271e08eea5 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 3dbaf489a8a5..3d7c90fe7189 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -444,11 +443,6 @@ static struct attribute *gpiochip_attrs[] = { }; ATTRIBUTE_GROUPS(gpiochip); -static struct gpio_desc *gpio_to_valid_desc(int gpio) -{ - return gpio_is_valid(gpio) ? gpio_to_desc(gpio) : NULL; -} - /* * /sys/class/gpio/export ... write-only * integer N ... number of GPIO to export (full access) @@ -467,7 +461,7 @@ static ssize_t export_store(struct class *class, if (status < 0) goto done; - desc = gpio_to_valid_desc(gpio); + desc = gpio_to_desc(gpio); /* reject invalid GPIOs */ if (!desc) { pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); @@ -514,7 +508,7 @@ static ssize_t unexport_store(struct class *class, if (status < 0) goto done; - desc = gpio_to_valid_desc(gpio); + desc = gpio_to_desc(gpio); /* reject bogus commands (gpio_unexport ignores them) */ if (!desc) { pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); -- cgit v1.2.3 From 587f7a694f016b65f8de8ea8b5fb0402712b5475 Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Fri, 31 Aug 2018 14:13:23 -0500 Subject: gpio: davinci: Use dev name for label and automatic base selection Use dev_name to get a unique label and use -1 for a base to get our selection automatically. We pull in all GPIOs per chip now so this does not have the effect of out of order labels like before. We do these both together so we can drop all the static data in one patch. This also lets us normalize the return paths as we don't need any cleanup after this change. Signed-off-by: Andrew F. Davis Tested-by: Keerthy Acked-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index a5ece8ea79bc..14d1729927d3 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -41,7 +41,6 @@ struct davinci_gpio_regs { typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq); #define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */ -#define MAX_LABEL_SIZE 20 static void __iomem *gpio_base; static unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0}; @@ -166,14 +165,12 @@ of_err: static int davinci_gpio_probe(struct platform_device *pdev) { - static int ctrl_num, bank_base; int gpio, bank, i, ret = 0; unsigned int ngpio, nbank, nirq; struct davinci_gpio_controller *chips; struct davinci_gpio_platform_data *pdata; struct device *dev = &pdev->dev; struct resource *res; - char label[MAX_LABEL_SIZE]; pdata = davinci_gpio_get_pdata(pdev); if (!pdata) { @@ -228,10 +225,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) } } - snprintf(label, MAX_LABEL_SIZE, "davinci_gpio.%d", ctrl_num++); - chips->chip.label = devm_kstrdup(dev, label, GFP_KERNEL); - if (!chips->chip.label) - return -ENOMEM; + chips->chip.label = dev_name(dev); chips->chip.direction_input = davinci_direction_in; chips->chip.get = davinci_gpio_get; @@ -239,7 +233,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) chips->chip.set = davinci_gpio_set; chips->chip.ngpio = ngpio; - chips->chip.base = bank_base; + chips->chip.base = -1; #ifdef CONFIG_OF_GPIO chips->chip.of_gpio_n_cells = 2; @@ -252,28 +246,20 @@ static int davinci_gpio_probe(struct platform_device *pdev) } #endif spin_lock_init(&chips->lock); - bank_base += ngpio; for (gpio = 0, bank = 0; gpio < ngpio; gpio += 32, bank++) chips->regs[bank] = gpio_base + offset_array[bank]; ret = devm_gpiochip_add_data(dev, &chips->chip, chips); if (ret) - goto err; + return ret; platform_set_drvdata(pdev, chips); ret = davinci_gpio_irq_setup(pdev); if (ret) - goto err; + return ret; return 0; - -err: - /* Revert the static variable increments */ - ctrl_num--; - bank_base -= ngpio; - - return ret; } /*--------------------------------------------------------------------------*/ -- cgit v1.2.3 From c809e37a3b5a889063ae737ff8b54705219d1ddc Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Fri, 31 Aug 2018 14:13:24 -0500 Subject: gpio: davinci: Allocate the correct amount of memory for controller Previously we created a controller structure per bank of GPIO pins. This has since been changed to one per controller, but the allocation size was not changed. Fix this here. This also leaves the variable 'nbank' unused, instead of removing it, move it down and use it to clean up a loop. For loops with multiple initializers and/or iteration expressions, especially ones that don't use those loop counters are quite hard to follow, fix this. Signed-off-by: Andrew F. Davis Tested-by: Keerthy Acked-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 14d1729927d3..121a7948f785 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -165,7 +165,7 @@ of_err: static int davinci_gpio_probe(struct platform_device *pdev) { - int gpio, bank, i, ret = 0; + int bank, i, ret = 0; unsigned int ngpio, nbank, nirq; struct davinci_gpio_controller *chips; struct davinci_gpio_platform_data *pdata; @@ -204,10 +204,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) else nirq = DIV_ROUND_UP(ngpio, 16); - nbank = DIV_ROUND_UP(ngpio, 32); - chips = devm_kcalloc(dev, - nbank, sizeof(struct davinci_gpio_controller), - GFP_KERNEL); + chips = devm_kzalloc(dev, sizeof(*chips), GFP_KERNEL); if (!chips) return -ENOMEM; @@ -247,7 +244,8 @@ static int davinci_gpio_probe(struct platform_device *pdev) #endif spin_lock_init(&chips->lock); - for (gpio = 0, bank = 0; gpio < ngpio; gpio += 32, bank++) + nbank = DIV_ROUND_UP(ngpio, 32); + for (bank = 0; bank < nbank; bank++) chips->regs[bank] = gpio_base + offset_array[bank]; ret = devm_gpiochip_add_data(dev, &chips->chip, chips); -- cgit v1.2.3 From 79b73ff9b2a3ef312d8fa30894fd963c80ded466 Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Fri, 31 Aug 2018 14:13:26 -0500 Subject: gpio: davinci: Move driver local definitions to driver These defines, structs and inline functions are used only internally by the driver, they do not belong in platform_data. Move them. Signed-off-by: Andrew F. Davis Tested-by: Keerthy Acked-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 28 ++++++++++++++++++++++++++++ include/linux/platform_data/gpio-davinci.h | 28 ---------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 121a7948f785..5c1564fcc24e 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -9,6 +9,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ + #include #include #include @@ -24,6 +25,12 @@ #include #include #include +#include + +#include + +#define MAX_REGS_BANKS 5 +#define MAX_INT_PER_BANK 32 struct davinci_gpio_regs { u32 dir; @@ -45,6 +52,27 @@ typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq); static void __iomem *gpio_base; static unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0}; +struct davinci_gpio_irq_data { + void __iomem *regs; + struct davinci_gpio_controller *chip; + int bank_num; +}; + +struct davinci_gpio_controller { + struct gpio_chip chip; + struct irq_domain *irq_domain; + /* Serialize access to GPIO registers */ + spinlock_t lock; + void __iomem *regs[MAX_REGS_BANKS]; + int gpio_unbanked; + int irqs[MAX_INT_PER_BANK]; +}; + +static inline u32 __gpio_mask(unsigned gpio) +{ + return 1 << (gpio % 32); +} + static inline struct davinci_gpio_regs __iomem *irq2regs(struct irq_data *d) { struct davinci_gpio_regs __iomem *g; diff --git a/include/linux/platform_data/gpio-davinci.h b/include/linux/platform_data/gpio-davinci.h index 47695b342883..f92a47e18034 100644 --- a/include/linux/platform_data/gpio-davinci.h +++ b/include/linux/platform_data/gpio-davinci.h @@ -16,40 +16,12 @@ #ifndef __DAVINCI_GPIO_PLATFORM_H #define __DAVINCI_GPIO_PLATFORM_H -#include -#include - -#include - -#define MAX_REGS_BANKS 5 -#define MAX_INT_PER_BANK 32 - struct davinci_gpio_platform_data { u32 ngpio; u32 gpio_unbanked; }; -struct davinci_gpio_irq_data { - void __iomem *regs; - struct davinci_gpio_controller *chip; - int bank_num; -}; - -struct davinci_gpio_controller { - struct gpio_chip chip; - struct irq_domain *irq_domain; - /* Serialize access to GPIO registers */ - spinlock_t lock; - void __iomem *regs[MAX_REGS_BANKS]; - int gpio_unbanked; - int irqs[MAX_INT_PER_BANK]; -}; - /* Convert GPIO signal to GPIO pin number */ #define GPIO_TO_PIN(bank, gpio) (16 * (bank) + (gpio)) -static inline u32 __gpio_mask(unsigned gpio) -{ - return 1 << (gpio % 32); -} #endif -- cgit v1.2.3 From 212d7069617c9b9565ffee17c4db6b8423557460 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 23 Sep 2018 16:07:07 +0200 Subject: gpio: htc-egpio: Unique label per chip Give the HTC EGPIO chips unique names, htc-egpio-0, htc-egpio-1 etc, so that it gets possible to associate machine descriptor tables with individual chips. Signed-off-by: Linus Walleij --- drivers/gpio/gpio-htc-egpio.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index ad6e5b518669..38be44dbd6e1 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -334,7 +334,13 @@ static int __init egpio_probe(struct platform_device *pdev) ei->chip[i].is_out = pdata->chip[i].direction; ei->chip[i].dev = &(pdev->dev); chip = &(ei->chip[i].chip); - chip->label = "htc-egpio"; + chip->label = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "htc-egpio-%d", + i); + if (!chip->label) { + ret = -ENOMEM; + goto fail; + } chip->parent = &pdev->dev; chip->owner = THIS_MODULE; chip->get = egpio_get; -- cgit v1.2.3 From 35ae7f9694e3c059a9d6a6049c7c171c8233c0c9 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Mon, 24 Sep 2018 01:53:35 +0200 Subject: gpiolib: Fix missing updates of bitmap index In new code introduced by commit b17566a6b08b ("gpiolib: Implement fast processing path in get/set array"), bitmap index is not updated with next found zero bit position as it should while skipping over pins already processed via fast bitmap path, possibly resulting in an infinite loop. Fix it. Signed-off-by: Janusz Krzysztofik Tested-by: Marek Szyprowski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ce7fa2db26a7..aa0b4b46fccc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2880,8 +2880,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, __set_bit(hwgpio, mask); if (array_info) - find_next_zero_bit(array_info->get_mask, - array_size, i); + i = find_next_zero_bit(array_info->get_mask, + array_size, i); else i++; } while ((i < array_size) && @@ -2905,7 +2905,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, trace_gpio_value(desc_to_gpio(desc), 1, value); if (array_info) - find_next_zero_bit(array_info->get_mask, i, j); + j = find_next_zero_bit(array_info->get_mask, i, + j); else j++; } @@ -3192,8 +3193,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, } if (array_info) - find_next_zero_bit(array_info->set_mask, - array_size, i); + i = find_next_zero_bit(array_info->set_mask, + array_size, i); else i++; } while ((i < array_size) && -- cgit v1.2.3 From c4c958aa64f31a962ddfdfb2ea628a25c774df25 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Mon, 24 Sep 2018 01:53:36 +0200 Subject: gpiolib: Fix array members of same chip processed separately New code introduced by commit bf9346f5d47b ("gpiolib: Identify arrays matching GPIO hardware") forcibly tries to find an array member which has its array index number equal to its hardware pin number and set up an array info for possible fast bitmap processing of all arrray pins belonging to that chip which also satisfy that numbering rule. Depending on array content, it may happen that consecutive array members which belong to the same chip but don't have array indexes equal to their pin hardware numbers will be split into groups, some of them processed together via the fast bitmap path, and rest of them separetely. However, applications may expect all those pins being processed together with a single call to .set_multiple() chip callback, like that was done before the change. Limit applicability of fast bitmap processing path to cases where all pins of consecutive array members starting from 0 which belong to the same chip have their hardware numbers equal to their corresponding array indexes. That should still speed up processing of applications using whole GPIO banks as I/O ports, while not breaking simultaneous manipulation of consecutive pins of the same chip which don't follow the equal numbering rule. Cc: Jonathan Corbet Signed-off-by: Janusz Krzysztofik Tested-by: Marek Szyprowski Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/board.rst | 19 +++++++++++++----- drivers/gpio/gpiolib.c | 35 +++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 15 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index c66821e033c2..a0f294e2e250 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -202,9 +202,18 @@ mapped to the device determines if the array qualifies for fast bitmap processing. If yes, a bitmap is passed over get/set array functions directly between a caller and a respective .get/set_multiple() callback of a GPIO chip. -In order to qualify for fast bitmap processing, the pin mapping must meet the +In order to qualify for fast bitmap processing, the array must meet the following requirements: -- it must belong to the same chip as other 'fast' pins of the function, -- its index within the function must match its hardware number within the chip. - -Open drain and open source pins are excluded from fast bitmap output processing. +- pin hardware number of array member 0 must also be 0, +- pin hardware numbers of consecutive array members which belong to the same + chip as member 0 does must also match their array indexes. + +Otherwise fast bitmap processing path is not used in order to avoid consecutive +pins which belong to the same chip but are not in hardware order being processed +separately. + +If the array applies for fast bitmap processing path, pins which belong to +different chips than member 0 does, as well as those with indexes different from +their hardware pin numbers, are excluded from the fast path, both input and +output. Moreover, open drain and open source pins are excluded from fast bitmap +output processing. diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index aa0b4b46fccc..03d15d84bdd8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4376,11 +4376,10 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, chip = gpiod_to_chip(desc); /* - * Select a chip of first array member - * whose index matches its pin hardware number - * as a candidate for fast bitmap processing. + * If pin hardware number of array member 0 is also 0, select + * its chip as a candidate for fast bitmap processing path. */ - if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) { + if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) { struct gpio_descs *array; bitmap_size = BITS_TO_LONGS(chip->ngpio > count ? @@ -4414,14 +4413,30 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, count - descs->ndescs); descs->info = array_info; } - /* - * Unmark members which don't qualify for fast bitmap - * processing (different chip, not in hardware order) - */ - if (array_info && (chip != array_info->chip || - gpio_chip_hwgpio(desc) != descs->ndescs)) { + /* Unmark array members which don't belong to the 'fast' chip */ + if (array_info && array_info->chip != chip) { __clear_bit(descs->ndescs, array_info->get_mask); __clear_bit(descs->ndescs, array_info->set_mask); + } + /* + * Detect array members which belong to the 'fast' chip + * but their pins are not in hardware order. + */ + else if (array_info && + gpio_chip_hwgpio(desc) != descs->ndescs) { + /* + * Don't use fast path if all array members processed so + * far belong to the same chip as this one but its pin + * hardware number is different from its array index. + */ + if (bitmap_full(array_info->get_mask, descs->ndescs)) { + array_info = NULL; + } else { + __clear_bit(descs->ndescs, + array_info->get_mask); + __clear_bit(descs->ndescs, + array_info->set_mask); + } } else if (array_info) { /* Exclude open drain or open source from fast output */ if (gpiochip_line_is_open_drain(chip, descs->ndescs) || -- cgit v1.2.3 From ec0daae685b20f3bee196719f64d79d1cc40457e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 20 Sep 2018 12:35:30 -0700 Subject: gpio: omap: Add level wakeup handling for omap4 based SoCs I noticed that unlike omap2 and 3 based SoCs, omap4 based SoCs keep the GPIO clocks enabled for GPIO level interrupts with wakeup enabled. This blocks deeper idle states as the whole domain will stay busy. The GPIO functional clock seems to stay enabled if the wakeup register is enabled and a level interrupt is triggered. In that case the only way to have the GPIO module idle is to reset it. It is possible this has gone unnoticed with OSWR (Open SWitch Retention) and off mode during idle resetting GPIO context most GPIO instances in the earlier Android trees for example. Looks like the way to deal with this is to have omap4 based SoCs only set wake for the duration of idle for level interrupts, and clear level registers for the idle. With level interrupts we can do this as the level interrupt from device will be still there on resume. I've taken the long path to fixing this to avoid yet more hard to read code. I've set up a quirks flag, and a struct for function pointers so we can use these to clean up other quirk handling easier in the later patches. The current level quirk handling is moved to the new functions. Cc: Aaro Koskinen Cc: Ladislav Michl Cc: Tero Kristo Signed-off-by: Tony Lindgren Acked-by: Grygorii Strashko Tested-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 151 +++++++++++++++++++++++++------- include/linux/platform_data/gpio-omap.h | 2 + 2 files changed, 120 insertions(+), 33 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index e81008678a38..d0051ef53cc4 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -31,6 +31,8 @@ #define OFF_MODE 1 #define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF +#define OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN BIT(1) + static LIST_HEAD(omap_gpio_list); struct gpio_regs { @@ -48,6 +50,13 @@ struct gpio_regs { u32 debounce_en; }; +struct gpio_bank; + +struct gpio_omap_funcs { + void (*idle_enable_level_quirk)(struct gpio_bank *bank); + void (*idle_disable_level_quirk)(struct gpio_bank *bank); +}; + struct gpio_bank { struct list_head node; void __iomem *base; @@ -55,6 +64,7 @@ struct gpio_bank { u32 non_wakeup_gpios; u32 enabled_non_wakeup_gpios; struct gpio_regs context; + struct gpio_omap_funcs funcs; u32 saved_datain; u32 level_mask; u32 toggle_mask; @@ -75,6 +85,7 @@ struct gpio_bank { int context_loss_count; int power_mode; bool workaround_enabled; + u32 quirks; void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable); void (*set_dataout_multiple)(struct gpio_bank *bank, @@ -368,9 +379,18 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio, readl_relaxed(bank->base + bank->regs->fallingdetect); if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { - omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0); - bank->context.wake_en = - readl_relaxed(bank->base + bank->regs->wkup_en); + /* Defer wkup_en register update until we idle? */ + if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) { + if (trigger) + bank->context.wake_en |= gpio_bit; + else + bank->context.wake_en &= ~gpio_bit; + } else { + omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, + trigger != 0); + bank->context.wake_en = + readl_relaxed(bank->base + bank->regs->wkup_en); + } } /* This part needs to be executed always for OMAP{34xx, 44xx} */ @@ -899,6 +919,82 @@ static void omap_gpio_unmask_irq(struct irq_data *d) raw_spin_unlock_irqrestore(&bank->lock, flags); } +/* + * Only edges can generate a wakeup event to the PRCM. + * + * Therefore, ensure any wake-up capable GPIOs have + * edge-detection enabled before going idle to ensure a wakeup + * to the PRCM is generated on a GPIO transition. (c.f. 34xx + * NDA TRM 25.5.3.1) + * + * The normal values will be restored upon ->runtime_resume() + * by writing back the values saved in bank->context. + */ +static void __maybe_unused +omap2_gpio_enable_level_quirk(struct gpio_bank *bank) +{ + u32 wake_low, wake_hi; + + /* Enable additional edge detection for level gpios for idle */ + wake_low = bank->context.leveldetect0 & bank->context.wake_en; + if (wake_low) + writel_relaxed(wake_low | bank->context.fallingdetect, + bank->base + bank->regs->fallingdetect); + + wake_hi = bank->context.leveldetect1 & bank->context.wake_en; + if (wake_hi) + writel_relaxed(wake_hi | bank->context.risingdetect, + bank->base + bank->regs->risingdetect); +} + +static void __maybe_unused +omap2_gpio_disable_level_quirk(struct gpio_bank *bank) +{ + /* Disable edge detection for level gpios after idle */ + writel_relaxed(bank->context.fallingdetect, + bank->base + bank->regs->fallingdetect); + writel_relaxed(bank->context.risingdetect, + bank->base + bank->regs->risingdetect); +} + +/* + * On omap4 and later SoC variants a level interrupt with wkup_en + * enabled blocks the GPIO functional clock from idling until the GPIO + * instance has been reset. To avoid that, we must set wkup_en only for + * idle for level interrupts, and clear level registers for the duration + * of idle. The level interrupts will be still there on wakeup by their + * nature. + */ +static void __maybe_unused +omap4_gpio_enable_level_quirk(struct gpio_bank *bank) +{ + /* Update wake register for idle, edge bits might be already set */ + writel_relaxed(bank->context.wake_en, + bank->base + bank->regs->wkup_en); + + /* Clear level registers for idle */ + writel_relaxed(0, bank->base + bank->regs->leveldetect0); + writel_relaxed(0, bank->base + bank->regs->leveldetect1); +} + +static void __maybe_unused +omap4_gpio_disable_level_quirk(struct gpio_bank *bank) +{ + /* Restore level registers after idle */ + writel_relaxed(bank->context.leveldetect0, + bank->base + bank->regs->leveldetect0); + writel_relaxed(bank->context.leveldetect1, + bank->base + bank->regs->leveldetect1); + + /* Clear saved wkup_en for level, it will be set for next idle again */ + bank->context.wake_en &= ~(bank->context.leveldetect0 | + bank->context.leveldetect1); + + /* Update wake with only edge configuration */ + writel_relaxed(bank->context.wake_en, + bank->base + bank->regs->wkup_en); +} + /*---------------------------------------------------------------------*/ static int omap_mpuio_suspend_noirq(struct device *dev) @@ -1270,6 +1366,7 @@ static int omap_gpio_probe(struct platform_device *pdev) bank->chip.parent = dev; bank->chip.owner = THIS_MODULE; bank->dbck_flag = pdata->dbck_flag; + bank->quirks = pdata->quirks; bank->stride = pdata->bank_stride; bank->width = pdata->bank_width; bank->is_mpuio = pdata->is_mpuio; @@ -1278,6 +1375,7 @@ static int omap_gpio_probe(struct platform_device *pdev) #ifdef CONFIG_OF_GPIO bank->chip.of_node = of_node_get(node); #endif + if (node) { if (!of_property_read_bool(node, "ti,gpio-always-on")) bank->loses_context = true; @@ -1298,6 +1396,18 @@ static int omap_gpio_probe(struct platform_device *pdev) omap_set_gpio_dataout_mask_multiple; } + if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) { + bank->funcs.idle_enable_level_quirk = + omap4_gpio_enable_level_quirk; + bank->funcs.idle_disable_level_quirk = + omap4_gpio_disable_level_quirk; + } else { + bank->funcs.idle_enable_level_quirk = + omap2_gpio_enable_level_quirk; + bank->funcs.idle_disable_level_quirk = + omap2_gpio_disable_level_quirk; + } + raw_spin_lock_init(&bank->lock); raw_spin_lock_init(&bank->wa_lock); @@ -1372,29 +1482,11 @@ static int omap_gpio_runtime_suspend(struct device *dev) struct gpio_bank *bank = platform_get_drvdata(pdev); u32 l1 = 0, l2 = 0; unsigned long flags; - u32 wake_low, wake_hi; raw_spin_lock_irqsave(&bank->lock, flags); - /* - * Only edges can generate a wakeup event to the PRCM. - * - * Therefore, ensure any wake-up capable GPIOs have - * edge-detection enabled before going idle to ensure a wakeup - * to the PRCM is generated on a GPIO transition. (c.f. 34xx - * NDA TRM 25.5.3.1) - * - * The normal values will be restored upon ->runtime_resume() - * by writing back the values saved in bank->context. - */ - wake_low = bank->context.leveldetect0 & bank->context.wake_en; - if (wake_low) - writel_relaxed(wake_low | bank->context.fallingdetect, - bank->base + bank->regs->fallingdetect); - wake_hi = bank->context.leveldetect1 & bank->context.wake_en; - if (wake_hi) - writel_relaxed(wake_hi | bank->context.risingdetect, - bank->base + bank->regs->risingdetect); + if (bank->funcs.idle_enable_level_quirk) + bank->funcs.idle_enable_level_quirk(bank); if (!bank->enabled_non_wakeup_gpios) goto update_gpio_context_count; @@ -1459,16 +1551,8 @@ static int omap_gpio_runtime_resume(struct device *dev) omap_gpio_dbck_enable(bank); - /* - * In ->runtime_suspend(), level-triggered, wakeup-enabled - * GPIOs were set to edge trigger also in order to be able to - * generate a PRCM wakeup. Here we restore the - * pre-runtime_suspend() values for edge triggering. - */ - writel_relaxed(bank->context.fallingdetect, - bank->base + bank->regs->fallingdetect); - writel_relaxed(bank->context.risingdetect, - bank->base + bank->regs->risingdetect); + if (bank->funcs.idle_disable_level_quirk) + bank->funcs.idle_disable_level_quirk(bank); if (bank->loses_context) { if (!bank->get_context_loss_count) { @@ -1706,6 +1790,7 @@ static const struct omap_gpio_platform_data omap4_pdata = { .regs = &omap4_gpio_regs, .bank_width = 32, .dbck_flag = true, + .quirks = OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN, }; static const struct of_device_id omap_gpio_match[] = { diff --git a/include/linux/platform_data/gpio-omap.h b/include/linux/platform_data/gpio-omap.h index 8612855691b2..d952151b6e33 100644 --- a/include/linux/platform_data/gpio-omap.h +++ b/include/linux/platform_data/gpio-omap.h @@ -197,6 +197,8 @@ struct omap_gpio_platform_data { bool is_mpuio; /* whether the bank is of type MPUIO */ u32 non_wakeup_gpios; + u32 quirks; /* Version specific quirks mask */ + struct omap_gpio_reg_offs *regs; /* Return context loss count due to PM states changing */ -- cgit v1.2.3 From b764a5863fd834836e65e258303e3a4a81c20f38 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 20 Sep 2018 12:35:31 -0700 Subject: gpio: omap: Remove custom PM calls and use cpu_pm instead For a long time the gpio-omap custom PM calls have been annoying me so let's replace them with cpu_pm instead. This will enable GPIO PM for deeper idle states on omap4. And we can handle GPIO PM for omap2/3/4 in the same way. Note that with this patch we are also slightly changing GPIO PM to be less aggressive for omap3 and only will idle GPIO when PER context may be lost. For omap2, we don't need to save context and don't want to remove any triggering so let's add a quirk flag for that. Let's do this all in a single patch to avoid a situation where old custom calls still are used with new code. Cc: Aaro Koskinen Cc: Keerthy Cc: Ladislav Michl Cc: Tero Kristo Signed-off-by: Tony Lindgren Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- arch/arm/mach-omap2/pm24xx.c | 7 +- arch/arm/mach-omap2/pm34xx.c | 14 +-- drivers/gpio/gpio-omap.c | 178 +++++++++++++++++++------------- include/linux/platform_data/gpio-omap.h | 13 --- 4 files changed, 116 insertions(+), 96 deletions(-) (limited to 'drivers/gpio') diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c index 2a1a4180d5d0..1298b53ac263 100644 --- a/arch/arm/mach-omap2/pm24xx.c +++ b/arch/arm/mach-omap2/pm24xx.c @@ -18,6 +18,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -29,8 +30,6 @@ #include #include #include -#include -#include #include @@ -87,7 +86,7 @@ static int omap2_enter_full_retention(void) l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL; omap_ctrl_writel(l, OMAP2_CONTROL_DEVCONF0); - omap2_gpio_prepare_for_idle(0); + cpu_cluster_pm_enter(); /* One last check for pending IRQs to avoid extra latency due * to sleeping unnecessarily. */ @@ -100,7 +99,7 @@ static int omap2_enter_full_retention(void) OMAP_SDRC_REGADDR(SDRC_POWER)); no_sleep: - omap2_gpio_resume_after_idle(); + cpu_cluster_pm_exit(); clk_enable(osc_ck); diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 36c55547137c..1a90050361f1 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -18,19 +18,18 @@ * published by the Free Software Foundation. */ +#include #include #include #include #include #include #include -#include #include #include #include #include #include -#include #include @@ -197,7 +196,6 @@ void omap_sram_idle(void) int mpu_next_state = PWRDM_POWER_ON; int per_next_state = PWRDM_POWER_ON; int core_next_state = PWRDM_POWER_ON; - int per_going_off; u32 sdrc_pwr = 0; mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); @@ -227,10 +225,8 @@ void omap_sram_idle(void) pwrdm_pre_transition(NULL); /* PER */ - if (per_next_state < PWRDM_POWER_ON) { - per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0; - omap2_gpio_prepare_for_idle(per_going_off); - } + if (per_next_state == PWRDM_POWER_OFF) + cpu_cluster_pm_enter(); /* CORE */ if (core_next_state < PWRDM_POWER_ON) { @@ -295,8 +291,8 @@ void omap_sram_idle(void) pwrdm_post_transition(NULL); /* PER */ - if (per_next_state < PWRDM_POWER_ON) - omap2_gpio_resume_after_idle(); + if (per_next_state == PWRDM_POWER_OFF) + cpu_cluster_pm_exit(); } static void omap3_pm_idle(void) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index d0051ef53cc4..3d021f648c5d 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -28,9 +29,9 @@ #include #include -#define OFF_MODE 1 #define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF +#define OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER BIT(2) #define OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN BIT(1) static LIST_HEAD(omap_gpio_list); @@ -72,6 +73,8 @@ struct gpio_bank { raw_spinlock_t wa_lock; struct gpio_chip chip; struct clk *dbck; + struct notifier_block nb; + unsigned int is_suspended:1; u32 mod_usage; u32 irq_usage; u32 dbck_enable_mask; @@ -83,7 +86,6 @@ struct gpio_bank { int stride; u32 width; int context_loss_count; - int power_mode; bool workaround_enabled; u32 quirks; @@ -1314,6 +1316,38 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) return ret; } +static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context); +static void omap_gpio_unidle(struct gpio_bank *bank); + +static int gpio_omap_cpu_notifier(struct notifier_block *nb, + unsigned long cmd, void *v) +{ + struct gpio_bank *bank; + struct device *dev; + unsigned long flags; + + bank = container_of(nb, struct gpio_bank, nb); + dev = bank->chip.parent; + + raw_spin_lock_irqsave(&bank->lock, flags); + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + if (bank->is_suspended) + break; + omap_gpio_idle(bank, true); + break; + case CPU_CLUSTER_PM_ENTER_FAILED: + case CPU_CLUSTER_PM_EXIT: + if (bank->is_suspended) + break; + omap_gpio_unidle(bank); + break; + } + raw_spin_unlock_irqrestore(&bank->lock, flags); + + return NOTIFY_OK; +} + static const struct of_device_id omap_gpio_match[]; static int omap_gpio_probe(struct platform_device *pdev) @@ -1401,7 +1435,7 @@ static int omap_gpio_probe(struct platform_device *pdev) omap4_gpio_enable_level_quirk; bank->funcs.idle_disable_level_quirk = omap4_gpio_disable_level_quirk; - } else { + } else if (bank->quirks & OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER) { bank->funcs.idle_enable_level_quirk = omap2_gpio_enable_level_quirk; bank->funcs.idle_disable_level_quirk = @@ -1451,6 +1485,12 @@ static int omap_gpio_probe(struct platform_device *pdev) omap_gpio_show_rev(bank); + if (bank->funcs.idle_enable_level_quirk && + bank->funcs.idle_disable_level_quirk) { + bank->nb.notifier_call = gpio_omap_cpu_notifier; + cpu_pm_register_notifier(&bank->nb); + } + pm_runtime_put(dev); list_add_tail(&bank->node, &omap_gpio_list); @@ -1462,6 +1502,8 @@ static int omap_gpio_remove(struct platform_device *pdev) { struct gpio_bank *bank = platform_get_drvdata(pdev); + if (bank->nb.notifier_call) + cpu_pm_unregister_notifier(&bank->nb); list_del(&bank->node); gpiochip_remove(&bank->chip); pm_runtime_disable(&pdev->dev); @@ -1471,19 +1513,12 @@ static int omap_gpio_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_ARCH_OMAP2PLUS - -#if defined(CONFIG_PM) static void omap_gpio_restore_context(struct gpio_bank *bank); -static int omap_gpio_runtime_suspend(struct device *dev) +static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context) { - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); + struct device *dev = bank->chip.parent; u32 l1 = 0, l2 = 0; - unsigned long flags; - - raw_spin_lock_irqsave(&bank->lock, flags); if (bank->funcs.idle_enable_level_quirk) bank->funcs.idle_enable_level_quirk(bank); @@ -1491,10 +1526,9 @@ static int omap_gpio_runtime_suspend(struct device *dev) if (!bank->enabled_non_wakeup_gpios) goto update_gpio_context_count; - if (bank->power_mode != OFF_MODE) { - bank->power_mode = 0; + if (!may_lose_context) goto update_gpio_context_count; - } + /* * If going to OFF, remove triggering for all * non-wakeup GPIOs. Otherwise spurious IRQs will be @@ -1519,23 +1553,16 @@ update_gpio_context_count: bank->get_context_loss_count(dev); omap_gpio_dbck_disable(bank); - raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; } static void omap_gpio_init_context(struct gpio_bank *p); -static int omap_gpio_runtime_resume(struct device *dev) +static void omap_gpio_unidle(struct gpio_bank *bank) { - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); + struct device *dev = bank->chip.parent; u32 l = 0, gen, gen0, gen1; - unsigned long flags; int c; - raw_spin_lock_irqsave(&bank->lock, flags); - /* * On the first resume during the probe, the context has not * been initialised and so initialise it now. Also initialise @@ -1562,16 +1589,13 @@ static int omap_gpio_runtime_resume(struct device *dev) if (c != bank->context_loss_count) { omap_gpio_restore_context(bank); } else { - raw_spin_unlock_irqrestore(&bank->lock, flags); - return 0; + return; } } } - if (!bank->workaround_enabled) { - raw_spin_unlock_irqrestore(&bank->lock, flags); - return 0; - } + if (!bank->workaround_enabled) + return; l = readl_relaxed(bank->base + bank->regs->datain); @@ -1624,41 +1648,8 @@ static int omap_gpio_runtime_resume(struct device *dev) } bank->workaround_enabled = false; - raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; } -#endif /* CONFIG_PM */ - -#if IS_BUILTIN(CONFIG_GPIO_OMAP) -void omap2_gpio_prepare_for_idle(int pwr_mode) -{ - struct gpio_bank *bank; - list_for_each_entry(bank, &omap_gpio_list, node) { - if (!BANK_USED(bank) || !bank->loses_context) - continue; - - bank->power_mode = pwr_mode; - - pm_runtime_put_sync_suspend(bank->chip.parent); - } -} - -void omap2_gpio_resume_after_idle(void) -{ - struct gpio_bank *bank; - - list_for_each_entry(bank, &omap_gpio_list, node) { - if (!BANK_USED(bank) || !bank->loses_context) - continue; - - pm_runtime_get_sync(bank->chip.parent); - } -} -#endif - -#if defined(CONFIG_PM) static void omap_gpio_init_context(struct gpio_bank *p) { struct omap_gpio_reg_offs *regs = p->regs; @@ -1715,17 +1706,57 @@ static void omap_gpio_restore_context(struct gpio_bank *bank) writel_relaxed(bank->context.irqenable2, bank->base + bank->regs->irqenable2); } -#endif /* CONFIG_PM */ -#else -#define omap_gpio_runtime_suspend NULL -#define omap_gpio_runtime_resume NULL -static inline void omap_gpio_init_context(struct gpio_bank *p) {} -#endif +static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + unsigned long flags; + int error = 0; + + raw_spin_lock_irqsave(&bank->lock, flags); + /* Must be idled only by CPU_CLUSTER_PM_ENTER? */ + if (bank->irq_usage) { + error = -EBUSY; + goto unlock; + } + omap_gpio_idle(bank, true); + bank->is_suspended = true; +unlock: + raw_spin_unlock_irqrestore(&bank->lock, flags); + + return error; +} + +static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + unsigned long flags; + int error = 0; + + raw_spin_lock_irqsave(&bank->lock, flags); + /* Must be unidled only by CPU_CLUSTER_PM_ENTER? */ + if (bank->irq_usage) { + error = -EBUSY; + goto unlock; + } + omap_gpio_unidle(bank); + bank->is_suspended = false; +unlock: + raw_spin_unlock_irqrestore(&bank->lock, flags); + + return error; +} + +#ifdef CONFIG_ARCH_OMAP2PLUS static const struct dev_pm_ops gpio_pm_ops = { SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL) }; +#else +static const struct dev_pm_ops gpio_pm_ops; +#endif /* CONFIG_ARCH_OMAP2PLUS */ #if defined(CONFIG_OF) static struct omap_gpio_reg_offs omap2_gpio_regs = { @@ -1774,6 +1805,11 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = { .fallingdetect = OMAP4_GPIO_FALLINGDETECT, }; +/* + * Note that omap2 does not currently support idle modes with context loss so + * no need to add OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER quirk flag to save + * and restore context. + */ static const struct omap_gpio_platform_data omap2_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, @@ -1784,13 +1820,15 @@ static const struct omap_gpio_platform_data omap3_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = true, + .quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER, }; static const struct omap_gpio_platform_data omap4_pdata = { .regs = &omap4_gpio_regs, .bank_width = 32, .dbck_flag = true, - .quirks = OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN, + .quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER | + OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN, }; static const struct of_device_id omap_gpio_match[] = { diff --git a/include/linux/platform_data/gpio-omap.h b/include/linux/platform_data/gpio-omap.h index d952151b6e33..8485c6a9a383 100644 --- a/include/linux/platform_data/gpio-omap.h +++ b/include/linux/platform_data/gpio-omap.h @@ -205,17 +205,4 @@ 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); -#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 5284521a290efd5418de79c205b612537ab2b12d Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 20 Sep 2018 12:35:32 -0700 Subject: gpio: omap: Get rid of pm_runtime_irq_safe() If a gpio instance has any GPIO bits requested we do a pm_runtime_get() on the device. Now with cpu_pm handling the deeper SoC idle state quirks, let's just remove pm_runtime_irq_safe() call and add a warning in case we ever happen to encounter it. Cc: Aaro Koskinen Cc: Keerthy Cc: Ladislav Michl Cc: Tero Kristo Signed-off-by: Tony Lindgren Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 3d021f648c5d..c0d7ae766de4 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -763,7 +763,9 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank) if (WARN_ON(!isr_reg)) goto exit; - pm_runtime_get_sync(bank->chip.parent); + if (WARN_ONCE(!pm_runtime_active(bank->chip.parent), + "gpio irq%i while runtime suspended?\n", irq)) + return IRQ_NONE; while (1) { raw_spin_lock_irqsave(&bank->lock, lock_flags); @@ -814,7 +816,6 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank) } } exit: - pm_runtime_put(bank->chip.parent); return IRQ_HANDLED; } @@ -1466,7 +1467,6 @@ static int omap_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bank); pm_runtime_enable(dev); - pm_runtime_irq_safe(dev); pm_runtime_get_sync(dev); if (bank->is_mpuio) -- cgit v1.2.3 From dae5f0afcfc35ff64dfb65cddc6842ceeeca68c4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 25 Sep 2018 09:08:48 +0200 Subject: gpio: Use SPDX header for core library Use the SPDX headers and cut down on boilerplate to indicate the license in the core gpiolib implementation. Signed-off-by: Linus Walleij --- drivers/gpio/devres.c | 12 ++---------- drivers/gpio/gpio-mmio.c | 6 +----- drivers/gpio/gpiolib-acpi.c | 5 +---- drivers/gpio/gpiolib-devprop.c | 5 +---- drivers/gpio/gpiolib-legacy.c | 1 + drivers/gpio/gpiolib-of.c | 6 +----- drivers/gpio/gpiolib-sysfs.c | 1 + drivers/gpio/gpiolib.c | 1 + drivers/gpio/gpiolib.h | 5 +---- 9 files changed, 10 insertions(+), 32 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index e82cc763633c..01959369360b 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -1,14 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * drivers/gpio/devres.c - managed gpio resources - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * devres.c - managed gpio resources * This file is based on kernel/irq/devres.c * * Copyright (c) 2011 John Crispin diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 935292a30c99..50bdc29591c0 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Generic driver for memory-mapped GPIO controllers. * * Copyright 2008 MontaVista Software, Inc. * Copyright 2008,2010 Anton Vorontsov * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * * ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`....... * ...`` ```````.. * ..The simplest form of a GPIO controller that the driver supports is`` diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 79271e08eea5..3fd3887d9ba8 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ACPI helpers for GPIO API * * Copyright (C) 2012, Intel Corporation * Authors: Mathias Nyman * Mika Westerberg - * - * 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 diff --git a/drivers/gpio/gpiolib-devprop.c b/drivers/gpio/gpiolib-devprop.c index f748aa3e77f7..43443b2c32a8 100644 --- a/drivers/gpio/gpiolib-devprop.c +++ b/drivers/gpio/gpiolib-devprop.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Device property helpers for GPIO chips. * * Copyright (C) 2016, Intel Corporation * Author: Mika Westerberg - * - * 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 diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 8b830996fe02..30e2476a6dc4 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 9985b748b14d..cb8ceddf05ac 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OF helpers for the GPIO API * * Copyright (c) 2007-2008 MontaVista Software, Inc. * * Author: Anton Vorontsov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 3d7c90fe7189..fbf6b1a0a4fa 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 03d15d84bdd8..1ffd3fb88466 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index f23a0c681494..7b5dcef1605b 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Internal GPIO functions. * * Copyright (C) 2013, Intel Corporation * Author: Mika Westerberg - * - * 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. */ #ifndef GPIOLIB_H -- cgit v1.2.3 From 32d064a16eaace1866610f36fb5f4b114d979246 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 25 Sep 2018 09:11:50 +0200 Subject: gpio: Rename devres implementation file All the other core files are named "gpiolib-" so let's rename the devres as well so we have some logical namespacing here. Signed-off-by: Linus Walleij --- drivers/gpio/Makefile | 2 +- drivers/gpio/devres.c | 439 ------------------------------------------ drivers/gpio/gpiolib-devres.c | 439 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 440 insertions(+), 440 deletions(-) delete mode 100644 drivers/gpio/devres.c create mode 100644 drivers/gpio/gpiolib-devres.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 80d58c2de730..f3a2efbf858b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -3,8 +3,8 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG -obj-$(CONFIG_GPIOLIB) += devres.o obj-$(CONFIG_GPIOLIB) += gpiolib.o +obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c deleted file mode 100644 index 01959369360b..000000000000 --- a/drivers/gpio/devres.c +++ /dev/null @@ -1,439 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * devres.c - managed gpio resources - * This file is based on kernel/irq/devres.c - * - * Copyright (c) 2011 John Crispin - */ - -#include -#include -#include -#include -#include -#include - -#include "gpiolib.h" - -static void devm_gpiod_release(struct device *dev, void *res) -{ - struct gpio_desc **desc = res; - - gpiod_put(*desc); -} - -static int devm_gpiod_match(struct device *dev, void *res, void *data) -{ - struct gpio_desc **this = res, **gpio = data; - - return *this == *gpio; -} - -static void devm_gpiod_release_array(struct device *dev, void *res) -{ - struct gpio_descs **descs = res; - - gpiod_put_array(*descs); -} - -static int devm_gpiod_match_array(struct device *dev, void *res, void *data) -{ - struct gpio_descs **this = res, **gpios = data; - - return *this == *gpios; -} - -/** - * devm_gpiod_get - Resource-managed gpiod_get() - * @dev: GPIO consumer - * @con_id: function within the GPIO consumer - * @flags: optional GPIO initialization flags - * - * Managed gpiod_get(). GPIO descriptors returned from this function are - * automatically disposed on driver detach. See gpiod_get() for detailed - * information about behavior and return values. - */ -struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, - const char *con_id, - enum gpiod_flags flags) -{ - return devm_gpiod_get_index(dev, con_id, 0, flags); -} -EXPORT_SYMBOL(devm_gpiod_get); - -/** - * devm_gpiod_get_optional - Resource-managed gpiod_get_optional() - * @dev: GPIO consumer - * @con_id: function within the GPIO consumer - * @flags: optional GPIO initialization flags - * - * Managed gpiod_get_optional(). GPIO descriptors returned from this function - * are automatically disposed on driver detach. See gpiod_get_optional() for - * detailed information about behavior and return values. - */ -struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, - const char *con_id, - enum gpiod_flags flags) -{ - return devm_gpiod_get_index_optional(dev, con_id, 0, flags); -} -EXPORT_SYMBOL(devm_gpiod_get_optional); - -/** - * devm_gpiod_get_index - Resource-managed gpiod_get_index() - * @dev: GPIO consumer - * @con_id: function within the GPIO consumer - * @idx: index of the GPIO to obtain in the consumer - * @flags: optional GPIO initialization flags - * - * Managed gpiod_get_index(). GPIO descriptors returned from this function are - * automatically disposed on driver detach. See gpiod_get_index() for detailed - * information about behavior and return values. - */ -struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, - const char *con_id, - unsigned int idx, - enum gpiod_flags flags) -{ - struct gpio_desc **dr; - struct gpio_desc *desc; - - dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), - GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); - - desc = gpiod_get_index(dev, con_id, idx, flags); - if (IS_ERR(desc)) { - devres_free(dr); - return desc; - } - - *dr = desc; - devres_add(dev, dr); - - return desc; -} -EXPORT_SYMBOL(devm_gpiod_get_index); - -/** - * devm_gpiod_get_from_of_node() - obtain a GPIO from an OF node - * @dev: device for lifecycle management - * @node: handle of the OF node - * @propname: name of the DT property representing the GPIO - * @index: index of the GPIO to obtain for the consumer - * @dflags: GPIO initialization flags - * @label: label to attach to the requested GPIO - * - * Returns: - * On successful request the GPIO pin is configured in accordance with - * provided @dflags. - * - * In case of error an ERR_PTR() is returned. - */ -struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, - struct device_node *node, - const char *propname, int index, - enum gpiod_flags dflags, - const char *label) -{ - struct gpio_desc **dr; - struct gpio_desc *desc; - - dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), - GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); - - desc = gpiod_get_from_of_node(node, propname, index, dflags, label); - if (IS_ERR(desc)) { - devres_free(dr); - return desc; - } - - *dr = desc; - devres_add(dev, dr); - - return desc; -} -EXPORT_SYMBOL(devm_gpiod_get_from_of_node); - -/** - * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a - * device's child node - * @dev: GPIO consumer - * @con_id: function within the GPIO consumer - * @index: index of the GPIO to obtain in the consumer - * @child: firmware node (child of @dev) - * @flags: GPIO initialization flags - * @label: label to attach to the requested GPIO - * - * GPIO descriptors returned from this function are automatically disposed on - * driver detach. - * - * On successful request the GPIO pin is configured in accordance with - * provided @flags. - */ -struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev, - const char *con_id, int index, - struct fwnode_handle *child, - enum gpiod_flags flags, - const char *label) -{ - char prop_name[32]; /* 32 is max size of property name */ - struct gpio_desc **dr; - struct gpio_desc *desc; - unsigned int i; - - dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), - GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id) - snprintf(prop_name, sizeof(prop_name), "%s-%s", - con_id, gpio_suffixes[i]); - else - snprintf(prop_name, sizeof(prop_name), "%s", - gpio_suffixes[i]); - - desc = fwnode_get_named_gpiod(child, prop_name, index, flags, - label); - if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) - break; - } - if (IS_ERR(desc)) { - devres_free(dr); - return desc; - } - - *dr = desc; - devres_add(dev, dr); - - return desc; -} -EXPORT_SYMBOL(devm_fwnode_get_index_gpiod_from_child); - -/** - * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() - * @dev: GPIO consumer - * @con_id: function within the GPIO consumer - * @index: index of the GPIO to obtain in the consumer - * @flags: optional GPIO initialization flags - * - * Managed gpiod_get_index_optional(). GPIO descriptors returned from this - * function are automatically disposed on driver detach. See - * gpiod_get_index_optional() for detailed information about behavior and - * return values. - */ -struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, - const char *con_id, - unsigned int index, - enum gpiod_flags flags) -{ - struct gpio_desc *desc; - - desc = devm_gpiod_get_index(dev, con_id, index, flags); - if (IS_ERR(desc)) { - if (PTR_ERR(desc) == -ENOENT) - return NULL; - } - - return desc; -} -EXPORT_SYMBOL(devm_gpiod_get_index_optional); - -/** - * devm_gpiod_get_array - Resource-managed gpiod_get_array() - * @dev: GPIO consumer - * @con_id: function within the GPIO consumer - * @flags: optional GPIO initialization flags - * - * Managed gpiod_get_array(). GPIO descriptors returned from this function are - * automatically disposed on driver detach. See gpiod_get_array() for detailed - * information about behavior and return values. - */ -struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev, - const char *con_id, - enum gpiod_flags flags) -{ - struct gpio_descs **dr; - struct gpio_descs *descs; - - dr = devres_alloc(devm_gpiod_release_array, - sizeof(struct gpio_descs *), GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); - - descs = gpiod_get_array(dev, con_id, flags); - if (IS_ERR(descs)) { - devres_free(dr); - return descs; - } - - *dr = descs; - devres_add(dev, dr); - - return descs; -} -EXPORT_SYMBOL(devm_gpiod_get_array); - -/** - * devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional() - * @dev: GPIO consumer - * @con_id: function within the GPIO consumer - * @flags: optional GPIO initialization flags - * - * Managed gpiod_get_array_optional(). GPIO descriptors returned from this - * function are automatically disposed on driver detach. - * See gpiod_get_array_optional() for detailed information about behavior and - * return values. - */ -struct gpio_descs *__must_check -devm_gpiod_get_array_optional(struct device *dev, const char *con_id, - enum gpiod_flags flags) -{ - struct gpio_descs *descs; - - descs = devm_gpiod_get_array(dev, con_id, flags); - if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT)) - return NULL; - - return descs; -} -EXPORT_SYMBOL(devm_gpiod_get_array_optional); - -/** - * devm_gpiod_put - Resource-managed gpiod_put() - * @dev: GPIO consumer - * @desc: GPIO descriptor to dispose of - * - * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or - * devm_gpiod_get_index(). Normally this function will not be called as the GPIO - * will be disposed of by the resource management code. - */ -void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) -{ - WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match, - &desc)); -} -EXPORT_SYMBOL(devm_gpiod_put); - -/** - * devm_gpiod_put_array - Resource-managed gpiod_put_array() - * @dev: GPIO consumer - * @descs: GPIO descriptor array to dispose of - * - * Dispose of an array of GPIO descriptors obtained with devm_gpiod_get_array(). - * Normally this function will not be called as the GPIOs will be disposed of - * by the resource management code. - */ -void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) -{ - WARN_ON(devres_release(dev, devm_gpiod_release_array, - devm_gpiod_match_array, &descs)); -} -EXPORT_SYMBOL(devm_gpiod_put_array); - - - - -static void devm_gpio_release(struct device *dev, void *res) -{ - unsigned *gpio = res; - - gpio_free(*gpio); -} - -static int devm_gpio_match(struct device *dev, void *res, void *data) -{ - unsigned *this = res, *gpio = data; - - return *this == *gpio; -} - -/** - * devm_gpio_request - request a GPIO for a managed device - * @dev: device to request the GPIO for - * @gpio: GPIO to allocate - * @label: the name of the requested GPIO - * - * Except for the extra @dev argument, this function takes the - * same arguments and performs the same function as - * gpio_request(). GPIOs requested with this function will be - * automatically freed on driver detach. - * - * If an GPIO allocated with this function needs to be freed - * separately, devm_gpio_free() must be used. - */ - -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) -{ - unsigned *dr; - int rc; - - dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - rc = gpio_request(gpio, label); - if (rc) { - devres_free(dr); - return rc; - } - - *dr = gpio; - devres_add(dev, dr); - - return 0; -} -EXPORT_SYMBOL(devm_gpio_request); - -/** - * devm_gpio_request_one - request a single GPIO with initial setup - * @dev: device to request for - * @gpio: the GPIO number - * @flags: GPIO configuration as specified by GPIOF_* - * @label: a literal description string of this GPIO - */ -int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label) -{ - unsigned *dr; - int rc; - - dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - rc = gpio_request_one(gpio, flags, label); - if (rc) { - devres_free(dr); - return rc; - } - - *dr = gpio; - devres_add(dev, dr); - - return 0; -} -EXPORT_SYMBOL(devm_gpio_request_one); - -/** - * devm_gpio_free - free a GPIO - * @dev: device to free GPIO for - * @gpio: GPIO to free - * - * Except for the extra @dev argument, this function takes the - * same arguments and performs the same function as gpio_free(). - * This function instead of gpio_free() should be used to manually - * free GPIOs allocated with devm_gpio_request(). - */ -void devm_gpio_free(struct device *dev, unsigned int gpio) -{ - - WARN_ON(devres_release(dev, devm_gpio_release, devm_gpio_match, - &gpio)); -} -EXPORT_SYMBOL(devm_gpio_free); diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c new file mode 100644 index 000000000000..01959369360b --- /dev/null +++ b/drivers/gpio/gpiolib-devres.c @@ -0,0 +1,439 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * devres.c - managed gpio resources + * This file is based on kernel/irq/devres.c + * + * Copyright (c) 2011 John Crispin + */ + +#include +#include +#include +#include +#include +#include + +#include "gpiolib.h" + +static void devm_gpiod_release(struct device *dev, void *res) +{ + struct gpio_desc **desc = res; + + gpiod_put(*desc); +} + +static int devm_gpiod_match(struct device *dev, void *res, void *data) +{ + struct gpio_desc **this = res, **gpio = data; + + return *this == *gpio; +} + +static void devm_gpiod_release_array(struct device *dev, void *res) +{ + struct gpio_descs **descs = res; + + gpiod_put_array(*descs); +} + +static int devm_gpiod_match_array(struct device *dev, void *res, void *data) +{ + struct gpio_descs **this = res, **gpios = data; + + return *this == *gpios; +} + +/** + * devm_gpiod_get - Resource-managed gpiod_get() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get() for detailed + * information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + return devm_gpiod_get_index(dev, con_id, 0, flags); +} +EXPORT_SYMBOL(devm_gpiod_get); + +/** + * devm_gpiod_get_optional - Resource-managed gpiod_get_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_optional(). GPIO descriptors returned from this function + * are automatically disposed on driver detach. See gpiod_get_optional() for + * detailed information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + return devm_gpiod_get_index_optional(dev, con_id, 0, flags); +} +EXPORT_SYMBOL(devm_gpiod_get_optional); + +/** + * devm_gpiod_get_index - Resource-managed gpiod_get_index() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_index(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get_index() for detailed + * information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags) +{ + struct gpio_desc **dr; + struct gpio_desc *desc; + + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + desc = gpiod_get_index(dev, con_id, idx, flags); + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return desc; +} +EXPORT_SYMBOL(devm_gpiod_get_index); + +/** + * devm_gpiod_get_from_of_node() - obtain a GPIO from an OF node + * @dev: device for lifecycle management + * @node: handle of the OF node + * @propname: name of the DT property representing the GPIO + * @index: index of the GPIO to obtain for the consumer + * @dflags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * Returns: + * On successful request the GPIO pin is configured in accordance with + * provided @dflags. + * + * In case of error an ERR_PTR() is returned. + */ +struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, + struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + struct gpio_desc **dr; + struct gpio_desc *desc; + + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + desc = gpiod_get_from_of_node(node, propname, index, dflags, label); + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return desc; +} +EXPORT_SYMBOL(devm_gpiod_get_from_of_node); + +/** + * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a + * device's child node + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * @child: firmware node (child of @dev) + * @flags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * GPIO descriptors returned from this function are automatically disposed on + * driver detach. + * + * On successful request the GPIO pin is configured in accordance with + * provided @flags. + */ +struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev, + const char *con_id, int index, + struct fwnode_handle *child, + enum gpiod_flags flags, + const char *label) +{ + char prop_name[32]; /* 32 is max size of property name */ + struct gpio_desc **dr; + struct gpio_desc *desc; + unsigned int i; + + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-%s", + con_id, gpio_suffixes[i]); + else + snprintf(prop_name, sizeof(prop_name), "%s", + gpio_suffixes[i]); + + desc = fwnode_get_named_gpiod(child, prop_name, index, flags, + label); + if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) + break; + } + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return desc; +} +EXPORT_SYMBOL(devm_fwnode_get_index_gpiod_from_child); + +/** + * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_index_optional(). GPIO descriptors returned from this + * function are automatically disposed on driver detach. See + * gpiod_get_index_optional() for detailed information about behavior and + * return values. + */ +struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, + const char *con_id, + unsigned int index, + enum gpiod_flags flags) +{ + struct gpio_desc *desc; + + desc = devm_gpiod_get_index(dev, con_id, index, flags); + if (IS_ERR(desc)) { + if (PTR_ERR(desc) == -ENOENT) + return NULL; + } + + return desc; +} +EXPORT_SYMBOL(devm_gpiod_get_index_optional); + +/** + * devm_gpiod_get_array - Resource-managed gpiod_get_array() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_array(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get_array() for detailed + * information about behavior and return values. + */ +struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs **dr; + struct gpio_descs *descs; + + dr = devres_alloc(devm_gpiod_release_array, + sizeof(struct gpio_descs *), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + descs = gpiod_get_array(dev, con_id, flags); + if (IS_ERR(descs)) { + devres_free(dr); + return descs; + } + + *dr = descs; + devres_add(dev, dr); + + return descs; +} +EXPORT_SYMBOL(devm_gpiod_get_array); + +/** + * devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_array_optional(). GPIO descriptors returned from this + * function are automatically disposed on driver detach. + * See gpiod_get_array_optional() for detailed information about behavior and + * return values. + */ +struct gpio_descs *__must_check +devm_gpiod_get_array_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs *descs; + + descs = devm_gpiod_get_array(dev, con_id, flags); + if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT)) + return NULL; + + return descs; +} +EXPORT_SYMBOL(devm_gpiod_get_array_optional); + +/** + * devm_gpiod_put - Resource-managed gpiod_put() + * @dev: GPIO consumer + * @desc: GPIO descriptor to dispose of + * + * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or + * devm_gpiod_get_index(). Normally this function will not be called as the GPIO + * will be disposed of by the resource management code. + */ +void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) +{ + WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match, + &desc)); +} +EXPORT_SYMBOL(devm_gpiod_put); + +/** + * devm_gpiod_put_array - Resource-managed gpiod_put_array() + * @dev: GPIO consumer + * @descs: GPIO descriptor array to dispose of + * + * Dispose of an array of GPIO descriptors obtained with devm_gpiod_get_array(). + * Normally this function will not be called as the GPIOs will be disposed of + * by the resource management code. + */ +void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) +{ + WARN_ON(devres_release(dev, devm_gpiod_release_array, + devm_gpiod_match_array, &descs)); +} +EXPORT_SYMBOL(devm_gpiod_put_array); + + + + +static void devm_gpio_release(struct device *dev, void *res) +{ + unsigned *gpio = res; + + gpio_free(*gpio); +} + +static int devm_gpio_match(struct device *dev, void *res, void *data) +{ + unsigned *this = res, *gpio = data; + + return *this == *gpio; +} + +/** + * devm_gpio_request - request a GPIO for a managed device + * @dev: device to request the GPIO for + * @gpio: GPIO to allocate + * @label: the name of the requested GPIO + * + * Except for the extra @dev argument, this function takes the + * same arguments and performs the same function as + * gpio_request(). GPIOs requested with this function will be + * automatically freed on driver detach. + * + * If an GPIO allocated with this function needs to be freed + * separately, devm_gpio_free() must be used. + */ + +int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) +{ + unsigned *dr; + int rc; + + dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + rc = gpio_request(gpio, label); + if (rc) { + devres_free(dr); + return rc; + } + + *dr = gpio; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL(devm_gpio_request); + +/** + * devm_gpio_request_one - request a single GPIO with initial setup + * @dev: device to request for + * @gpio: the GPIO number + * @flags: GPIO configuration as specified by GPIOF_* + * @label: a literal description string of this GPIO + */ +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label) +{ + unsigned *dr; + int rc; + + dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + rc = gpio_request_one(gpio, flags, label); + if (rc) { + devres_free(dr); + return rc; + } + + *dr = gpio; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL(devm_gpio_request_one); + +/** + * devm_gpio_free - free a GPIO + * @dev: device to free GPIO for + * @gpio: GPIO to free + * + * Except for the extra @dev argument, this function takes the + * same arguments and performs the same function as gpio_free(). + * This function instead of gpio_free() should be used to manually + * free GPIOs allocated with devm_gpio_request(). + */ +void devm_gpio_free(struct device *dev, unsigned int gpio) +{ + + WARN_ON(devres_release(dev, devm_gpio_release, devm_gpio_match, + &gpio)); +} +EXPORT_SYMBOL(devm_gpio_free); -- cgit v1.2.3 From ae9847f48a4b4bff0335da20be63ac84d94eb54c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 21 Sep 2018 12:36:03 +0200 Subject: gpiolib: Fix gpio_direction_* for single direction GPIOs GPIOs with no programmable direction are not required to implement direction_output nor direction_input. If we try to set an output direction on an output-only GPIO or input direction on an input-only GPIO simply return 0. This allows this single direction GPIO to be used by libgpiod. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1ffd3fb88466..cabe1b460458 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2543,19 +2543,27 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); int gpiod_direction_input(struct gpio_desc *desc) { struct gpio_chip *chip; - int status = -EINVAL; + int status = 0; VALIDATE_DESC(desc); chip = desc->gdev->chip; - if (!chip->get || !chip->direction_input) { + if (!chip->get && chip->direction_input) { gpiod_warn(desc, - "%s: missing get() or direction_input() operations\n", + "%s: missing get() and direction_input() operations\n", __func__); return -EIO; } - status = chip->direction_input(chip, gpio_chip_hwgpio(desc)); + if (chip->direction_input) { + status = chip->direction_input(chip, gpio_chip_hwgpio(desc)); + } else if (chip->get_direction && + (chip->get_direction(chip, gpio_chip_hwgpio(desc)) != 1)) { + gpiod_warn(desc, + "%s: missing direction_input() operation\n", + __func__); + return -EIO; + } if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); @@ -2577,16 +2585,28 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { struct gpio_chip *gc = desc->gdev->chip; int val = !!value; - int ret; + int ret = 0; - if (!gc->set || !gc->direction_output) { + if (!gc->set && !gc->direction_output) { gpiod_warn(desc, - "%s: missing set() or direction_output() operations\n", + "%s: missing set() and direction_output() operations\n", __func__); return -EIO; } - ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val); + if (gc->direction_output) { + ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val); + } else { + if (gc->get_direction && + gc->get_direction(gc, gpio_chip_hwgpio(desc))) { + gpiod_warn(desc, + "%s: missing direction_output() operation\n", + __func__); + return -EIO; + } + gc->set(gc, gpio_chip_hwgpio(desc), val); + } + if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); trace_gpio_value(desc_to_gpio(desc), 0, val); -- cgit v1.2.3 From e48d194d1204b19655c1a9d78a67f2f01d2fe432 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 25 Sep 2018 09:54:14 +0200 Subject: gpio: Add comments on single direction chips A patch from Ricardo got me thinking about some gpio chip semantics so let's drop in some comments to make things more clear around that. Cc: Ricardo Ribalda Delgado Cc: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 33 +++++++++++++++++++++++++++------ include/linux/gpio/driver.h | 6 +++++- 2 files changed, 32 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index cabe1b460458..4ce402f9fc69 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2548,20 +2548,31 @@ int gpiod_direction_input(struct gpio_desc *desc) VALIDATE_DESC(desc); chip = desc->gdev->chip; + /* + * It is legal to have no .get() and .direction_input() specified if + * the chip is output-only, but you can't specify .direction_input() + * and not support the .get() operation, that doesn't make sense. + */ if (!chip->get && chip->direction_input) { gpiod_warn(desc, - "%s: missing get() and direction_input() operations\n", - __func__); + "%s: missing get() but have direction_input()\n", + __func__); return -EIO; } + /* + * If we have a .direction_input() callback, things are simple, + * just call it. Else we are some input-only chip so try to check the + * direction (if .get_direction() is supported) else we silently + * assume we are in input mode after this. + */ if (chip->direction_input) { status = chip->direction_input(chip, gpio_chip_hwgpio(desc)); } else if (chip->get_direction && (chip->get_direction(chip, gpio_chip_hwgpio(desc)) != 1)) { gpiod_warn(desc, - "%s: missing direction_input() operation\n", - __func__); + "%s: missing direction_input() operation and line is output\n", + __func__); return -EIO; } if (status == 0) @@ -2587,16 +2598,22 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) int val = !!value; int ret = 0; + /* + * It's OK not to specify .direction_output() if the gpiochip is + * output-only, but if there is then not even a .set() operation it + * is pretty tricky to drive the output line. + */ if (!gc->set && !gc->direction_output) { gpiod_warn(desc, - "%s: missing set() and direction_output() operations\n", - __func__); + "%s: missing set() and direction_output() operations\n", + __func__); return -EIO; } if (gc->direction_output) { ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val); } else { + /* Check that we are in output mode if we can */ if (gc->get_direction && gc->get_direction(gc, gpio_chip_hwgpio(desc))) { gpiod_warn(desc, @@ -2604,6 +2621,10 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) __func__); return -EIO; } + /* + * If we can't actively set the direction, we are some + * output-only chip, so just drive the output as desired. + */ gc->set(gc, gpio_chip_hwgpio(desc), val); } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index d8dcd0e44cab..f6b95734073f 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -178,9 +178,13 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * @free: optional hook for chip-specific deactivation, such as * disabling module power and clock; may sleep * @get_direction: returns direction for signal "offset", 0=out, 1=in, - * (same as GPIOF_DIR_XXX), or negative error + * (same as GPIOF_DIR_XXX), or negative error. + * It is recommended to always implement this function, even on + * input-only or output-only gpio chips. * @direction_input: configures signal "offset" as input, or returns error + * This can be omitted on input-only or output-only gpio chips. * @direction_output: configures signal "offset" as output, or returns error + * This can be omitted on input-only or output-only gpio chips. * @get: returns value for signal "offset", 0=low, 1=high, or negative error * @get_multiple: reads values for multiple signals defined by "mask" and * stores them in "bits", returns 0 on success or negative error -- cgit v1.2.3 From be8c8facc707d48f367d9313f5972353d24f19be Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 27 Sep 2018 09:48:08 +0200 Subject: gpio: new driver to work with a 8x12 siox This driver controls a SIOX device that provides 20 I/O lines. The first twelve are fixed inputs, the remaining eight are outputs. Acked-by: Gavin Schenk Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-siox.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 drivers/gpio/gpio-siox.c (limited to 'drivers/gpio') diff --git a/MAINTAINERS b/MAINTAINERS index 0937f2131f73..920c0f92bef4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13289,6 +13289,7 @@ M: Uwe Kleine-König R: Pengutronix Kernel Team S: Supported F: drivers/siox/* +F: drivers/gpio/gpio-siox.c F: include/trace/events/siox.h SIS 190 ETHERNET DRIVER diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b8cbf5081389..88c25677eeff 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -429,6 +429,14 @@ config GPIO_REG A 32-bit single register GPIO fixed in/out implementation. This can be used to represent any register as a set of GPIO signals. +config GPIO_SIOX + tristate "SIOX GPIO support" + depends on SIOX + select GPIOLIB_IRQCHIP + help + Say yes here to support SIOX I/O devices. These are units connected + via a SIOX bus and have a number of fixed-direction I/O lines. + config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" depends on PLAT_SPEAR diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index f3a2efbf858b..e85e770045fb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -124,6 +124,7 @@ obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o +obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c new file mode 100644 index 000000000000..571b2a81c6de --- /dev/null +++ b/drivers/gpio/gpio-siox.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2018 Pengutronix, Uwe Kleine-König + */ + +#include +#include +#include +#include + +struct gpio_siox_ddata { + struct gpio_chip gchip; + struct irq_chip ichip; + struct mutex lock; + u8 setdata[1]; + u8 getdata[3]; + + spinlock_t irqlock; + u32 irq_enable; + u32 irq_status; + u32 irq_type[20]; +}; + +/* + * Note that this callback only sets the value that is clocked out in the next + * cycle. + */ +static int gpio_siox_set_data(struct siox_device *sdevice, u8 status, u8 buf[]) +{ + struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev); + + mutex_lock(&ddata->lock); + buf[0] = ddata->setdata[0]; + mutex_unlock(&ddata->lock); + + return 0; +} + +static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[]) +{ + struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev); + size_t offset; + u32 trigger; + + mutex_lock(&ddata->lock); + + spin_lock_irq(&ddata->irqlock); + + for (offset = 0; offset < 12; ++offset) { + unsigned int bitpos = 11 - offset; + unsigned int gpiolevel = buf[bitpos / 8] & (1 << bitpos % 8); + unsigned int prev_level = + ddata->getdata[bitpos / 8] & (1 << (bitpos % 8)); + u32 irq_type = ddata->irq_type[offset]; + + if (gpiolevel) { + if ((irq_type & IRQ_TYPE_LEVEL_HIGH) || + ((irq_type & IRQ_TYPE_EDGE_RISING) && !prev_level)) + ddata->irq_status |= 1 << offset; + } else { + if ((irq_type & IRQ_TYPE_LEVEL_LOW) || + ((irq_type & IRQ_TYPE_EDGE_FALLING) && prev_level)) + ddata->irq_status |= 1 << offset; + } + } + + trigger = ddata->irq_status & ddata->irq_enable; + + spin_unlock_irq(&ddata->irqlock); + + ddata->getdata[0] = buf[0]; + ddata->getdata[1] = buf[1]; + ddata->getdata[2] = buf[2]; + + mutex_unlock(&ddata->lock); + + for (offset = 0; offset < 12; ++offset) { + if (trigger & (1 << offset)) { + struct irq_domain *irqdomain = ddata->gchip.irq.domain; + unsigned int irq = irq_find_mapping(irqdomain, offset); + + /* + * Conceptually handle_nested_irq should call the flow + * handler of the irq chip. But it doesn't, so we have + * to clean the irq_status here. + */ + spin_lock_irq(&ddata->irqlock); + ddata->irq_status &= ~(1 << offset); + spin_unlock_irq(&ddata->irqlock); + + handle_nested_irq(irq); + } + } + + return 0; +} + +static void gpio_siox_irq_ack(struct irq_data *d) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct gpio_siox_ddata *ddata = + container_of(ic, struct gpio_siox_ddata, ichip); + + spin_lock_irq(&ddata->irqlock); + ddata->irq_status &= ~(1 << d->hwirq); + spin_unlock_irq(&ddata->irqlock); +} + +static void gpio_siox_irq_mask(struct irq_data *d) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct gpio_siox_ddata *ddata = + container_of(ic, struct gpio_siox_ddata, ichip); + + spin_lock_irq(&ddata->irqlock); + ddata->irq_enable &= ~(1 << d->hwirq); + spin_unlock_irq(&ddata->irqlock); +} + +static void gpio_siox_irq_unmask(struct irq_data *d) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct gpio_siox_ddata *ddata = + container_of(ic, struct gpio_siox_ddata, ichip); + + spin_lock_irq(&ddata->irqlock); + ddata->irq_enable |= 1 << d->hwirq; + spin_unlock_irq(&ddata->irqlock); +} + +static int gpio_siox_irq_set_type(struct irq_data *d, u32 type) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct gpio_siox_ddata *ddata = + container_of(ic, struct gpio_siox_ddata, ichip); + + spin_lock_irq(&ddata->irqlock); + ddata->irq_type[d->hwirq] = type; + spin_unlock_irq(&ddata->irqlock); + + return 0; +} + +static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset) +{ + struct gpio_siox_ddata *ddata = + container_of(chip, struct gpio_siox_ddata, gchip); + int ret; + + mutex_lock(&ddata->lock); + + if (offset >= 12) { + unsigned int bitpos = 19 - offset; + + ret = ddata->setdata[0] & (1 << bitpos); + } else { + unsigned int bitpos = 11 - offset; + + ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8)); + } + + mutex_unlock(&ddata->lock); + + return ret; +} + +static void gpio_siox_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct gpio_siox_ddata *ddata = + container_of(chip, struct gpio_siox_ddata, gchip); + u8 mask = 1 << (19 - offset); + + mutex_lock(&ddata->lock); + + if (value) + ddata->setdata[0] |= mask; + else + ddata->setdata[0] &= ~mask; + + mutex_unlock(&ddata->lock); +} + +static int gpio_siox_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + if (offset >= 12) + return -EINVAL; + + return 0; +} + +static int gpio_siox_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + if (offset < 12) + return -EINVAL; + + gpio_siox_set(chip, offset, value); + return 0; +} + +static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + if (offset < 12) + return 1; /* input */ + else + return 0; /* output */ +} + +static int gpio_siox_probe(struct siox_device *sdevice) +{ + struct gpio_siox_ddata *ddata; + int ret; + + ddata = devm_kzalloc(&sdevice->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + dev_set_drvdata(&sdevice->dev, ddata); + + mutex_init(&ddata->lock); + spin_lock_init(&ddata->irqlock); + + ddata->gchip.base = -1; + ddata->gchip.can_sleep = 1; + ddata->gchip.parent = &sdevice->dev; + ddata->gchip.owner = THIS_MODULE; + ddata->gchip.get = gpio_siox_get; + ddata->gchip.set = gpio_siox_set; + ddata->gchip.direction_input = gpio_siox_direction_input; + ddata->gchip.direction_output = gpio_siox_direction_output; + ddata->gchip.get_direction = gpio_siox_get_direction; + ddata->gchip.ngpio = 20; + + ddata->ichip.name = "siox-gpio"; + ddata->ichip.irq_ack = gpio_siox_irq_ack; + ddata->ichip.irq_mask = gpio_siox_irq_mask; + ddata->ichip.irq_unmask = gpio_siox_irq_unmask; + ddata->ichip.irq_set_type = gpio_siox_irq_set_type; + + ret = gpiochip_add(&ddata->gchip); + if (ret) { + dev_err(&sdevice->dev, + "Failed to register gpio chip (%d)\n", ret); + goto err_gpiochip; + } + + ret = gpiochip_irqchip_add(&ddata->gchip, &ddata->ichip, + 0, handle_level_irq, IRQ_TYPE_EDGE_RISING); + if (ret) { + dev_err(&sdevice->dev, + "Failed to register irq chip (%d)\n", ret); +err_gpiochip: + gpiochip_remove(&ddata->gchip); + } + + return ret; +} + +static int gpio_siox_remove(struct siox_device *sdevice) +{ + struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev); + + gpiochip_remove(&ddata->gchip); + return 0; +} + +static struct siox_driver gpio_siox_driver = { + .probe = gpio_siox_probe, + .remove = gpio_siox_remove, + .set_data = gpio_siox_set_data, + .get_data = gpio_siox_get_data, + .driver = { + .name = "gpio-siox", + }, +}; + +static int __init gpio_siox_init(void) +{ + return siox_driver_register(&gpio_siox_driver); +} +module_init(gpio_siox_init); + +static void __exit gpio_siox_exit(void) +{ + siox_driver_unregister(&gpio_siox_driver); +} +module_exit(gpio_siox_exit); + +MODULE_AUTHOR("Uwe Kleine-Koenig "); +MODULE_DESCRIPTION("SIOX gpio driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 799d5eb4e7aede8ddfcdd313eeb875f4eea860be Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sat, 29 Sep 2018 14:20:22 +0200 Subject: gpiolib: Fix incorrect use of find_next_zero_bit() Commit b17566a6b08b ("gpiolib: Implement fast processing path in get/set array"), already fixed to some extent with commit 5d581d7e8cdc ("gpiolib: Fix missing updates of bitmap index"), introduced a new mode of processing bitmaps where bits applicable for fast bitmap processing path are supposed to be skipped while iterating bits which don't apply. Unfortunately, find_next_zero_bit() function supposed to skip over those fast bits is always called with a 'start' argument equal to an index of last zero bit found and returns that index value again an again, causing an infinite loop. Fix it by incrementing the index uncoditionally before find_next_zero_bit() is optionally called. Reported-by: Marek Szyprowski Signed-off-by: Janusz Krzysztofik Tested-by: Marek Szyprowski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4ce402f9fc69..a293f14ba75c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2920,12 +2920,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, int hwgpio = gpio_chip_hwgpio(desc); __set_bit(hwgpio, mask); + i++; if (array_info) i = find_next_zero_bit(array_info->get_mask, array_size, i); - else - i++; } while ((i < array_size) && (desc_array[i]->gdev->chip == chip)); @@ -2945,12 +2944,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, value = !value; __assign_bit(j, value_bitmap, value); trace_gpio_value(desc_to_gpio(desc), 1, value); + j++; if (array_info) j = find_next_zero_bit(array_info->get_mask, i, j); - else - j++; } if (mask != fastpath) @@ -3233,12 +3231,11 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, __clear_bit(hwgpio, bits); count++; } + i++; if (array_info) i = find_next_zero_bit(array_info->set_mask, array_size, i); - else - i++; } while ((i < array_size) && (desc_array[i]->gdev->chip == chip)); /* push collected bits to outputs */ -- cgit v1.2.3 From cf9af0d5786c008971148f4e06567a98f6a7f9d0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 27 Sep 2018 13:38:09 +0200 Subject: gpio: Propagate errors from gpiod_set_array_value_complex() Internal helper function gpiod_set_array_value_complex() was changed to return an error value, but not all gpiolib callers were updated to propagate the new error up. Fixes: 3027743f83f867d8 ("gpio: Remove VLA from gpiolib") Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/consumer.rst | 16 ++++++++-------- drivers/gpio/gpiolib.c | 30 ++++++++++++++++-------------- include/linux/gpio/consumer.h | 28 +++++++++++++++------------- 3 files changed, 39 insertions(+), 35 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index ba4973829fae..5e4d8aa68913 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -340,18 +340,18 @@ The following functions get or set the values of an array of GPIOs:: struct gpio_array *array_info, unsigned long *value_bitmap); - void gpiod_set_array_value(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) + int gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, unsigned long *value_bitmap) - void gpiod_set_array_value_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) + int gpiod_set_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a293f14ba75c..51dd9d601cac 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3346,15 +3346,16 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); * This function should be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. */ -void gpiod_set_array_value(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) +int gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { if (!desc_array) - return; - gpiod_set_array_value_complex(false, false, array_size, desc_array, - array_info, value_bitmap); + return -EINVAL; + return gpiod_set_array_value_complex(false, false, array_size, + desc_array, array_info, + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_array_value); @@ -3763,16 +3764,17 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) * * This function is to be called from contexts that can sleep. */ -void gpiod_set_array_value_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) +int gpiod_set_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) - return; - gpiod_set_array_value_complex(false, true, array_size, desc_array, - array_info, value_bitmap); + return -EINVAL; + return gpiod_set_array_value_complex(false, true, array_size, + desc_array, array_info, + value_bitmap); } EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index d7fbe30ece84..136aeca37740 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -117,10 +117,10 @@ int gpiod_get_array_value(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap); void gpiod_set_value(struct gpio_desc *desc, int value); -void gpiod_set_array_value(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap); +int gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap); int gpiod_get_raw_value(const struct gpio_desc *desc); int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, @@ -139,10 +139,10 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_array *array_info, unsigned long *value_bitmap); void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); -void gpiod_set_array_value_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap); +int gpiod_set_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap); int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc); int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, @@ -361,13 +361,14 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value) /* GPIO can never have been requested */ WARN_ON(1); } -static inline void gpiod_set_array_value(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) +static inline int gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); + return 0; } static inline int gpiod_get_raw_value(const struct gpio_desc *desc) { @@ -419,13 +420,14 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) /* GPIO can never have been requested */ WARN_ON(1); } -static inline void gpiod_set_array_value_cansleep(unsigned int array_size, +static inline int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); + return 0; } static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { -- cgit v1.2.3 From 3c940660cb1ea6c952bb863040cdacafd5077448 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 27 Sep 2018 13:38:10 +0200 Subject: gpio: Restore indentation of continued lines Fixes: 3027743f83f867d8 ("gpio: Remove VLA from gpiolib") Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 20 ++++++++++---------- drivers/gpio/gpiolib.h | 8 ++++---- include/linux/gpio/consumer.h | 18 +++++++++--------- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 51dd9d601cac..83c6ec7f7634 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3145,10 +3145,10 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, } int gpiod_set_array_value_complex(bool raw, bool can_sleep, - unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) + unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { int i = 0; @@ -3322,9 +3322,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); * complain if the GPIO chip functions potentially sleep. */ int gpiod_set_raw_array_value(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { if (!desc_array) return -EINVAL; @@ -3723,9 +3723,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); * This function is to be called from contexts that can sleep. */ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { might_sleep_if(extra_checks); if (!desc_array) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 7b5dcef1605b..087d865286a0 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -196,10 +196,10 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, struct gpio_array *array_info, unsigned long *value_bitmap); int gpiod_set_array_value_complex(bool raw, bool can_sleep, - unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap); + unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap); /* This is just passed between gpiolib and devres */ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 136aeca37740..0f350616d372 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -128,9 +128,9 @@ int gpiod_get_raw_array_value(unsigned int array_size, unsigned long *value_bitmap); void gpiod_set_raw_value(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap); + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap); /* Value get/set from sleeping context */ int gpiod_get_value_cansleep(const struct gpio_desc *desc); @@ -150,9 +150,9 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, unsigned long *value_bitmap); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value_cansleep(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap); + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap); int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); @@ -391,9 +391,9 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) WARN_ON(1); } static inline int gpiod_set_raw_array_value(unsigned int array_size, - struct gpio_desc **desc_array, - struct gpio_array *array_info, - unsigned long *value_bitmap) + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { /* GPIO can never have been requested */ WARN_ON(1); -- cgit v1.2.3 From 8898240aaace9eb315b3e9c43bc132da6e62a953 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 28 Sep 2018 02:18:09 +0000 Subject: gpio: htc-egpio: Remove set but not used variable 'bit' Fixes gcc '-Wunused-but-set-variable' warning: drivers/gpio/gpio-htc-egpio.c: In function 'egpio_set': drivers/gpio/gpio-htc-egpio.c:192:20: warning: variable 'bit' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Signed-off-by: Linus Walleij --- drivers/gpio/gpio-htc-egpio.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 38be44dbd6e1..9d3ac51a765c 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -189,7 +189,6 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) unsigned long flag; struct egpio_chip *egpio; struct egpio_info *ei; - unsigned bit; int pos; int reg; int shift; @@ -199,7 +198,6 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) egpio = gpiochip_get_data(chip); ei = dev_get_drvdata(egpio->dev); - bit = egpio_bit(ei, offset); pos = egpio_pos(ei, offset); reg = egpio->reg_start + pos; shift = pos << ei->reg_shift; -- cgit v1.2.3 From 467480738d0b33335032652b29776d82200db41a Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 28 Sep 2018 16:39:50 -0500 Subject: gpio: omap: get rid of the conditional PM runtime calls OMAP GPIO driver is checking !BANK_USED() used condition before calling PM runtime API, because of PM runtime calls in omap2_gpio_prepare/resume_for_idle(). It's not required any more since "omap gpio add level idle, cpu_pm and drop runtime_irq_safe" series [1] from Tony Lindgren was accepted and PM runtime management was enabled in IRQ chip core by commit be45beb2df69 ("genirq: Add runtime power management support for IRQ chips") . As result safely drop !BANK_USED() checks from omap_gpio_request/free(), omap_gpio_irq_bus_lock/unlock() and enable PM runtime management for OMAP GPIO IRQ chip. [1] https://www.spinics.net/lists/arm-kernel/msg677583.html Tested-by: Tony Lindgren Signed-off-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index c0d7ae766de4..cab14f2297aa 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -704,12 +704,7 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) struct gpio_bank *bank = gpiochip_get_data(chip); unsigned long flags; - /* - * If this is the first gpio_request for the bank, - * enable the bank module. - */ - if (!BANK_USED(bank)) - pm_runtime_get_sync(chip->parent); + pm_runtime_get_sync(chip->parent); raw_spin_lock_irqsave(&bank->lock, flags); omap_enable_gpio_module(bank, offset); @@ -733,12 +728,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) omap_disable_gpio_module(bank, offset); raw_spin_unlock_irqrestore(&bank->lock, flags); - /* - * If this is the last gpio to be freed in the bank, - * disable the bank module. - */ - if (!BANK_USED(bank)) - pm_runtime_put(chip->parent); + pm_runtime_put(chip->parent); } /* @@ -864,20 +854,14 @@ static void omap_gpio_irq_bus_lock(struct irq_data *data) { struct gpio_bank *bank = omap_irq_data_get_bank(data); - if (!BANK_USED(bank)) - pm_runtime_get_sync(bank->chip.parent); + pm_runtime_get_sync(bank->chip.parent); } static void gpio_irq_bus_sync_unlock(struct irq_data *data) { struct gpio_bank *bank = omap_irq_data_get_bank(data); - /* - * If this is the last IRQ to be freed in the bank, - * disable the bank module. - */ - if (!BANK_USED(bank)) - pm_runtime_put(bank->chip.parent); + pm_runtime_put(bank->chip.parent); } static void omap_gpio_ack_irq(struct irq_data *d) @@ -1387,6 +1371,7 @@ static int omap_gpio_probe(struct platform_device *pdev) irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock, irqc->name = dev_name(&pdev->dev); irqc->flags = IRQCHIP_MASK_ON_SUSPEND; + irqc->parent_device = dev; bank->irq = platform_get_irq(pdev, 0); if (bank->irq <= 0) { -- cgit v1.2.3 From 8dc196974429b28f1a2f2563d30d02b7561a46aa Mon Sep 17 00:00:00 2001 From: Christophe Blaess Date: Fri, 28 Sep 2018 15:38:43 +0200 Subject: Accept partial 'gpio-line-names' property. Documentation/devicetree/bindings/gpio/gpio.txt says: "The names are assigned starting from line offset 0 from left to right from the passed array. An incomplete array (where the number of passed named are less than ngpios) will still be used up until the last provided valid line index". This patch makes it actually work this way. Signed-off-by: Christophe Blaess Signed-off-by: Patrick Boettcher Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-devprop.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-devprop.c b/drivers/gpio/gpiolib-devprop.c index 43443b2c32a8..dd517098ab95 100644 --- a/drivers/gpio/gpiolib-devprop.c +++ b/drivers/gpio/gpiolib-devprop.c @@ -29,32 +29,29 @@ void devprop_gpiochip_set_names(struct gpio_chip *chip, struct gpio_device *gdev = chip->gpiodev; const char **names; int ret, i; + int count; - ret = fwnode_property_read_string_array(fwnode, "gpio-line-names", - NULL, 0); - if (ret < 0) + count = fwnode_property_read_string_array(fwnode, "gpio-line-names", + NULL, 0); + if (count < 0) return; - if (ret != gdev->ngpio) { - dev_warn(&gdev->dev, - "names %d do not match number of GPIOs %d\n", ret, - gdev->ngpio); - return; - } + if (count > gdev->ngpio) + count = gdev->ngpio; - names = kcalloc(gdev->ngpio, sizeof(*names), GFP_KERNEL); + names = kcalloc(count, sizeof(*names), GFP_KERNEL); if (!names) return; ret = fwnode_property_read_string_array(fwnode, "gpio-line-names", - names, gdev->ngpio); + names, count); if (ret < 0) { dev_warn(&gdev->dev, "failed to read GPIO line names\n"); kfree(names); return; } - for (i = 0; i < gdev->ngpio; i++) + for (i = 0; i < count; i++) gdev->descs[i].name = names[i]; kfree(names); -- cgit v1.2.3 From c4791bc6e3a6b95bed0ee72cf9784ecbb87074ee Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 27 Sep 2018 18:38:44 -0500 Subject: gpio: omap: drop omap_gpio_list omap_gpio_list is unused so drop it. Signed-off-by: Grygorii Strashko Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index cab14f2297aa..c081f399cef5 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -34,8 +34,6 @@ #define OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER BIT(2) #define OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN BIT(1) -static LIST_HEAD(omap_gpio_list); - struct gpio_regs { u32 irqenable1; u32 irqenable2; @@ -1478,8 +1476,6 @@ static int omap_gpio_probe(struct platform_device *pdev) pm_runtime_put(dev); - list_add_tail(&bank->node, &omap_gpio_list); - return 0; } -- cgit v1.2.3 From 850d434ea37bd2e15154501279abc5c18bfb72d0 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 28 Sep 2018 02:17:48 +0000 Subject: gpio: omap: Remove set but not used variable 'dev' Fixes gcc '-Wunused-but-set-variable' warning: drivers/gpio/gpio-omap.c: In function 'gpio_omap_cpu_notifier': drivers/gpio/gpio-omap.c:1327:17: warning: variable 'dev' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index c081f399cef5..9887c3db6e16 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1306,11 +1306,9 @@ static int gpio_omap_cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) { struct gpio_bank *bank; - struct device *dev; unsigned long flags; bank = container_of(nb, struct gpio_bank, nb); - dev = bank->chip.parent; raw_spin_lock_irqsave(&bank->lock, flags); switch (cmd) { -- cgit v1.2.3 From 90fd227029a25b452fd39dec6d557212d03d79b1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 1 Oct 2018 23:06:17 +0200 Subject: gpio: Slightly more helpful debugfs This at least makes debugfs print if the line is active high or low. That is pretty helpful as what we display as "lo" or "hi" is the raw physical level of the line. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 83c6ec7f7634..02660bf11189 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4584,8 +4584,9 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) struct gpio_chip *chip = gdev->chip; unsigned gpio = gdev->base; struct gpio_desc *gdesc = &gdev->descs[0]; - int is_out; - int is_irq; + bool is_out; + bool is_irq; + bool active_low; for (i = 0; i < gdev->ngpio; i++, gpio++, gdesc++) { if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) { @@ -4599,11 +4600,13 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) gpiod_get_direction(gdesc); is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); is_irq = test_bit(FLAG_USED_AS_IRQ, &gdesc->flags); - seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s", + active_low = test_bit(FLAG_ACTIVE_LOW, &gdesc->flags); + seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s", gpio, gdesc->name ? gdesc->name : "", gdesc->label, is_out ? "out" : "in ", chip->get ? (chip->get(chip, i) ? "hi" : "lo") : "? ", - is_irq ? "IRQ" : " "); + is_irq ? "IRQ " : "", + active_low ? "ACTIVE LOW" : ""); seq_printf(s, "\n"); } } -- cgit v1.2.3 From 3ea47b447da0b8e93735612f2b0d128dba4d7524 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 25 Sep 2018 13:29:46 +0200 Subject: gpio: mockup: use device properties instead of platform_data Some users want to introduce device tree support to the mockup driver. Let's make it easier by switching to using generic device properties. The driver stays compatible with previous use cases and after this conversion there'll be no need to change the way probing of mockup GPIO chips works. Tested with libgpiod test suite. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 82 ++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 31 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index d66b7a768ecd..8269cffc2967 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "gpiolib.h" @@ -28,6 +29,8 @@ * of GPIO lines. */ #define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2) +/* Maximum of three properties + the sentinel. */ +#define GPIO_MOCKUP_MAX_PROP 4 #define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__) @@ -59,13 +62,6 @@ struct gpio_mockup_dbgfs_private { int offset; }; -struct gpio_mockup_platform_data { - int base; - int ngpio; - int index; - bool named_lines; -}; - static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; static int gpio_mockup_num_ranges; module_param_array(gpio_mockup_ranges, int, &gpio_mockup_num_ranges, 0400); @@ -255,26 +251,37 @@ static int gpio_mockup_name_lines(struct device *dev, static int gpio_mockup_probe(struct platform_device *pdev) { - struct gpio_mockup_platform_data *pdata; struct gpio_mockup_chip *chip; struct gpio_chip *gc; - int rv, base, ngpio; struct device *dev; - char *name; + const char *name; + int rv, base; + u16 ngpio; dev = &pdev->dev; - pdata = dev_get_platdata(dev); - base = pdata->base; - ngpio = pdata->ngpio; + + rv = device_property_read_u32(dev, "gpio-base", &base); + if (rv) + base = -1; + + rv = device_property_read_u16(dev, "nr-gpios", &ngpio); + if (rv) + return rv; + + rv = device_property_read_string(dev, "chip-name", &name); + if (rv) + name = NULL; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; - name = devm_kasprintf(dev, GFP_KERNEL, "%s-%c", - pdev->name, pdata->index); - if (!name) - return -ENOMEM; + if (!name) { + name = devm_kasprintf(dev, GFP_KERNEL, + "%s-%c", pdev->name, pdev->id + 'A'); + if (!name) + return -ENOMEM; + } gc = &chip->gc; gc->base = base; @@ -295,7 +302,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) if (!chip->lines) return -ENOMEM; - if (pdata->named_lines) { + if (device_property_read_bool(dev, "named-gpio-lines")) { rv = gpio_mockup_name_lines(dev, chip); if (rv) return rv; @@ -339,9 +346,11 @@ static void gpio_mockup_unregister_pdevs(void) static int __init gpio_mockup_init(void) { - int i, num_chips, err = 0, index = 'A'; - struct gpio_mockup_platform_data pdata; + struct property_entry properties[GPIO_MOCKUP_MAX_PROP]; + int i, prop, num_chips, err = 0, base; + struct platform_device_info pdevinfo; struct platform_device *pdev; + u16 ngpio; if ((gpio_mockup_num_ranges < 2) || (gpio_mockup_num_ranges % 2) || @@ -371,17 +380,28 @@ static int __init gpio_mockup_init(void) } for (i = 0; i < num_chips; i++) { - pdata.index = index++; - pdata.base = gpio_mockup_range_base(i); - pdata.ngpio = pdata.base < 0 - ? gpio_mockup_range_ngpio(i) - : gpio_mockup_range_ngpio(i) - pdata.base; - pdata.named_lines = gpio_mockup_named_lines; - - pdev = platform_device_register_resndata(NULL, - GPIO_MOCKUP_NAME, - i, NULL, 0, &pdata, - sizeof(pdata)); + memset(properties, 0, sizeof(properties)); + memset(&pdevinfo, 0, sizeof(pdevinfo)); + prop = 0; + + base = gpio_mockup_range_base(i); + if (base >= 0) + properties[prop++] = PROPERTY_ENTRY_U32("gpio-base", + base); + + ngpio = base < 0 ? gpio_mockup_range_ngpio(i) + : gpio_mockup_range_ngpio(i) - base; + properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio); + + if (gpio_mockup_named_lines) + properties[prop++] = PROPERTY_ENTRY_BOOL( + "named-gpio-lines"); + + pdevinfo.name = GPIO_MOCKUP_NAME; + pdevinfo.id = i; + pdevinfo.properties = properties; + + pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) { gpio_mockup_err("error registering device"); platform_driver_unregister(&gpio_mockup_driver); -- cgit v1.2.3 From 2505c7b0128f63289a59d77ee39cacd08aacf779 Mon Sep 17 00:00:00 2001 From: Eugeniy Paltsev Date: Fri, 28 Sep 2018 16:15:30 +0300 Subject: GPIO: add single-register GPIO via CREG driver Add single-register MMIO GPIO driver for complex cases where only several fields in register belong to GPIO lines and each GPIO line owns a field with different length and on/off value. Such CREG GPIOs are used in Synopsys AXS10x and HSDK boards. Signed-off-by: Eugeniy Paltsev Signed-off-by: Linus Walleij --- MAINTAINERS | 6 ++ drivers/gpio/Kconfig | 10 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-creg-snps.c | 191 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 drivers/gpio/gpio-creg-snps.c (limited to 'drivers/gpio') diff --git a/MAINTAINERS b/MAINTAINERS index 920c0f92bef4..d2f4b8059adf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14018,6 +14018,12 @@ S: Supported F: drivers/reset/reset-axs10x.c F: Documentation/devicetree/bindings/reset/snps,axs10x-reset.txt +SYNOPSYS CREG GPIO DRIVER +M: Eugeniy Paltsev +S: Maintained +F: drivers/gpio/gpio-creg-snps.c +F: Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt + SYNOPSYS DESIGNWARE 8250 UART DRIVER R: Andy Shevchenko S: Maintained diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 88c25677eeff..2bb9ff243f3f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -437,6 +437,16 @@ config GPIO_SIOX Say yes here to support SIOX I/O devices. These are units connected via a SIOX bus and have a number of fixed-direction I/O lines. +config GPIO_SNPS_CREG + bool "Synopsys GPIO via CREG (Control REGisters) driver" + depends on ARC || COMPILE_TEST + select OF_GPIO + help + This driver supports GPIOs via CREG on various Synopsys SoCs. + This is a single-register MMIO GPIO driver for complex cases + where only several fields in register belong to GPIO lines and + each GPIO line owns a field with different length and on/off value. + config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" depends on PLAT_SPEAR diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e85e770045fb..671c4477c951 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_GPIO_REG) += gpio-reg.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o +obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c new file mode 100644 index 000000000000..8cbc94d0d424 --- /dev/null +++ b/drivers/gpio/gpio-creg-snps.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Synopsys CREG (Control REGisters) GPIO driver +// +// Copyright (C) 2018 Synopsys +// Author: Eugeniy Paltsev + +#include +#include +#include +#include + +#define MAX_GPIO 32 + +struct creg_layout { + u8 ngpio; + u8 shift[MAX_GPIO]; + u8 on[MAX_GPIO]; + u8 off[MAX_GPIO]; + u8 bit_per_gpio[MAX_GPIO]; +}; + +struct creg_gpio { + struct gpio_chip gc; + void __iomem *regs; + spinlock_t lock; + const struct creg_layout *layout; +}; + +static void creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct creg_gpio *hcg = gpiochip_get_data(gc); + const struct creg_layout *layout = hcg->layout; + u32 reg, reg_shift, value; + unsigned long flags; + int i; + + value = val ? hcg->layout->on[offset] : hcg->layout->off[offset]; + + reg_shift = layout->shift[offset]; + for (i = 0; i < offset; i++) + reg_shift += layout->bit_per_gpio[i] + layout->shift[i]; + + spin_lock_irqsave(&hcg->lock, flags); + reg = readl(hcg->regs); + reg &= ~(GENMASK(layout->bit_per_gpio[i] - 1, 0) << reg_shift); + reg |= (value << reg_shift); + writel(reg, hcg->regs); + spin_unlock_irqrestore(&hcg->lock, flags); +} + +static int creg_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) +{ + creg_gpio_set(gc, offset, val); + + return 0; +} + +static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg, + int i) +{ + const struct creg_layout *layout = hcg->layout; + + if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8) + return -EINVAL; + + /* Check that on valiue fits it's placeholder */ + if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i]) + return -EINVAL; + + /* Check that off valiue fits it's placeholder */ + if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i]) + return -EINVAL; + + if (layout->on[i] == layout->off[i]) + return -EINVAL; + + return 0; +} + +static int creg_gpio_validate(struct device *dev, struct creg_gpio *hcg, + u32 ngpios) +{ + u32 reg_len = 0; + int i; + + if (hcg->layout->ngpio < 1 || hcg->layout->ngpio > MAX_GPIO) + return -EINVAL; + + if (ngpios < 1 || ngpios > hcg->layout->ngpio) { + dev_err(dev, "ngpios must be in [1:%u]\n", hcg->layout->ngpio); + return -EINVAL; + } + + for (i = 0; i < hcg->layout->ngpio; i++) { + if (creg_gpio_validate_pg(dev, hcg, i)) + return -EINVAL; + + reg_len += hcg->layout->shift[i] + hcg->layout->bit_per_gpio[i]; + } + + /* Check that we fit in 32 bit register */ + if (reg_len > 32) + return -EINVAL; + + return 0; +} + +static const struct creg_layout hsdk_cs_ctl = { + .ngpio = 10, + .shift = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + .off = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, + .on = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + .bit_per_gpio = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 } +}; + +static const struct creg_layout axs10x_flsh_cs_ctl = { + .ngpio = 1, + .shift = { 0 }, + .off = { 1 }, + .on = { 3 }, + .bit_per_gpio = { 2 } +}; + +static const struct of_device_id creg_gpio_ids[] = { + { + .compatible = "snps,creg-gpio-axs10x", + .data = &axs10x_flsh_cs_ctl + }, { + .compatible = "snps,creg-gpio-hsdk", + .data = &hsdk_cs_ctl + }, { /* sentinel */ } +}; + +static int creg_gpio_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device *dev = &pdev->dev; + struct creg_gpio *hcg; + struct resource *mem; + u32 ngpios; + int ret; + + hcg = devm_kzalloc(dev, sizeof(struct creg_gpio), GFP_KERNEL); + if (!hcg) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcg->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(hcg->regs)) + return PTR_ERR(hcg->regs); + + match = of_match_node(creg_gpio_ids, pdev->dev.of_node); + hcg->layout = match->data; + if (!hcg->layout) + return -EINVAL; + + ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios); + if (ret) + return ret; + + ret = creg_gpio_validate(dev, hcg, ngpios); + if (ret) + return ret; + + spin_lock_init(&hcg->lock); + + hcg->gc.label = dev_name(dev); + hcg->gc.base = -1; + hcg->gc.ngpio = ngpios; + hcg->gc.set = creg_gpio_set; + hcg->gc.direction_output = creg_gpio_dir_out; + hcg->gc.of_node = dev->of_node; + + ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg); + if (ret) + return ret; + + dev_info(dev, "GPIO controller with %d gpios probed\n", ngpios); + + return 0; +} + +static struct platform_driver creg_gpio_snps_driver = { + .driver = { + .name = "snps-creg-gpio", + .of_match_table = creg_gpio_ids, + }, + .probe = creg_gpio_probe, +}; +builtin_platform_driver(creg_gpio_snps_driver); -- cgit v1.2.3 From f8ec92a9f63b3b11e399409141b7868bb405e6b5 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 5 Oct 2018 08:52:58 +0200 Subject: gpiolib: Add init_valid_mask exported function Add a function that allows initializing the valid_mask from gpiochip_add_data. This prevents race conditions during gpiochip initialization. If the function is not exported, then the old behaviour is respected, this is, set all gpios as valid. Signed-off-by: Ricardo Ribalda Delgado Tested-by: Jeffrey Hugo Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 16 ++++++++++++++-- include/linux/gpio/driver.h | 7 ++++++- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 02660bf11189..b66aae75223b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -360,7 +360,7 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip) return p; } -static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip) +static int gpiochip_alloc_valid_mask(struct gpio_chip *gpiochip) { #ifdef CONFIG_OF_GPIO int size; @@ -381,6 +381,14 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip) return 0; } +static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip) +{ + if (gpiochip->init_valid_mask) + return gpiochip->init_valid_mask(gpiochip); + + return 0; +} + static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip) { kfree(gpiochip->valid_mask); @@ -1369,7 +1377,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, if (status) goto err_remove_from_list; - status = gpiochip_init_valid_mask(chip); + status = gpiochip_alloc_valid_mask(chip); if (status) goto err_remove_irqchip_mask; @@ -1381,6 +1389,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, if (status) goto err_remove_chip; + status = gpiochip_init_valid_mask(chip); + if (status) + goto err_remove_chip; + acpi_gpiochip_add(chip); machine_gpiochip_add(chip); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index f6b95734073f..c5a51af8f76e 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -280,6 +280,9 @@ struct gpio_chip { void (*dbg_show)(struct seq_file *s, struct gpio_chip *chip); + + int (*init_valid_mask)(struct gpio_chip *chip); + int base; u16 ngpio; const char *const *names; @@ -318,7 +321,9 @@ struct gpio_chip { /** * @need_valid_mask: * - * If set core allocates @valid_mask with all bits set to one. + * If set core allocates @valid_mask with all its values initialized + * with init_valid_mask() or set to one if init_valid_mask() is not + * defined */ bool need_valid_mask; -- cgit v1.2.3 From 3edfb7bd76bd1cba6b917736943dffd799deed8a Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 5 Oct 2018 08:53:00 +0200 Subject: gpiolib: Show correct direction from the beginning Current code assumes that the direction is input if direction_input function is set. This might not be the case on GPIOs with programmable direction. Signed-off-by: Ricardo Ribalda Delgado Tested-by: Jeffrey Hugo Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b66aae75223b..16ab167717dc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1351,20 +1351,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, spin_unlock_irqrestore(&gpio_lock, flags); - for (i = 0; i < chip->ngpio; i++) { - struct gpio_desc *desc = &gdev->descs[i]; - - desc->gdev = gdev; - - /* REVISIT: most hardware initializes GPIOs as inputs (often - * with pullups enabled) so power usage is minimized. Linux - * code should set the gpio direction first thing; but until - * it does, and in case chip->get_direction is not set, we may - * expose the wrong direction in sysfs. - */ - desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; - } - #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -1393,6 +1379,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, if (status) goto err_remove_chip; + for (i = 0; i < chip->ngpio; i++) { + struct gpio_desc *desc = &gdev->descs[i]; + + desc->gdev = gdev; + + if (chip->get_direction && gpiochip_line_is_valid(chip, i)) + desc->flags = !chip->get_direction(chip, i) ? + (1 << FLAG_IS_OUT) : 0; + else + desc->flags = !chip->direction_input ? + (1 << FLAG_IS_OUT) : 0; + } + acpi_gpiochip_add(chip); machine_gpiochip_add(chip); -- cgit v1.2.3 From 70728c29465bc4bfa7a8c14304771eab77e923c7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 4 Oct 2018 00:52:52 +0200 Subject: gpio: syscon: Fix possible NULL ptr usage The priv->data->set can be NULL while flags contains GPIO_SYSCON_FEAT_OUT and chip->set is valid pointer. This happens in case the controller uses the default GPIO setter. Always use chip->set to access the setter to avoid possible NULL pointer dereferencing. Signed-off-by: Marek Vasut Signed-off-by: Linus Walleij --- drivers/gpio/gpio-syscon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 87c18a544513..7f3da34c7874 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -122,7 +122,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val) BIT(offs % SYSCON_REG_BITS)); } - priv->data->set(chip, offset, val); + chip->set(chip, offset, val); return 0; } -- cgit v1.2.3 From 3925b90fa4622b708fc609c98a3f346ee6641e8c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Oct 2018 11:34:03 +0200 Subject: gpio: fix doc string for devm_gpiochip_add_data() to not talk about irq_chip The function is about adding a gpio_chip so dev has to belong to this one. Fix wording to be more grammatically correct (but attention, I'm not a native speaker). Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 16ab167717dc..76ade1a7cf43 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1525,7 +1525,7 @@ static int devm_gpio_chip_match(struct device *dev, void *res, void *data) /** * devm_gpiochip_add_data() - Resource manager gpiochip_add_data() - * @dev: the device pointer on which irq_chip belongs to. + * @dev: pointer to the device that gpio_chip belongs to. * @chip: the chip to register, with chip->base initialized * @data: driver-private data associated with this chip * -- cgit v1.2.3 From 767cd17a5cc5ce2d5342f5c4bd6c5d36b812c7a7 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 12 Oct 2018 08:11:36 +0200 Subject: gpiolib: Initialize gdev field before is used gpio_hog depends on gdev field being initialized. This patch fixes an OOPs during initialization of TI's AM335x-ICEv2. Fixes: 3edfb7bd76bd1cba ("gpiolib: Show correct direction from the beginning") Tested-by: Vignesh R Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 76ade1a7cf43..1a283fbac84c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1351,6 +1351,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, spin_unlock_irqrestore(&gpio_lock, flags); + for (i = 0; i < chip->ngpio; i++) + gdev->descs[i].gdev = gdev; + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -1382,8 +1385,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, for (i = 0; i < chip->ngpio; i++) { struct gpio_desc *desc = &gdev->descs[i]; - desc->gdev = gdev; - if (chip->get_direction && gpiochip_line_is_valid(chip, i)) desc->flags = !chip->get_direction(chip, i) ? (1 << FLAG_IS_OUT) : 0; -- cgit v1.2.3 From a7c0b4b80f02d748031c785e30d380cfa7fd41ee Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 12 Oct 2018 20:46:29 -0700 Subject: gpio: fix SNPS_CREG kconfig dependency warning Fix kconfig warning for GPIO_SNPS_CREG: WARNING: unmet direct dependencies detected for OF_GPIO Depends on [n]: GPIOLIB [=y] && OF [=n] && HAS_IOMEM [=y] Selected by [y]: - GPIO_SNPS_CREG [=y] && GPIOLIB [=y] && HAS_IOMEM [=y] && (ARC || COMPILE_TEST [=y]) Drivers in drivers/gpio/Kconfig depend on OF_GPIO, not select it. This prevents attempting to build when OF is not enabled. Signed-off-by: Randy Dunlap Cc: Linus Walleij Cc: linux-gpio@vger.kernel.org Cc: Eugeniy Paltsev Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 2bb9ff243f3f..833a1b51c948 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -440,7 +440,7 @@ config GPIO_SIOX config GPIO_SNPS_CREG bool "Synopsys GPIO via CREG (Control REGisters) driver" depends on ARC || COMPILE_TEST - select OF_GPIO + depends on OF_GPIO help This driver supports GPIOs via CREG on various Synopsys SoCs. This is a single-register MMIO GPIO driver for complex cases -- cgit v1.2.3 From 9cb7cf9c4ebb19981cfa156cc60e2a75a51ccf1b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 8 Oct 2018 09:32:14 -0700 Subject: gpio: Drop parent irq assignment during cascade setup We want to set the irq parent for interrupts that we're setting up to be cascaded from another interrupt controller, but we may or may not have already mapped the gpiochip irqs into the kernel's virtual irq number space at this point. If we have mapped irqs before calling here, then we've gone through gpiochip_irq_map() and called irq_set_parent() already. If we haven't mapped irqs, then the gpiochip is dynamically mapping irqs and we can rely on gpiochip_irq_map() or the gpio driver's irqdomain ops to setup the irq parent properly. Either way, setting the parent here when cascading the gpiochip doesn't make much sense because it should be done at irq mapping time. In the dynamic mapping case, this code is mapping virq 0 to some parent virq in a loop. While that's benign, let's drop this code to simplify. Cc: Evan Green Cc: Thierry Reding Cc: Grygorii Strashko Signed-off-by: Stephen Boyd Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1a283fbac84c..350b0541fdb0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1675,8 +1675,6 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, unsigned int parent_irq, irq_flow_handler_t parent_handler) { - unsigned int offset; - if (!gpiochip->irq.domain) { chip_err(gpiochip, "called %s before setting up irqchip\n", __func__); @@ -1699,14 +1697,6 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, gpiochip->irq.parents = &parent_irq; gpiochip->irq.num_parents = 1; } - - /* Set the parent IRQ for all affected IRQs */ - for (offset = 0; offset < gpiochip->ngpio; offset++) { - if (!gpiochip_irqchip_irq_valid(gpiochip, offset)) - continue; - irq_set_parent(irq_find_mapping(gpiochip->irq.domain, offset), - parent_irq); - } } /** -- cgit v1.2.3 From 3c1f6b2d116c74f96691989258cad90dfe454d8d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 8 Oct 2018 09:32:15 -0700 Subject: gpio: Remove unused 'irqchip' argument to gpiochip_set_cascaded_irqchip() This argument hasn't ever been used since it was introduced in commit 1425052097b5 ("gpio: add IRQ chip helpers in gpiolib"). Let's drop it to reduce reading confusion. Cc: Evan Green Cc: Thierry Reding Cc: Grygorii Strashko Signed-off-by: Stephen Boyd Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 350b0541fdb0..6f6b4c26d099 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1663,7 +1663,6 @@ EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); /** * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip * @gpiochip: the gpiochip to set the irqchip chain to - * @irqchip: the irqchip to chain to the gpiochip * @parent_irq: the irq number corresponding to the parent IRQ for this * chained irqchip * @parent_handler: the parent interrupt handler for the accumulated IRQ @@ -1671,7 +1670,6 @@ EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); * cascaded, pass NULL in this handler argument */ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, - struct irq_chip *irqchip, unsigned int parent_irq, irq_flow_handler_t parent_handler) { @@ -1719,8 +1717,7 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, return; } - gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq, - parent_handler); + gpiochip_set_cascaded_irqchip(gpiochip, parent_irq, parent_handler); } EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip); @@ -1735,8 +1732,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq) { - gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq, - NULL); + gpiochip_set_cascaded_irqchip(gpiochip, parent_irq, NULL); } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); -- cgit v1.2.3 From 40f5ff4f9f23a849ad135cb736d4d448d810ac17 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 8 Oct 2018 09:32:16 -0700 Subject: gpio: Clarify kerneldoc on gpiochip_set_chained_irqchip() This doesn't support nested anymore, so drivers shouldn't call it with the handler set to NULL. Cc: Evan Green Cc: Thierry Reding Cc: Grygorii Strashko Signed-off-by: Stephen Boyd Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6f6b4c26d099..17dc23055755 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1704,8 +1704,7 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, * @parent_irq: the irq number corresponding to the parent IRQ for this * chained irqchip * @parent_handler: the parent interrupt handler for the accumulated IRQ - * coming out of the gpiochip. If the interrupt is nested rather than - * cascaded, pass NULL in this handler argument + * coming out of the gpiochip. */ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, -- cgit v1.2.3