diff options
Diffstat (limited to 'drivers/gpio')
27 files changed, 610 insertions, 171 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 833a1b51c948..a1876c39a7b7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -288,6 +288,7 @@ config GPIO_LPC18XX tristate "NXP LPC18XX/43XX GPIO support" default y if ARCH_LPC18XX depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST) + select IRQ_DOMAIN_HIERARCHY help Select this option to enable GPIO driver for NXP LPC18XX/43XX devices. diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO new file mode 100644 index 000000000000..19d27c904916 --- /dev/null +++ b/drivers/gpio/TODO @@ -0,0 +1,109 @@ +This is a place for planning the ongoing long-term work in the GPIO +subsystem. + + +GPIO descriptors + +Starting with commit 79a9becda894 the GPIO subsystem embarked on a journey +to move away from the global GPIO numberspace and toward a decriptor-based +approach. This means that GPIO consumers, drivers and machine descriptions +ideally have no use or idea of the global GPIO numberspace that has/was +used in the inception of the GPIO subsystem. + +Work items: + +- Convert all GPIO device drivers to only #include <linux/gpio/driver.h> + +- Convert all consumer drivers to only #include <linux/gpio/consumer.h> + +- Convert all machine descriptors in "boardfiles" to only + #include <linux/gpio/machine.h>, the other option being to convert it + to a machine description such as device tree, ACPI or fwnode that + implicitly does not use global GPIO numbers. + +- When this work is complete (will require some of the items in the + following ongoing work as well) we can delete the old global + numberspace accessors from <linux/gpio.h> and eventually delete + <linux/gpio.h> altogether. + + +Get rid of <linux/of_gpio.h> + +This header and helpers appeared at one point when there was no proper +driver infrastructure for doing simpler MMIO GPIO devices and there was +no core support for parsing device tree GPIOs from the core library with +the [devm_]gpiod_get() calls we have today that will implicitly go into +the device tree back-end. + +Work items: + +- Get rid of struct of_mm_gpio_chip altogether: use the generic MMIO + GPIO for all current users (see below). Delete struct of_mm_gpio_chip, + to_of_mm_gpio_chip(), of_mm_gpiochip_add_data(), of_mm_gpiochip_add() + of_mm_gpiochip_remove() from the kernel. + +- Change all consumer drivers that #include <linux/of_gpio.h> to + #include <linux/gpio/consumer.h> and stop doing custom parsing of the + GPIO lines from the device tree. This can be tricky and often ivolves + changing boardfiles, etc. + +- Pull semantics for legacy device tree (OF) GPIO lookups into + gpiolib-of.c: in some cases subsystems are doing custom flags and + lookups for polarity inversion, open drain and what not. As we now + handle this with generic OF bindings, pull all legacy handling into + gpiolib so the library API becomes narrow and deep and handle all + legacy bindings internally. (See e.g. commits 6953c57ab172, + 6a537d48461d etc) + +- Delete <linux/of_gpio.h> when all the above is complete and everything + uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead. + + +Collect drivers + +Collect GPIO drivers from arch/* and other places that should be placed +in drivers/gpio/gpio-*. Augment platforms to create platform devices or +similar and probe a proper driver in the gpiolib subsystem. + +In some cases it makes sense to create a GPIO chip from the local driver +for a few GPIOs. Those should stay where they are. + + +Generic MMIO GPIO + +The GPIO drivers can utilize the generic MMIO helper library in many +cases, and the helper library should be as helpful as possible for MMIO +drivers. (drivers/gpio/gpio-mmio.c) + +Work items: + +- Look over and identify any remaining easily converted drivers and + dry-code conversions to MMIO GPIO for maintainers to test + +- Expand the MMIO GPIO or write a new library for port-mapped I/O + helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use + this with dry-coding and sending to maintainers to test + + +GPIOLIB irqchip + +The GPIOLIB irqchip is a helper irqchip for "simple cases" that should +try to cover any generic kind of irqchip cascaded from a GPIO. + +- Look over and identify any remaining easily converted drivers and + dry-code conversions to gpiolib irqchip for maintainers to test + +- Support generic hierarchical GPIO interrupts: these are for the + non-cascading case where there is one IRQ per GPIO line, there is + currently no common infrastructure for this. + + +Increase integration with pin control + +There are already ways to use pin control as back-end for GPIO and +it may make sense to bring these subsystems closer. One reason for +creating pin control as its own subsystem was that we could avoid any +use of the global GPIO numbers. Once the above is complete, it may +make sense to simply join the subsystems into one and make pin +multiplexing, pin configuration, GPIO, etc selectable options in one +and the same pin control and GPIO subsystem. diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index 9c4e07fcb74b..92c8f944bf64 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -222,7 +222,7 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, port_state = inb(dio48egpio->base + ports[i]); /* store acquired bits at respective bits array offset */ - bits[word_index] |= port_state << word_offset; + bits[word_index] |= (port_state << word_offset) & word_mask; } return 0; diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 2c9738adb3a6..88dc6f2449f6 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -128,7 +128,7 @@ static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, port_state = inb(idi48gpio->base + ports[i]); /* store acquired bits at respective bits array offset */ - bits[word_index] |= port_state << word_offset; + bits[word_index] |= (port_state << word_offset) & word_mask; } return 0; diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 044888fd96a1..84ae04402f70 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -748,8 +748,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int dwapb_gpio_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct dwapb_gpio *gpio = platform_get_drvdata(pdev); + struct dwapb_gpio *gpio = dev_get_drvdata(dev); struct gpio_chip *gc = &gpio->ports[0].gc; unsigned long flags; int i; @@ -793,8 +792,7 @@ static int dwapb_gpio_suspend(struct device *dev) static int dwapb_gpio_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct dwapb_gpio *gpio = platform_get_drvdata(pdev); + struct dwapb_gpio *gpio = dev_get_drvdata(dev); struct gpio_chip *gc = &gpio->ports[0].gc; unsigned long flags; int i; diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index b56ff2efbf36..8c150fd68d9d 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -211,7 +211,7 @@ static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, port_state = inb(gpiommgpio->base + ports[i]); /* store acquired bits at respective bits array offset */ - bits[word_index] |= port_state << word_offset; + bits[word_index] |= (port_state << word_offset) & word_mask; } return 0; diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 60a1556c570a..45b8d6a02b87 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -30,7 +30,6 @@ #include <linux/gpio/driver.h> #include <linux/slab.h> #include <linux/err.h> -#include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c index 55d562e1278e..d6d6140ffc40 100644 --- a/drivers/gpio/gpio-ks8695.c +++ b/drivers/gpio/gpio-ks8695.c @@ -282,22 +282,13 @@ static int ks8695_gpio_show(struct seq_file *s, void *unused) return 0; } -static int ks8695_gpio_open(struct inode *inode, struct file *file) -{ - return single_open(file, ks8695_gpio_show, NULL); -} - -static const struct file_operations ks8695_gpio_operations = { - .open = ks8695_gpio_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ks8695_gpio); static int __init ks8695_gpio_debugfs_init(void) { /* /sys/kernel/debug/ks8695_gpio */ - (void) debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, &ks8695_gpio_operations); + debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, + &ks8695_gpio_fops); return 0; } postcore_initcall(ks8695_gpio_debugfs_init); diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index f12e02e1016d..d441dbaed7a3 100644 --- a/drivers/gpio/gpio-lpc18xx.c +++ b/drivers/gpio/gpio-lpc18xx.c @@ -1,20 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 /* * GPIO driver for NXP LPC18xx/43xx. * + * Copyright (C) 2018 Vladimir Zapolskiy <vz@mleia.com> * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> * - * 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 <linux/clk.h> #include <linux/gpio/driver.h> #include <linux/io.h> +#include <linux/irqdomain.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_gpio.h> +#include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> @@ -24,13 +25,246 @@ #define LPC18XX_MAX_PORTS 8 #define LPC18XX_PINS_PER_PORT 32 +/* LPC18xx GPIO pin interrupt controller register offsets */ +#define LPC18XX_GPIO_PIN_IC_ISEL 0x00 +#define LPC18XX_GPIO_PIN_IC_IENR 0x04 +#define LPC18XX_GPIO_PIN_IC_SIENR 0x08 +#define LPC18XX_GPIO_PIN_IC_CIENR 0x0c +#define LPC18XX_GPIO_PIN_IC_IENF 0x10 +#define LPC18XX_GPIO_PIN_IC_SIENF 0x14 +#define LPC18XX_GPIO_PIN_IC_CIENF 0x18 +#define LPC18XX_GPIO_PIN_IC_RISE 0x1c +#define LPC18XX_GPIO_PIN_IC_FALL 0x20 +#define LPC18XX_GPIO_PIN_IC_IST 0x24 + +#define NR_LPC18XX_GPIO_PIN_IC_IRQS 8 + +struct lpc18xx_gpio_pin_ic { + void __iomem *base; + struct irq_domain *domain; + struct raw_spinlock lock; +}; + struct lpc18xx_gpio_chip { struct gpio_chip gpio; void __iomem *base; struct clk *clk; + struct lpc18xx_gpio_pin_ic *pin_ic; spinlock_t lock; }; +static inline void lpc18xx_gpio_pin_ic_isel(struct lpc18xx_gpio_pin_ic *ic, + u32 pin, bool set) +{ + u32 val = readl_relaxed(ic->base + LPC18XX_GPIO_PIN_IC_ISEL); + + if (set) + val &= ~BIT(pin); + else + val |= BIT(pin); + + writel_relaxed(val, ic->base + LPC18XX_GPIO_PIN_IC_ISEL); +} + +static inline void lpc18xx_gpio_pin_ic_set(struct lpc18xx_gpio_pin_ic *ic, + u32 pin, u32 reg) +{ + writel_relaxed(BIT(pin), ic->base + reg); +} + +static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d) +{ + struct lpc18xx_gpio_pin_ic *ic = d->chip_data; + u32 type = irqd_get_trigger_type(d); + + raw_spin_lock(&ic->lock); + + if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING) + lpc18xx_gpio_pin_ic_set(ic, d->hwirq, + LPC18XX_GPIO_PIN_IC_CIENR); + + if (type & IRQ_TYPE_EDGE_FALLING) + lpc18xx_gpio_pin_ic_set(ic, d->hwirq, + LPC18XX_GPIO_PIN_IC_CIENF); + + raw_spin_unlock(&ic->lock); + + irq_chip_mask_parent(d); +} + +static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d) +{ + struct lpc18xx_gpio_pin_ic *ic = d->chip_data; + u32 type = irqd_get_trigger_type(d); + + raw_spin_lock(&ic->lock); + + if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING) + lpc18xx_gpio_pin_ic_set(ic, d->hwirq, + LPC18XX_GPIO_PIN_IC_SIENR); + + if (type & IRQ_TYPE_EDGE_FALLING) + lpc18xx_gpio_pin_ic_set(ic, d->hwirq, + LPC18XX_GPIO_PIN_IC_SIENF); + + raw_spin_unlock(&ic->lock); + + irq_chip_unmask_parent(d); +} + +static void lpc18xx_gpio_pin_ic_eoi(struct irq_data *d) +{ + struct lpc18xx_gpio_pin_ic *ic = d->chip_data; + u32 type = irqd_get_trigger_type(d); + + raw_spin_lock(&ic->lock); + + if (type & IRQ_TYPE_EDGE_BOTH) + lpc18xx_gpio_pin_ic_set(ic, d->hwirq, + LPC18XX_GPIO_PIN_IC_IST); + + raw_spin_unlock(&ic->lock); + + irq_chip_eoi_parent(d); +} + +static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type) +{ + struct lpc18xx_gpio_pin_ic *ic = d->chip_data; + + raw_spin_lock(&ic->lock); + + if (type & IRQ_TYPE_LEVEL_HIGH) { + lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true); + lpc18xx_gpio_pin_ic_set(ic, d->hwirq, + LPC18XX_GPIO_PIN_IC_SIENF); + } else if (type & IRQ_TYPE_LEVEL_LOW) { + lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true); + lpc18xx_gpio_pin_ic_set(ic, d->hwirq, + LPC18XX_GPIO_PIN_IC_CIENF); + } else { + lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, false); + } + + raw_spin_unlock(&ic->lock); + + return 0; +} + +static struct irq_chip lpc18xx_gpio_pin_ic = { + .name = "LPC18xx GPIO pin", + .irq_mask = lpc18xx_gpio_pin_ic_mask, + .irq_unmask = lpc18xx_gpio_pin_ic_unmask, + .irq_eoi = lpc18xx_gpio_pin_ic_eoi, + .irq_set_type = lpc18xx_gpio_pin_ic_set_type, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct irq_fwspec parent_fwspec, *fwspec = data; + struct lpc18xx_gpio_pin_ic *ic = domain->host_data; + irq_hw_number_t hwirq; + int ret; + + if (nr_irqs != 1) + return -EINVAL; + + hwirq = fwspec->param[0]; + if (hwirq >= NR_LPC18XX_GPIO_PIN_IC_IRQS) + return -EINVAL; + + /* + * All LPC18xx/LPC43xx GPIO pin hardware interrupts are translated + * into edge interrupts 32...39 on parent Cortex-M3/M4 NVIC + */ + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 1; + parent_fwspec.param[0] = hwirq + 32; + + ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec); + if (ret < 0) { + pr_err("failed to allocate parent irq %u: %d\n", + parent_fwspec.param[0], ret); + return ret; + } + + return irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + &lpc18xx_gpio_pin_ic, ic); +} + +static const struct irq_domain_ops lpc18xx_gpio_pin_ic_domain_ops = { + .alloc = lpc18xx_gpio_pin_ic_domain_alloc, + .xlate = irq_domain_xlate_twocell, + .free = irq_domain_free_irqs_common, +}; + +static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc) +{ + struct device *dev = gc->gpio.parent; + struct irq_domain *parent_domain; + struct device_node *parent_node; + struct lpc18xx_gpio_pin_ic *ic; + struct resource res; + int ret, index; + + parent_node = of_irq_find_parent(dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + ic = devm_kzalloc(dev, sizeof(*ic), GFP_KERNEL); + if (!ic) + return -ENOMEM; + + index = of_property_match_string(dev->of_node, "reg-names", + "gpio-pin-ic"); + if (index < 0) { + ret = -ENODEV; + goto free_ic; + } + + ret = of_address_to_resource(dev->of_node, index, &res); + if (ret < 0) + goto free_ic; + + ic->base = devm_ioremap_resource(dev, &res); + if (IS_ERR(ic->base)) { + ret = PTR_ERR(ic->base); + goto free_ic; + } + + raw_spin_lock_init(&ic->lock); + + ic->domain = irq_domain_add_hierarchy(parent_domain, 0, + NR_LPC18XX_GPIO_PIN_IC_IRQS, + dev->of_node, + &lpc18xx_gpio_pin_ic_domain_ops, + ic); + if (!ic->domain) { + pr_err("unable to add irq domain\n"); + ret = -ENODEV; + goto free_iomap; + } + + gc->pin_ic = ic; + + return 0; + +free_iomap: + devm_iounmap(dev, ic->base); +free_ic: + devm_kfree(dev, ic); + + return ret; +} + static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip); @@ -92,45 +326,62 @@ static const struct gpio_chip lpc18xx_chip = { static int lpc18xx_gpio_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct lpc18xx_gpio_chip *gc; - struct resource *res; - int ret; + int index, ret; - gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); if (!gc) return -ENOMEM; gc->gpio = lpc18xx_chip; platform_set_drvdata(pdev, gc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gc->base = devm_ioremap_resource(&pdev->dev, res); + index = of_property_match_string(dev->of_node, "reg-names", "gpio"); + if (index < 0) { + /* To support backward compatibility take the first resource */ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gc->base = devm_ioremap_resource(dev, res); + } else { + struct resource res; + + ret = of_address_to_resource(dev->of_node, index, &res); + if (ret < 0) + return ret; + + gc->base = devm_ioremap_resource(dev, &res); + } if (IS_ERR(gc->base)) return PTR_ERR(gc->base); - gc->clk = devm_clk_get(&pdev->dev, NULL); + gc->clk = devm_clk_get(dev, NULL); if (IS_ERR(gc->clk)) { - dev_err(&pdev->dev, "input clock not found\n"); + dev_err(dev, "input clock not found\n"); return PTR_ERR(gc->clk); } ret = clk_prepare_enable(gc->clk); if (ret) { - dev_err(&pdev->dev, "unable to enable clock\n"); + dev_err(dev, "unable to enable clock\n"); return ret; } spin_lock_init(&gc->lock); - gc->gpio.parent = &pdev->dev; + gc->gpio.parent = dev; - ret = gpiochip_add_data(&gc->gpio, gc); + ret = devm_gpiochip_add_data(dev, &gc->gpio, gc); if (ret) { - dev_err(&pdev->dev, "failed to add gpio chip\n"); + dev_err(dev, "failed to add gpio chip\n"); clk_disable_unprepare(gc->clk); return ret; } + /* On error GPIO pin interrupt controller just won't be registered */ + lpc18xx_gpio_pin_ic_probe(gc); + return 0; } @@ -138,7 +389,9 @@ static int lpc18xx_gpio_remove(struct platform_device *pdev) { struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev); - gpiochip_remove(&gc->gpio); + if (gc->pin_ic) + irq_domain_remove(gc->pin_ic->domain); + clk_disable_unprepare(gc->clk); return 0; @@ -161,5 +414,6 @@ static struct platform_driver lpc18xx_gpio_driver = { module_platform_driver(lpc18xx_gpio_driver); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); +MODULE_AUTHOR("Vladimir Zapolskiy <vz@mleia.com>"); MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 24dd4069262a..31b4a091ab60 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -396,8 +396,7 @@ static int lp_gpio_runtime_resume(struct device *dev) static int lp_gpio_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct lp_gpio *lg = platform_get_drvdata(pdev); + struct lp_gpio *lg = dev_get_drvdata(dev); unsigned long reg; int i; diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index d72af6f6cdbd..00e954f22bc9 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -244,6 +244,8 @@ mediatek_gpio_bank_probe(struct device *dev, rg->chip.of_xlate = mediatek_gpio_xlate; rg->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", dev_name(dev), bank); + if (!rg->chip.label) + return -ENOMEM; ret = devm_gpiochip_add_data(dev, &rg->chip, mtk); if (ret < 0) { @@ -295,6 +297,7 @@ mediatek_gpio_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct mtk *mtk; int i; + int ret; mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL); if (!mtk) @@ -309,8 +312,11 @@ mediatek_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mtk); mediatek_gpio_irq_chip.name = dev_name(dev); - for (i = 0; i < MTK_BANK_CNT; i++) - mediatek_gpio_bank_probe(dev, np, i); + for (i = 0; i < MTK_BANK_CNT; i++) { + ret = mediatek_gpio_bank_probe(dev, np, i); + if (ret) + return ret; + } return 0; } diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 6e02148c208b..6c675c5accba 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -608,7 +608,7 @@ static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ret = -EBUSY; } else { desc = gpiochip_request_own_desc(&mvchip->chip, - pwm->hwpwm, "mvebu-pwm"); + pwm->hwpwm, "mvebu-pwm", 0); if (IS_ERR(desc)) { ret = PTR_ERR(desc); goto out; diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 995cf0b9e0b1..2d1dfa1e0745 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -17,6 +17,7 @@ #include <linux/irqchip/chained_irq.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/syscore_ops.h> #include <linux/gpio/driver.h> #include <linux/of.h> #include <linux/of_device.h> @@ -550,33 +551,38 @@ static void mxc_gpio_restore_regs(struct mxc_gpio_port *port) writel(port->gpio_saved_reg.dr, port->base + GPIO_DR); } -static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev) +static int mxc_gpio_syscore_suspend(void) { - struct platform_device *pdev = to_platform_device(dev); - struct mxc_gpio_port *port = platform_get_drvdata(pdev); + struct mxc_gpio_port *port; - mxc_gpio_save_regs(port); - clk_disable_unprepare(port->clk); + /* walk through all ports */ + list_for_each_entry(port, &mxc_gpio_ports, node) { + mxc_gpio_save_regs(port); + clk_disable_unprepare(port->clk); + } return 0; } -static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev) +static void mxc_gpio_syscore_resume(void) { - struct platform_device *pdev = to_platform_device(dev); - struct mxc_gpio_port *port = platform_get_drvdata(pdev); + struct mxc_gpio_port *port; int ret; - ret = clk_prepare_enable(port->clk); - if (ret) - return ret; - mxc_gpio_restore_regs(port); - - return 0; + /* walk through all ports */ + list_for_each_entry(port, &mxc_gpio_ports, node) { + ret = clk_prepare_enable(port->clk); + if (ret) { + pr_err("mxc: failed to enable gpio clock %d\n", ret); + return; + } + mxc_gpio_restore_regs(port); + } } -static const struct dev_pm_ops mxc_gpio_dev_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) +static struct syscore_ops mxc_gpio_syscore_ops = { + .suspend = mxc_gpio_syscore_suspend, + .resume = mxc_gpio_syscore_resume, }; static struct platform_driver mxc_gpio_driver = { @@ -584,7 +590,6 @@ static struct platform_driver mxc_gpio_driver = { .name = "gpio-mxc", .of_match_table = mxc_gpio_dt_ids, .suppress_bind_attrs = true, - .pm = &mxc_gpio_dev_pm_ops, }, .probe = mxc_gpio_probe, .id_table = mxc_gpio_devtype, @@ -592,6 +597,8 @@ static struct platform_driver mxc_gpio_driver = { static int __init gpio_mxc_init(void) { + register_syscore_ops(&mxc_gpio_syscore_ops); + return platform_driver_register(&mxc_gpio_driver); } subsys_initcall(gpio_mxc_init); diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 9887c3db6e16..6ed621a00561 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -984,8 +984,7 @@ omap4_gpio_disable_level_quirk(struct gpio_bank *bank) static int omap_mpuio_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); + struct gpio_bank *bank = dev_get_drvdata(dev); void __iomem *mask_reg = bank->base + OMAP_MPUIO_GPIO_MASKIT / bank->stride; unsigned long flags; @@ -999,8 +998,7 @@ static int omap_mpuio_suspend_noirq(struct device *dev) static int omap_mpuio_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); + struct gpio_bank *bank = dev_get_drvdata(dev); void __iomem *mask_reg = bank->base + OMAP_MPUIO_GPIO_MASKIT / bank->stride; unsigned long flags; @@ -1688,8 +1686,7 @@ static void omap_gpio_restore_context(struct gpio_bank *bank) 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); + struct gpio_bank *bank = dev_get_drvdata(dev); unsigned long flags; int error = 0; @@ -1709,8 +1706,7 @@ unlock: 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); + struct gpio_bank *bank = dev_get_drvdata(dev); unsigned long flags; int error = 0; diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 25d16b2af1c3..6b7349783223 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -146,7 +146,7 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip, port_state = ioread8(ports[i]); /* store acquired bits at respective bits array offset */ - bits[word_index] |= port_state << word_offset; + bits[word_index] |= (port_state << word_offset) & word_mask; } return 0; diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index f953541e7890..52f1647a46fd 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -243,7 +243,7 @@ static int idio_24_gpio_get_multiple(struct gpio_chip *chip, port_state = ioread8(&idio24gpio->reg->ttl_in0_7); /* store acquired bits at respective bits array offset */ - bits[word_index] |= port_state << word_offset; + bits[word_index] |= (port_state << word_offset) & word_mask; } return 0; diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 2afd9de84a0d..dc42571e6fdc 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -54,6 +54,7 @@ struct pl061 { void __iomem *base; struct gpio_chip gc; + struct irq_chip irq_chip; int parent_irq; #ifdef CONFIG_PM @@ -281,15 +282,6 @@ static int pl061_irq_set_wake(struct irq_data *d, unsigned int state) return irq_set_irq_wake(pl061->parent_irq, state); } -static struct irq_chip pl061_irqchip = { - .name = "pl061", - .irq_ack = pl061_irq_ack, - .irq_mask = pl061_irq_mask, - .irq_unmask = pl061_irq_unmask, - .irq_set_type = pl061_irq_type, - .irq_set_wake = pl061_irq_set_wake, -}; - static int pl061_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; @@ -328,6 +320,13 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) /* * irq_chip support */ + pl061->irq_chip.name = dev_name(dev); + pl061->irq_chip.irq_ack = pl061_irq_ack; + pl061->irq_chip.irq_mask = pl061_irq_mask; + pl061->irq_chip.irq_unmask = pl061_irq_unmask; + pl061->irq_chip.irq_set_type = pl061_irq_type; + pl061->irq_chip.irq_set_wake = pl061_irq_set_wake; + writeb(0, pl061->base + GPIOIE); /* disable irqs */ irq = adev->irq[0]; if (irq < 0) { @@ -336,14 +335,14 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) } pl061->parent_irq = irq; - ret = gpiochip_irqchip_add(&pl061->gc, &pl061_irqchip, + ret = gpiochip_irqchip_add(&pl061->gc, &pl061->irq_chip, 0, handle_bad_irq, IRQ_TYPE_NONE); if (ret) { dev_info(&adev->dev, "could not add irqchip\n"); return ret; } - gpiochip_set_chained_irqchip(&pl061->gc, &pl061_irqchip, + gpiochip_set_chained_irqchip(&pl061->gc, &pl061->irq_chip, irq, pl061_irq_handler); amba_set_drvdata(adev, pl061); diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 3c82bb3c2030..068ce25ffd28 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas R-Car GPIO Support * * Copyright (C) 2014 Renesas Electronics Corporation * Copyright (C) 2013 Magnus Damm - * - * 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 - * - * 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 <linux/err.h> @@ -43,7 +35,7 @@ struct gpio_rcar_bank_info { struct gpio_rcar_priv { void __iomem *base; spinlock_t lock; - struct platform_device *pdev; + struct device *dev; struct gpio_chip gpio_chip; struct irq_chip irq_chip; unsigned int irq_parent; @@ -148,7 +140,7 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_rcar_priv *p = gpiochip_get_data(gc); unsigned int hwirq = irqd_to_hwirq(d); - dev_dbg(&p->pdev->dev, "sense irq = %d, type = %d\n", hwirq, type); + dev_dbg(p->dev, "sense irq = %d, type = %d\n", hwirq, type); switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_LEVEL_HIGH: @@ -188,8 +180,7 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on) if (p->irq_parent) { error = irq_set_irq_wake(p->irq_parent, on); if (error) { - dev_dbg(&p->pdev->dev, - "irq %u doesn't support irq_set_wake\n", + dev_dbg(p->dev, "irq %u doesn't support irq_set_wake\n", p->irq_parent); p->irq_parent = 0; } @@ -252,13 +243,13 @@ static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) struct gpio_rcar_priv *p = gpiochip_get_data(chip); int error; - error = pm_runtime_get_sync(&p->pdev->dev); + error = pm_runtime_get_sync(p->dev); if (error < 0) return error; error = pinctrl_gpio_request(chip->base + offset); if (error) - pm_runtime_put(&p->pdev->dev); + pm_runtime_put(p->dev); return error; } @@ -275,7 +266,7 @@ static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) */ gpio_rcar_config_general_input_output_mode(chip, offset, false); - pm_runtime_put(&p->pdev->dev); + pm_runtime_put(p->dev); } static int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset) @@ -406,21 +397,20 @@ MODULE_DEVICE_TABLE(of, gpio_rcar_of_table); static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) { - struct device_node *np = p->pdev->dev.of_node; + struct device_node *np = p->dev->of_node; const struct gpio_rcar_info *info; struct of_phandle_args args; int ret; - info = of_device_get_match_data(&p->pdev->dev); + info = of_device_get_match_data(p->dev); ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; p->has_both_edge_trigger = info->has_both_edge_trigger; if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { - dev_warn(&p->pdev->dev, - "Invalid number of gpio lines %u, using %u\n", *npins, - RCAR_MAX_GPIO_PER_BANK); + dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n", + *npins, RCAR_MAX_GPIO_PER_BANK); *npins = RCAR_MAX_GPIO_PER_BANK; } @@ -442,7 +432,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) if (!p) return -ENOMEM; - p->pdev = pdev; + p->dev = dev; spin_lock_init(&p->lock); /* Get device configuration from DT node */ diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index 5497f0a88cf0..4df5335469fd 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -188,7 +188,7 @@ static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset, struct sch311x_gpio_block *block = gpiochip_get_data(chip); spin_lock(&block->lock); - __sch311x_gpio_set(block, offset, value); + __sch311x_gpio_set(block, offset, value); spin_unlock(&block->lock); } diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 47dbd19751d0..02f6db925fd5 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -404,8 +404,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) #ifdef CONFIG_PM_SLEEP static int tegra_gpio_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct tegra_gpio_info *tgi = platform_get_drvdata(pdev); + struct tegra_gpio_info *tgi = dev_get_drvdata(dev); unsigned long flags; unsigned int b, p; @@ -444,8 +443,7 @@ static int tegra_gpio_resume(struct device *dev) static int tegra_gpio_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct tegra_gpio_info *tgi = platform_get_drvdata(pdev); + struct tegra_gpio_info *tgi = dev_get_drvdata(dev); unsigned long flags; unsigned int b, p; diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 74551cbdb2e8..0f662b297a95 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -1,16 +1,7 @@ -/* - * Copyright (C) 2017 Socionext Inc. - * Author: Masahiro Yamada <yamada.masahiro@socionext.com> - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2017 Socionext Inc. +// Author: Masahiro Yamada <yamada.masahiro@socionext.com> #include <linux/bits.h> #include <linux/gpio/driver.h> diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 5960396c8d9a..1b79ebcfce3e 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -7,6 +7,7 @@ * Author: Stefan Agner <stefan@agner.ch>. */ #include <linux/bitops.h> +#include <linux/clk.h> #include <linux/err.h> #include <linux/gpio/driver.h> #include <linux/init.h> @@ -32,6 +33,8 @@ struct vf610_gpio_port { void __iomem *gpio_base; const struct fsl_gpio_soc_data *sdata; u8 irqc[VF610_GPIO_PER_PORT]; + struct clk *clk_port; + struct clk *clk_gpio; int irq; }; @@ -271,6 +274,33 @@ static int vf610_gpio_probe(struct platform_device *pdev) if (port->irq < 0) return port->irq; + port->clk_port = devm_clk_get(&pdev->dev, "port"); + if (!IS_ERR(port->clk_port)) { + ret = clk_prepare_enable(port->clk_port); + if (ret) + return ret; + } else if (port->clk_port == ERR_PTR(-EPROBE_DEFER)) { + /* + * Percolate deferrals, for anything else, + * just live without the clocking. + */ + return PTR_ERR(port->clk_port); + } + + port->clk_gpio = devm_clk_get(&pdev->dev, "gpio"); + if (!IS_ERR(port->clk_gpio)) { + ret = clk_prepare_enable(port->clk_gpio); + if (ret) { + clk_disable_unprepare(port->clk_port); + return ret; + } + } else if (port->clk_gpio == ERR_PTR(-EPROBE_DEFER)) { + clk_disable_unprepare(port->clk_port); + return PTR_ERR(port->clk_gpio); + } + + platform_set_drvdata(pdev, port); + gc = &port->gc; gc->of_node = np; gc->parent = dev; @@ -305,12 +335,26 @@ static int vf610_gpio_probe(struct platform_device *pdev) return 0; } +static int vf610_gpio_remove(struct platform_device *pdev) +{ + struct vf610_gpio_port *port = platform_get_drvdata(pdev); + + gpiochip_remove(&port->gc); + if (!IS_ERR(port->clk_port)) + clk_disable_unprepare(port->clk_port); + if (!IS_ERR(port->clk_gpio)) + clk_disable_unprepare(port->clk_gpio); + + return 0; +} + static struct platform_driver vf610_gpio_driver = { .driver = { .name = "gpio-vf610", .of_match_table = vf610_gpio_dt_ids, }, .probe = vf610_gpio_probe, + .remove = vf610_gpio_remove, }; builtin_platform_driver(vf610_gpio_driver); diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index c7028eb0b8e1..5cf3697bfb15 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -169,7 +169,7 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip, port_state = inb(ws16c48gpio->base + i); /* store acquired bits at respective bits array offset */ - bits[word_index] |= port_state << word_offset; + bits[word_index] |= (port_state << word_offset) & word_mask; } return 0; diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 3f5fcdd5a429..b3b4edcdffe0 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -358,6 +358,28 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, } /** + * zynq_gpio_get_direction - Read the direction of the specified GPIO pin + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function returns the direction of the specified GPIO. + * + * Return: 0 for output, 1 for input + */ +static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio *gpio = gpiochip_get_data(chip); + + zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); + + reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + return !(reg & BIT(bank_pin_num)); +} + +/** * zynq_gpio_irq_mask - Disable the interrupts for a gpio pin * @irq_data: per irq and chip data passed down to chip functions * @@ -693,8 +715,7 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev) static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct zynq_gpio *gpio = platform_get_drvdata(pdev); + struct zynq_gpio *gpio = dev_get_drvdata(dev); clk_disable_unprepare(gpio->clk); @@ -703,8 +724,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev) static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct zynq_gpio *gpio = platform_get_drvdata(pdev); + struct zynq_gpio *gpio = dev_get_drvdata(dev); return clk_prepare_enable(gpio->clk); } @@ -827,6 +847,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) chip->free = zynq_gpio_free; chip->direction_input = zynq_gpio_dir_in; chip->direction_output = zynq_gpio_dir_out; + chip->get_direction = zynq_gpio_get_direction; chip->base = of_alias_get_id(pdev->dev.of_node, "gpio"); chip->ngpio = gpio->p_data->ngpio; diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 55b72fbe1631..722a9befa8a9 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -167,7 +167,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, if (!handler) return AE_OK; - desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event"); + desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event", 0); if (IS_ERR(desc)) { dev_err(chip->parent, "Failed to request GPIO\n"); return AE_ERROR; @@ -884,21 +884,14 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, const char *label = "ACPI:OpRegion"; int err; - desc = gpiochip_request_own_desc(chip, pin, label); + desc = gpiochip_request_own_desc(chip, pin, label, + flags); if (IS_ERR(desc)) { status = AE_ERROR; mutex_unlock(&achip->conn_lock); goto out; } - err = gpiod_configure_flags(desc, label, 0, flags); - if (err < 0) { - status = AE_NOT_CONFIGURED; - gpiochip_free_own_desc(desc); - mutex_unlock(&achip->conn_lock); - goto out; - } - conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) { status = AE_NO_MEMORY; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 7f1260c78270..fa8044228f0e 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0+ /* * OF helpers for the GPIO API * @@ -58,6 +58,45 @@ static void of_gpio_flags_quirks(struct device_node *np, int index) { /* + * Handle MMC "cd-inverted" and "wp-inverted" semantics. + */ + if (IS_ENABLED(CONFIG_MMC)) { + if (of_property_read_bool(np, "cd-gpios")) { + if (of_property_read_bool(np, "cd-inverted")) { + if (*flags & OF_GPIO_ACTIVE_LOW) { + /* "cd-inverted" takes precedence */ + *flags &= ~OF_GPIO_ACTIVE_LOW; + pr_warn("%s GPIO handle specifies CD active low - ignored\n", + of_node_full_name(np)); + } + } else { + /* + * Active low is the default according to the + * SDHCI specification. If the GPIO handle + * specifies the same thing - good. + */ + *flags |= OF_GPIO_ACTIVE_LOW; + } + } + if (of_property_read_bool(np, "wp-gpios")) { + if (of_property_read_bool(np, "wp-inverted")) { + /* "wp-inverted" takes precedence */ + if (*flags & OF_GPIO_ACTIVE_LOW) { + *flags &= ~OF_GPIO_ACTIVE_LOW; + pr_warn("%s GPIO handle specifies WP active low - ignored\n", + of_node_full_name(np)); + } + } else { + /* + * Active low is the default according to the + * SDHCI specification. If the GPIO handle + * specifies the same thing - good. + */ + *flags |= OF_GPIO_ACTIVE_LOW; + } + } + } + /* * Some GPIO fixed regulator quirks. * Note that active low is the default. */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 230e41562462..2ec8b0d2096a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1511,19 +1511,6 @@ static void devm_gpio_chip_release(struct device *dev, void *res) gpiochip_remove(chip); } -static int devm_gpio_chip_match(struct device *dev, void *res, void *data) - -{ - struct gpio_chip **r = res; - - if (!r || !*r) { - WARN_ON(!r || !*r); - return 0; - } - - return *r == data; -} - /** * devm_gpiochip_add_data() - Resource manager gpiochip_add_data() * @dev: pointer to the device that gpio_chip belongs to. @@ -1564,23 +1551,6 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip, EXPORT_SYMBOL_GPL(devm_gpiochip_add_data); /** - * devm_gpiochip_remove() - Resource manager of gpiochip_remove() - * @dev: device for which which resource was allocated - * @chip: the chip to remove - * - * A gpio_chip with any GPIOs still requested may not be removed. - */ -void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip) -{ - int ret; - - ret = devres_release(dev, devm_gpio_chip_release, - devm_gpio_chip_match, chip); - WARN_ON(ret); -} -EXPORT_SYMBOL_GPL(devm_gpiochip_remove); - -/** * gpiochip_find() - iterator for locating a specific gpio_chip * @data: data to pass to match function * @match: Callback function to check gpio_chip @@ -2298,6 +2268,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) unsigned long flags; unsigned offset; + if (label) { + label = kstrdup_const(label, GFP_KERNEL); + if (!label) + return -ENOMEM; + } + spin_lock_irqsave(&gpio_lock, flags); /* NOTE: gpio_request() can be called in early boot, @@ -2308,6 +2284,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) desc_set_label(desc, label ? : "?"); status = 0; } else { + kfree_const(label); status = -EBUSY; goto done; } @@ -2324,6 +2301,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (status < 0) { desc_set_label(desc, NULL); + kfree_const(label); clear_bit(FLAG_REQUESTED, &desc->flags); goto done; } @@ -2419,6 +2397,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) chip->free(chip, gpio_chip_hwgpio(desc)); spin_lock_irqsave(&gpio_lock, flags); } + kfree_const(desc->label); desc_set_label(desc, NULL); clear_bit(FLAG_ACTIVE_LOW, &desc->flags); clear_bit(FLAG_REQUESTED, &desc->flags); @@ -2475,6 +2454,7 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * @chip: GPIO chip * @hwnum: hardware number of the GPIO for which to request the descriptor * @label: label for the GPIO + * @flags: flags for this GPIO or 0 if default * * Function allows GPIO chip drivers to request and use their own GPIO * descriptors via gpiolib API. Difference to gpiod_request() is that this @@ -2487,7 +2467,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * code on failure. */ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, - const char *label) + const char *label, + enum gpiod_flags flags) { struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum); int err; @@ -2501,6 +2482,13 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, if (err < 0) return ERR_PTR(err); + err = gpiod_configure_flags(desc, label, 0, flags); + if (err) { + chip_err(chip, "setup of own GPIO %s failed\n", label); + gpiod_free_commit(desc); + return ERR_PTR(err); + } + return desc; } EXPORT_SYMBOL_GPL(gpiochip_request_own_desc); @@ -3374,11 +3362,19 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); * @desc: gpio to set the consumer name on * @name: the new consumer name */ -void gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) +int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) { - VALIDATE_DESC_VOID(desc); - /* Just overwrite whatever the previous name was */ - desc->label = name; + VALIDATE_DESC(desc); + if (name) { + name = kstrdup_const(name, GFP_KERNEL); + if (!name) + return -ENOMEM; + } + + kfree_const(desc->label); + desc_set_label(desc, name); + + return 0; } EXPORT_SYMBOL_GPL(gpiod_set_consumer_name); @@ -4345,7 +4341,15 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, chip = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); - local_desc = gpiochip_request_own_desc(chip, hwnum, name); + /* + * FIXME: not very elegant that we call gpiod_configure_flags() + * twice here (once inside gpiochip_request_own_desc() and + * again here), but the gpiochip_request_own_desc() is external + * and cannot really pass the lflags so this is the lesser evil + * at the moment. Pass zero as dflags on this first call so we + * don't screw anything up. + */ + local_desc = gpiochip_request_own_desc(chip, hwnum, name, 0); if (IS_ERR(local_desc)) { status = PTR_ERR(local_desc); pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n", |