diff options
Diffstat (limited to 'drivers/gpio')
28 files changed, 837 insertions, 765 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4b6d2ef15c39..b585f2ef6dd9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -312,6 +312,12 @@ config GPIO_IXP4XX IXP4xx series of chips. If unsure, say N. +config GPIO_LOGICVC + tristate "Xylon LogiCVC GPIO support" + depends on MFD_SYSCON && OF + help + Say yes here to support GPIO functionality of the Xylon LogiCVC + programmable logic block. config GPIO_LOONGSON bool "Loongson-2/3 GPIO support" @@ -335,14 +341,6 @@ config GPIO_LPC32XX Select this option to enable GPIO driver for NXP LPC32XX devices. -config GPIO_LYNXPOINT - tristate "Intel Lynxpoint GPIO support" - depends on ACPI && X86 - select GPIOLIB_IRQCHIP - help - driver for GPIO functionality on Intel Lynxpoint PCH chipset - Requires ACPI device enumeration code to set up a platform device. - config GPIO_MB86S7X tristate "GPIO support for Fujitsu MB86S7x Platforms" help @@ -479,6 +477,15 @@ config GPIO_SAMA5D2_PIOBU The difference from regular GPIOs is that they maintain their value during backup/self-refresh. +config GPIO_SIFIVE + bool "SiFive GPIO support" + depends on OF_GPIO && IRQ_DOMAIN_HIERARCHY + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + select REGMAP_MMIO + help + Say yes here to support the GPIO device on SiFive SoCs. + config GPIO_SIOX tristate "SIOX GPIO support" depends on SIOX @@ -573,6 +580,7 @@ config GPIO_THUNDERX tristate "Cavium ThunderX/OCTEON-TX GPIO" depends on ARCH_THUNDER || (64BIT && COMPILE_TEST) depends on PCI_MSI + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS help @@ -612,6 +620,13 @@ config GPIO_VX855 additional drivers must be enabled in order to use the functionality of the device. +config GPIO_WCD934X + tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver" + depends on MFD_WCD934X && OF_GPIO + help + This driver is to supprot GPIO block found on the Qualcomm Technologies + Inc WCD9340/WCD9341 Audio Codec. + config GPIO_XGENE bool "APM X-Gene GPIO controller support" depends on ARM64 && OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 34eb8b2b12dd..76ff2a5feaba 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_IT87) += gpio-it87.o obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o +obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o @@ -76,7 +77,6 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o -obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o @@ -124,6 +124,7 @@ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o +obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o @@ -157,6 +158,7 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 76f8c7ff18ff..3a44e6ae52bd 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -10,6 +10,28 @@ 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. +The numberspace issue is the same as to why irq is moving away from irq +numbers to IRQ descriptors. + +The underlying motivation for this is that the GPIO numberspace has become +unmanageable: machine board files tend to become full of macros trying to +establish the numberspace at compile-time, making it hard to add any numbers +in the middle (such as if you missed a pin on a chip) without the numberspace +breaking. + +Machine descriptions such as device tree or ACPI does not have a concept of the +Linux GPIO number as those descriptions are external to the Linux kernel +and treat GPIO lines as abstract entities. + +The runtime-assigned GPIO numberspace (what you get if you assign the GPIO +base as -1 in struct gpio_chip) has also became unpredictable due to factors +such as probe ordering and the introduction of -EPROBE_DEFER making probe +ordering of independent GPIO chips essentially unpredictable, as their base +number will be assigned on a first come first serve basis. + +The best way to get out of the problem is to make the global GPIO numbers +unimportant by simply not using them. GPIO descriptors deal with this. + Work items: - Convert all GPIO device drivers to only #include <linux/gpio/driver.h> @@ -33,7 +55,7 @@ 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. +the device tree back-end. It is legacy and should not be used in new code. Work items: @@ -59,6 +81,15 @@ Work items: uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead. +Get rid of <linux/gpio.h> + +This legacy header is a one stop shop for anything GPIO is closely tied +to the global GPIO numberspace. The endgame of the above refactorings will +be the removal of <linux/gpio.h> and from that point only the specialized +headers under <linux/gpio/*.h> will be used. This requires all the above to +be completed and is expected to take a long time. + + Collect drivers Collect GPIO drivers from arch/* and other places that should be placed @@ -109,7 +140,7 @@ try to cover any generic kind of irqchip cascaded from a GPIO. int irq; /* from platform etc */ struct my_gpio *g; - struct gpio_irq_chip *girq + struct gpio_irq_chip *girq; /* Set up the irqchip dynamically */ g->irq.name = "my_gpio_irq"; @@ -137,9 +168,14 @@ 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. +- Drop gpiochip_set_chained_irqchip() when all the chained irqchips + have been converted to the above infrastructure. + +- Add more infrastructure to make it possible to also pass a threaded + irqchip in struct gpio_irq_chip. + +- Drop gpiochip_irqchip_add_nested() when all the chained irqchips + have been converted to the above infrastructure. Increase integration with pin control diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 9f2e6b04c361..cc4ba71e4fe3 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -266,7 +266,7 @@ static int altera_gpio_probe(struct platform_device *pdev) altera_gc->mmchip.gc.owner = THIS_MODULE; altera_gc->mmchip.gc.parent = &pdev->dev; - altera_gc->mapped_irq = platform_get_irq(pdev, 0); + altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); if (altera_gc->mapped_irq < 0) goto skip_irq; diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 8319812593e3..d16645c1d8d9 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -391,7 +391,7 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, gpio->irq = rc; - /* Disable IRQ and clear Interrupt status registers for all SPGIO Pins. */ + /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { bank = &aspeed_sgpio_banks[i]; /* disable irq enable bits */ diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index f1037b61f763..879db23d8454 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -978,7 +978,7 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, } /** - * aspeed_gpio_copro_set_ops - Sets the callbacks used for handhsaking with + * aspeed_gpio_copro_set_ops - Sets the callbacks used for handshaking with * the coprocessor for shared GPIO banks * @ops: The callbacks * @data: Pointer passed back to the callbacks diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 4122683eb1f9..baee8c3f06ad 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -19,7 +19,6 @@ #include <linux/io.h> #include <linux/gpio/driver.h> #include <linux/of_device.h> -#include <linux/of_irq.h> #include <linux/init.h> #include <linux/irqdomain.h> #include <linux/irqchip/chained_irq.h> @@ -586,11 +585,18 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) kona_gpio->gpio_chip = template_chip; chip = &kona_gpio->gpio_chip; - kona_gpio->num_bank = of_irq_count(dev->of_node); - if (kona_gpio->num_bank == 0) { + ret = platform_irq_count(pdev); + if (!ret) { dev_err(dev, "Couldn't determine # GPIO banks\n"); return -ENOENT; + } else if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Couldn't determine GPIO banks: (%pe)\n", + ERR_PTR(ret)); + return ret; } + kona_gpio->num_bank = ret; + if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) { dev_err(dev, "Too many GPIO banks configured (max=%d)\n", GPIO_MAX_BANK_NUM); diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index ff19a8ad5663..1d0827e79703 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -64,11 +64,11 @@ static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg, if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8) return -EINVAL; - /* Check that on valiue fits it's placeholder */ + /* Check that on value fits its placeholder */ if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i]) return -EINVAL; - /* Check that off valiue fits it's placeholder */ + /* Check that off value fits its placeholder */ if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i]) return -EINVAL; diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 08234e64993a..f954359c9544 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -253,17 +253,16 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, lirq->irq = irq; uirq = &priv->uirqs[lirq->index]; if (uirq->refcnt == 0) { + spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, dev_name(priv->dev), priv); if (ret) { dev_err(priv->dev, "Could not request underlying irq %d\n", uirq->uirq); - - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - return ret; } + spin_lock_irqsave(&priv->gc.bgpio_lock, flags); } uirq->refcnt++; @@ -309,8 +308,11 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) if (index >= 0) { uirq = &priv->uirqs[lirq->index]; uirq->refcnt--; - if (uirq->refcnt == 0) + if (uirq->refcnt == 0) { + spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); free_irq(uirq->uirq, priv); + return; + } } spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); @@ -433,12 +435,9 @@ static int grgpio_probe(struct platform_device *ofdev) static int grgpio_remove(struct platform_device *ofdev) { struct grgpio_priv *priv = platform_get_drvdata(ofdev); - unsigned long flags; int i; int ret = 0; - spin_lock_irqsave(&priv->gc.bgpio_lock, flags); - if (priv->domain) { for (i = 0; i < GRGPIO_MAX_NGPIO; i++) { if (priv->uirqs[i].refcnt != 0) { @@ -454,8 +453,6 @@ static int grgpio_remove(struct platform_device *ofdev) irq_domain_remove(priv->domain); out: - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - return ret; } diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c new file mode 100644 index 000000000000..015632cf159f --- /dev/null +++ b/drivers/gpio/gpio-logicvc.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#define LOGICVC_CTRL_REG 0x40 +#define LOGICVC_CTRL_GPIO_SHIFT 11 +#define LOGICVC_CTRL_GPIO_BITS 5 + +#define LOGICVC_POWER_CTRL_REG 0x78 +#define LOGICVC_POWER_CTRL_GPIO_SHIFT 0 +#define LOGICVC_POWER_CTRL_GPIO_BITS 4 + +struct logicvc_gpio { + struct gpio_chip chip; + struct regmap *regmap; +}; + +static void logicvc_gpio_offset(struct logicvc_gpio *logicvc, unsigned offset, + unsigned int *reg, unsigned int *bit) +{ + if (offset >= LOGICVC_CTRL_GPIO_BITS) { + *reg = LOGICVC_POWER_CTRL_REG; + + /* To the (virtual) power ctrl offset. */ + offset -= LOGICVC_CTRL_GPIO_BITS; + /* To the actual bit offset in reg. */ + offset += LOGICVC_POWER_CTRL_GPIO_SHIFT; + } else { + *reg = LOGICVC_CTRL_REG; + + /* To the actual bit offset in reg. */ + offset += LOGICVC_CTRL_GPIO_SHIFT; + } + + *bit = BIT(offset); +} + +static int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct logicvc_gpio *logicvc = gpiochip_get_data(chip); + unsigned int reg, bit, value; + int ret; + + logicvc_gpio_offset(logicvc, offset, ®, &bit); + + ret = regmap_read(logicvc->regmap, reg, &value); + if (ret) + return ret; + + return !!(value & bit); +} + +static void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct logicvc_gpio *logicvc = gpiochip_get_data(chip); + unsigned int reg, bit; + + logicvc_gpio_offset(logicvc, offset, ®, &bit); + + regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0); +} + +static int logicvc_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + /* Pins are always configured as output, so just set the value. */ + logicvc_gpio_set(chip, offset, value); + + return 0; +} + +static struct regmap_config logicvc_gpio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .name = "logicvc-gpio", +}; + +static int logicvc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *of_node = dev->of_node; + struct logicvc_gpio *logicvc; + int ret; + + logicvc = devm_kzalloc(dev, sizeof(*logicvc), GFP_KERNEL); + if (!logicvc) + return -ENOMEM; + + /* Try to get regmap from parent first. */ + logicvc->regmap = syscon_node_to_regmap(of_node->parent); + + /* Grab our own regmap if that fails. */ + if (IS_ERR(logicvc->regmap)) { + struct resource res; + void __iomem *base; + + ret = of_address_to_resource(of_node, 0, &res); + if (ret) { + dev_err(dev, "Failed to get resource from address\n"); + return ret; + } + + base = devm_ioremap_resource(dev, &res); + if (IS_ERR(base)) { + dev_err(dev, "Failed to map I/O base\n"); + return PTR_ERR(base); + } + + logicvc_gpio_regmap_config.max_register = resource_size(&res) - + logicvc_gpio_regmap_config.reg_stride; + + logicvc->regmap = + devm_regmap_init_mmio(dev, base, + &logicvc_gpio_regmap_config); + if (IS_ERR(logicvc->regmap)) { + dev_err(dev, "Failed to create regmap for I/O\n"); + return PTR_ERR(logicvc->regmap); + } + } + + logicvc->chip.parent = dev; + logicvc->chip.owner = THIS_MODULE; + logicvc->chip.label = dev_name(dev); + logicvc->chip.base = -1; + logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS + + LOGICVC_POWER_CTRL_GPIO_BITS; + logicvc->chip.get = logicvc_gpio_get; + logicvc->chip.set = logicvc_gpio_set; + logicvc->chip.direction_output = logicvc_gpio_direction_output; + + platform_set_drvdata(pdev, logicvc); + + return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); +} + +static const struct of_device_id logicivc_gpio_of_table[] = { + { + .compatible = "xylon,logicvc-3.02.a-gpio", + }, + { } +}; + +MODULE_DEVICE_TABLE(of, logicivc_gpio_of_table); + +static struct platform_driver logicvc_gpio_driver = { + .driver = { + .name = "gpio-logicvc", + .of_match_table = logicivc_gpio_of_table, + }, + .probe = logicvc_gpio_probe, +}; + +module_platform_driver(logicvc_gpio_driver); + +MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); +MODULE_DESCRIPTION("Xylon LogiCVC GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c deleted file mode 100644 index 490ce7bae25e..000000000000 --- a/drivers/gpio/gpio-lynxpoint.c +++ /dev/null @@ -1,471 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * GPIO controller driver for Intel Lynxpoint PCH chipset> - * Copyright (c) 2012, Intel Corporation. - * - * Author: Mathias Nyman <mathias.nyman@linux.intel.com> - */ - -#include <linux/acpi.h> -#include <linux/bitops.h> -#include <linux/gpio/driver.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/types.h> - -/* LynxPoint chipset has support for 94 gpio pins */ - -#define LP_NUM_GPIO 94 - -/* Bitmapped register offsets */ -#define LP_ACPI_OWNED 0x00 /* Bitmap, set by bios, 0: pin reserved for ACPI */ -#define LP_GC 0x7C /* set APIC IRQ to IRQ14 or IRQ15 for all pins */ -#define LP_INT_STAT 0x80 -#define LP_INT_ENABLE 0x90 - -/* Each pin has two 32 bit config registers, starting at 0x100 */ -#define LP_CONFIG1 0x100 -#define LP_CONFIG2 0x104 - -/* LP_CONFIG1 reg bits */ -#define OUT_LVL_BIT BIT(31) -#define IN_LVL_BIT BIT(30) -#define TRIG_SEL_BIT BIT(4) /* 0: Edge, 1: Level */ -#define INT_INV_BIT BIT(3) /* Invert interrupt triggering */ -#define DIR_BIT BIT(2) /* 0: Output, 1: Input */ -#define USE_SEL_BIT BIT(0) /* 0: Native, 1: GPIO */ - -/* LP_CONFIG2 reg bits */ -#define GPINDIS_BIT BIT(2) /* disable input sensing */ -#define GPIWP_BIT (BIT(0) | BIT(1)) /* weak pull options */ - -struct lp_gpio { - struct gpio_chip chip; - struct platform_device *pdev; - spinlock_t lock; - unsigned long reg_base; -}; - -/* - * Lynxpoint gpios are controlled through both bitmapped registers and - * per gpio specific registers. The bitmapped registers are in chunks of - * 3 x 32bit registers to cover all 94 gpios - * - * per gpio specific registers consist of two 32bit registers per gpio - * (LP_CONFIG1 and LP_CONFIG2), with 94 gpios there's a total of - * 188 config registers. - * - * A simplified view of the register layout look like this: - * - * LP_ACPI_OWNED[31:0] gpio ownerships for gpios 0-31 (bitmapped registers) - * LP_ACPI_OWNED[63:32] gpio ownerships for gpios 32-63 - * LP_ACPI_OWNED[94:64] gpio ownerships for gpios 63-94 - * ... - * LP_INT_ENABLE[31:0] ... - * LP_INT_ENABLE[63:31] ... - * LP_INT_ENABLE[94:64] ... - * LP0_CONFIG1 (gpio 0) config1 reg for gpio 0 (per gpio registers) - * LP0_CONFIG2 (gpio 0) config2 reg for gpio 0 - * LP1_CONFIG1 (gpio 1) config1 reg for gpio 1 - * LP1_CONFIG2 (gpio 1) config2 reg for gpio 1 - * LP2_CONFIG1 (gpio 2) ... - * LP2_CONFIG2 (gpio 2) ... - * ... - * LP94_CONFIG1 (gpio 94) ... - * LP94_CONFIG2 (gpio 94) ... - */ - -static unsigned long lp_gpio_reg(struct gpio_chip *chip, unsigned offset, - int reg) -{ - struct lp_gpio *lg = gpiochip_get_data(chip); - int reg_offset; - - if (reg == LP_CONFIG1 || reg == LP_CONFIG2) - /* per gpio specific config registers */ - reg_offset = offset * 8; - else - /* bitmapped registers */ - reg_offset = (offset / 32) * 4; - - return lg->reg_base + reg + reg_offset; -} - -static int lp_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct lp_gpio *lg = gpiochip_get_data(chip); - unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); - unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2); - unsigned long acpi_use = lp_gpio_reg(chip, offset, LP_ACPI_OWNED); - - pm_runtime_get(&lg->pdev->dev); /* should we put if failed */ - - /* Fail if BIOS reserved pin for ACPI use */ - if (!(inl(acpi_use) & BIT(offset % 32))) { - dev_err(&lg->pdev->dev, "gpio %d reserved for ACPI\n", offset); - return -EBUSY; - } - /* Fail if pin is in alternate function mode (not GPIO mode) */ - if (!(inl(reg) & USE_SEL_BIT)) - return -ENODEV; - - /* enable input sensing */ - outl(inl(conf2) & ~GPINDIS_BIT, conf2); - - - return 0; -} - -static void lp_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct lp_gpio *lg = gpiochip_get_data(chip); - unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2); - - /* disable input sensing */ - outl(inl(conf2) | GPINDIS_BIT, conf2); - - pm_runtime_put(&lg->pdev->dev); -} - -static int lp_irq_type(struct irq_data *d, unsigned type) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct lp_gpio *lg = gpiochip_get_data(gc); - u32 hwirq = irqd_to_hwirq(d); - unsigned long flags; - u32 value; - unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_CONFIG1); - - if (hwirq >= lg->chip.ngpio) - return -EINVAL; - - spin_lock_irqsave(&lg->lock, flags); - value = inl(reg); - - /* set both TRIG_SEL and INV bits to 0 for rising edge */ - if (type & IRQ_TYPE_EDGE_RISING) - value &= ~(TRIG_SEL_BIT | INT_INV_BIT); - - /* TRIG_SEL bit 0, INV bit 1 for falling edge */ - if (type & IRQ_TYPE_EDGE_FALLING) - value = (value | INT_INV_BIT) & ~TRIG_SEL_BIT; - - /* TRIG_SEL bit 1, INV bit 0 for level low */ - if (type & IRQ_TYPE_LEVEL_LOW) - value = (value | TRIG_SEL_BIT) & ~INT_INV_BIT; - - /* TRIG_SEL bit 1, INV bit 1 for level high */ - if (type & IRQ_TYPE_LEVEL_HIGH) - value |= TRIG_SEL_BIT | INT_INV_BIT; - - outl(value, reg); - - if (type & IRQ_TYPE_EDGE_BOTH) - irq_set_handler_locked(d, handle_edge_irq); - else if (type & IRQ_TYPE_LEVEL_MASK) - irq_set_handler_locked(d, handle_level_irq); - - spin_unlock_irqrestore(&lg->lock, flags); - - return 0; -} - -static int lp_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); - return !!(inl(reg) & IN_LVL_BIT); -} - -static void lp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct lp_gpio *lg = gpiochip_get_data(chip); - unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); - unsigned long flags; - - spin_lock_irqsave(&lg->lock, flags); - - if (value) - outl(inl(reg) | OUT_LVL_BIT, reg); - else - outl(inl(reg) & ~OUT_LVL_BIT, reg); - - spin_unlock_irqrestore(&lg->lock, flags); -} - -static int lp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct lp_gpio *lg = gpiochip_get_data(chip); - unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); - unsigned long flags; - - spin_lock_irqsave(&lg->lock, flags); - outl(inl(reg) | DIR_BIT, reg); - spin_unlock_irqrestore(&lg->lock, flags); - - return 0; -} - -static int lp_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct lp_gpio *lg = gpiochip_get_data(chip); - unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); - unsigned long flags; - - lp_gpio_set(chip, offset, value); - - spin_lock_irqsave(&lg->lock, flags); - outl(inl(reg) & ~DIR_BIT, reg); - spin_unlock_irqrestore(&lg->lock, flags); - - return 0; -} - -static void lp_gpio_irq_handler(struct irq_desc *desc) -{ - struct irq_data *data = irq_desc_get_irq_data(desc); - struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct lp_gpio *lg = gpiochip_get_data(gc); - struct irq_chip *chip = irq_data_get_irq_chip(data); - unsigned long reg, ena, pending; - u32 base, pin; - - /* check from GPIO controller which pin triggered the interrupt */ - for (base = 0; base < lg->chip.ngpio; base += 32) { - reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT); - ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE); - - /* Only interrupts that are enabled */ - pending = inl(reg) & inl(ena); - - for_each_set_bit(pin, &pending, 32) { - unsigned irq; - - /* Clear before handling so we don't lose an edge */ - outl(BIT(pin), reg); - - irq = irq_find_mapping(lg->chip.irq.domain, base + pin); - generic_handle_irq(irq); - } - } - chip->irq_eoi(data); -} - -static void lp_irq_unmask(struct irq_data *d) -{ -} - -static void lp_irq_mask(struct irq_data *d) -{ -} - -static void lp_irq_enable(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct lp_gpio *lg = gpiochip_get_data(gc); - u32 hwirq = irqd_to_hwirq(d); - unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE); - unsigned long flags; - - spin_lock_irqsave(&lg->lock, flags); - outl(inl(reg) | BIT(hwirq % 32), reg); - spin_unlock_irqrestore(&lg->lock, flags); -} - -static void lp_irq_disable(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct lp_gpio *lg = gpiochip_get_data(gc); - u32 hwirq = irqd_to_hwirq(d); - unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE); - unsigned long flags; - - spin_lock_irqsave(&lg->lock, flags); - outl(inl(reg) & ~BIT(hwirq % 32), reg); - spin_unlock_irqrestore(&lg->lock, flags); -} - -static struct irq_chip lp_irqchip = { - .name = "LP-GPIO", - .irq_mask = lp_irq_mask, - .irq_unmask = lp_irq_unmask, - .irq_enable = lp_irq_enable, - .irq_disable = lp_irq_disable, - .irq_set_type = lp_irq_type, - .flags = IRQCHIP_SKIP_SET_WAKE, -}; - -static int lp_gpio_irq_init_hw(struct gpio_chip *chip) -{ - struct lp_gpio *lg = gpiochip_get_data(chip); - unsigned long reg; - unsigned base; - - for (base = 0; base < lg->chip.ngpio; base += 32) { - /* disable gpio pin interrupts */ - reg = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE); - outl(0, reg); - /* Clear interrupt status register */ - reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT); - outl(0xffffffff, reg); - } - - return 0; -} - -static int lp_gpio_probe(struct platform_device *pdev) -{ - struct lp_gpio *lg; - struct gpio_chip *gc; - struct resource *io_rc, *irq_rc; - struct device *dev = &pdev->dev; - unsigned long reg_len; - int ret = -ENODEV; - - lg = devm_kzalloc(dev, sizeof(struct lp_gpio), GFP_KERNEL); - if (!lg) - return -ENOMEM; - - lg->pdev = pdev; - platform_set_drvdata(pdev, lg); - - io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); - irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - - if (!io_rc) { - dev_err(dev, "missing IO resources\n"); - return -EINVAL; - } - - lg->reg_base = io_rc->start; - reg_len = resource_size(io_rc); - - if (!devm_request_region(dev, lg->reg_base, reg_len, "lp-gpio")) { - dev_err(dev, "failed requesting IO region 0x%x\n", - (unsigned int)lg->reg_base); - return -EBUSY; - } - - spin_lock_init(&lg->lock); - - gc = &lg->chip; - gc->label = dev_name(dev); - gc->owner = THIS_MODULE; - gc->request = lp_gpio_request; - gc->free = lp_gpio_free; - gc->direction_input = lp_gpio_direction_input; - gc->direction_output = lp_gpio_direction_output; - gc->get = lp_gpio_get; - gc->set = lp_gpio_set; - gc->base = -1; - gc->ngpio = LP_NUM_GPIO; - gc->can_sleep = false; - gc->parent = dev; - - /* set up interrupts */ - if (irq_rc && irq_rc->start) { - struct gpio_irq_chip *girq; - - girq = &gc->irq; - girq->chip = &lp_irqchip; - girq->init_hw = lp_gpio_irq_init_hw; - girq->parent_handler = lp_gpio_irq_handler; - girq->num_parents = 1; - girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, - sizeof(*girq->parents), - GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; - girq->parents[0] = (unsigned)irq_rc->start; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_bad_irq; - } - - ret = devm_gpiochip_add_data(dev, gc, lg); - if (ret) { - dev_err(dev, "failed adding lp-gpio chip\n"); - return ret; - } - - pm_runtime_enable(dev); - - return 0; -} - -static int lp_gpio_runtime_suspend(struct device *dev) -{ - return 0; -} - -static int lp_gpio_runtime_resume(struct device *dev) -{ - return 0; -} - -static int lp_gpio_resume(struct device *dev) -{ - struct lp_gpio *lg = dev_get_drvdata(dev); - unsigned long reg; - int i; - - /* on some hardware suspend clears input sensing, re-enable it here */ - for (i = 0; i < lg->chip.ngpio; i++) { - if (gpiochip_is_requested(&lg->chip, i) != NULL) { - reg = lp_gpio_reg(&lg->chip, i, LP_CONFIG2); - outl(inl(reg) & ~GPINDIS_BIT, reg); - } - } - return 0; -} - -static const struct dev_pm_ops lp_gpio_pm_ops = { - .runtime_suspend = lp_gpio_runtime_suspend, - .runtime_resume = lp_gpio_runtime_resume, - .resume = lp_gpio_resume, -}; - -static const struct acpi_device_id lynxpoint_gpio_acpi_match[] = { - { "INT33C7", 0 }, - { "INT3437", 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match); - -static int lp_gpio_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - return 0; -} - -static struct platform_driver lp_gpio_driver = { - .probe = lp_gpio_probe, - .remove = lp_gpio_remove, - .driver = { - .name = "lp_gpio", - .pm = &lp_gpio_pm_ops, - .acpi_match_table = ACPI_PTR(lynxpoint_gpio_acpi_match), - }, -}; - -static int __init lp_gpio_init(void) -{ - return platform_driver_register(&lp_gpio_driver); -} - -static void __exit lp_gpio_exit(void) -{ - platform_driver_unregister(&lp_gpio_driver); -} - -subsys_initcall(lp_gpio_init); -module_exit(lp_gpio_exit); - -MODULE_AUTHOR("Mathias Nyman (Intel)"); -MODULE_DESCRIPTION("GPIO interface for Intel Lynxpoint"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:lp_gpio"); diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 94b8d3ae27bc..7d343bea784a 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * GPIO Testing Device Driver * @@ -7,18 +7,18 @@ * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl> */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/gpio/driver.h> +#include <linux/debugfs.h> #include <linux/gpio/consumer.h> -#include <linux/platform_device.h> -#include <linux/slab.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irq_sim.h> -#include <linux/debugfs.h> -#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include <linux/property.h> +#include <linux/slab.h> +#include <linux/uaccess.h> #include "gpiolib.h" diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 5ae30de3490a..604dfec353a1 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -296,6 +296,7 @@ static const struct mpc8xxx_gpio_devtype mpc512x_gpio_devtype = { static const struct mpc8xxx_gpio_devtype ls1028a_gpio_devtype = { .gpio_dir_in_init = ls1028a_gpio_dir_in_init, + .irq_set_type = mpc8xxx_irq_set_type, }; static const struct mpc8xxx_gpio_devtype mpc5125_gpio_devtype = { diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index d1d785f983a7..b992321bb852 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -253,8 +253,7 @@ mediatek_gpio_bank_probe(struct device *dev, /* * Directly request the irq here instead of passing - * a flow-handler to gpiochip_set_chained_irqchip, - * because the irq is shared. + * a flow-handler because the irq is shared. */ ret = devm_request_irq(dev, mtk->gpio_irq, mediatek_gpio_irq_handler, IRQF_SHARED, diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 993bbeb3c006..d2b999c7987f 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -46,7 +46,6 @@ #include <linux/irqdomain.h> #include <linux/mfd/syscon.h> #include <linux/of_device.h> -#include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -432,6 +431,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) u32 mask = d->mask; irq_gc_lock(gc); + mvebu_gpio_write_edge_cause(mvchip, ~mask); ct->mask_cache_priv |= mask; mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); @@ -1102,7 +1102,11 @@ static int mvebu_gpio_probe(struct platform_device *pdev) soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION; /* Some gpio controllers do not provide irq support */ - have_irqs = of_irq_count(np) != 0; + err = platform_irq_count(pdev); + if (err < 0) + return err; + + have_irqs = err != 0; mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 9853547e7276..5638b4e5355f 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -764,8 +764,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, pca953x_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | - IRQF_SHARED, + IRQF_ONESHOT | IRQF_SHARED, dev_name(&client->dev), chip); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", @@ -855,8 +854,6 @@ out: return ret; } -static const struct of_device_id pca953x_dt_ids[]; - static int pca953x_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index b04c561f3280..4d47b2c41186 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -244,7 +244,6 @@ static struct platform_driver sama5d2_piobu_driver = { module_platform_driver(sama5d2_piobu_driver); -MODULE_VERSION("1.0"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver"); MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>"); diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c new file mode 100644 index 000000000000..147a1bd04515 --- /dev/null +++ b/drivers/gpio/gpio-sifive.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 SiFive + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/of_irq.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/regmap.h> + +#define SIFIVE_GPIO_INPUT_VAL 0x00 +#define SIFIVE_GPIO_INPUT_EN 0x04 +#define SIFIVE_GPIO_OUTPUT_EN 0x08 +#define SIFIVE_GPIO_OUTPUT_VAL 0x0C +#define SIFIVE_GPIO_RISE_IE 0x18 +#define SIFIVE_GPIO_RISE_IP 0x1C +#define SIFIVE_GPIO_FALL_IE 0x20 +#define SIFIVE_GPIO_FALL_IP 0x24 +#define SIFIVE_GPIO_HIGH_IE 0x28 +#define SIFIVE_GPIO_HIGH_IP 0x2C +#define SIFIVE_GPIO_LOW_IE 0x30 +#define SIFIVE_GPIO_LOW_IP 0x34 +#define SIFIVE_GPIO_OUTPUT_XOR 0x40 + +#define SIFIVE_GPIO_MAX 32 +#define SIFIVE_GPIO_IRQ_OFFSET 7 + +struct sifive_gpio { + void __iomem *base; + struct gpio_chip gc; + struct regmap *regs; + u32 irq_state; + unsigned int trigger[SIFIVE_GPIO_MAX]; + unsigned int irq_parent[SIFIVE_GPIO_MAX]; +}; + +static void sifive_gpio_set_ie(struct sifive_gpio *chip, unsigned int offset) +{ + unsigned long flags; + unsigned int trigger; + + spin_lock_irqsave(&chip->gc.bgpio_lock, flags); + trigger = (chip->irq_state & BIT(offset)) ? chip->trigger[offset] : 0; + regmap_update_bits(chip->regs, SIFIVE_GPIO_RISE_IE, BIT(offset), + (trigger & IRQ_TYPE_EDGE_RISING) ? BIT(offset) : 0); + regmap_update_bits(chip->regs, SIFIVE_GPIO_FALL_IE, BIT(offset), + (trigger & IRQ_TYPE_EDGE_FALLING) ? BIT(offset) : 0); + regmap_update_bits(chip->regs, SIFIVE_GPIO_HIGH_IE, BIT(offset), + (trigger & IRQ_TYPE_LEVEL_HIGH) ? BIT(offset) : 0); + regmap_update_bits(chip->regs, SIFIVE_GPIO_LOW_IE, BIT(offset), + (trigger & IRQ_TYPE_LEVEL_LOW) ? BIT(offset) : 0); + spin_unlock_irqrestore(&chip->gc.bgpio_lock, flags); +} + +static int sifive_gpio_irq_set_type(struct irq_data *d, unsigned int trigger) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct sifive_gpio *chip = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(d); + + if (offset < 0 || offset >= gc->ngpio) + return -EINVAL; + + chip->trigger[offset] = trigger; + sifive_gpio_set_ie(chip, offset); + return 0; +} + +static void sifive_gpio_irq_enable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct sifive_gpio *chip = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(d) % SIFIVE_GPIO_MAX; + u32 bit = BIT(offset); + unsigned long flags; + + irq_chip_enable_parent(d); + + /* Switch to input */ + gc->direction_input(gc, offset); + + spin_lock_irqsave(&gc->bgpio_lock, flags); + /* Clear any sticky pending interrupts */ + regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit); + regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit); + regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit); + regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit); + spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + /* Enable interrupts */ + assign_bit(offset, (unsigned long *)&chip->irq_state, 1); + sifive_gpio_set_ie(chip, offset); +} + +static void sifive_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct sifive_gpio *chip = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(d) % SIFIVE_GPIO_MAX; + + assign_bit(offset, (unsigned long *)&chip->irq_state, 0); + sifive_gpio_set_ie(chip, offset); + irq_chip_disable_parent(d); +} + +static void sifive_gpio_irq_eoi(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct sifive_gpio *chip = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(d) % SIFIVE_GPIO_MAX; + u32 bit = BIT(offset); + unsigned long flags; + + spin_lock_irqsave(&gc->bgpio_lock, flags); + /* Clear all pending interrupts */ + regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit); + regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit); + regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit); + regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit); + spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + irq_chip_eoi_parent(d); +} + +static struct irq_chip sifive_gpio_irqchip = { + .name = "sifive-gpio", + .irq_set_type = sifive_gpio_irq_set_type, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_enable = sifive_gpio_irq_enable, + .irq_disable = sifive_gpio_irq_disable, + .irq_eoi = sifive_gpio_irq_eoi, +}; + +static int sifive_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) +{ + *parent_type = IRQ_TYPE_NONE; + *parent = child + SIFIVE_GPIO_IRQ_OFFSET; + return 0; +} + +static const struct regmap_config sifive_gpio_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .disable_locking = true, +}; + +static int sifive_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct device_node *irq_parent; + struct irq_domain *parent; + struct gpio_irq_chip *girq; + struct sifive_gpio *chip; + int ret, ngpio; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(chip->base)) { + dev_err(dev, "failed to allocate device memory\n"); + return PTR_ERR(chip->base); + } + + chip->regs = devm_regmap_init_mmio(dev, chip->base, + &sifive_gpio_regmap_config); + if (IS_ERR(chip->regs)) + return PTR_ERR(chip->regs); + + ngpio = of_irq_count(node); + if (ngpio >= SIFIVE_GPIO_MAX) { + dev_err(dev, "Too many GPIO interrupts (max=%d)\n", + SIFIVE_GPIO_MAX); + return -ENXIO; + } + + irq_parent = of_irq_find_parent(node); + if (!irq_parent) { + dev_err(dev, "no IRQ parent node\n"); + return -ENODEV; + } + parent = irq_find_host(irq_parent); + if (!parent) { + dev_err(dev, "no IRQ parent domain\n"); + return -ENODEV; + } + + ret = bgpio_init(&chip->gc, dev, 4, + chip->base + SIFIVE_GPIO_INPUT_VAL, + chip->base + SIFIVE_GPIO_OUTPUT_VAL, + NULL, + chip->base + SIFIVE_GPIO_OUTPUT_EN, + chip->base + SIFIVE_GPIO_INPUT_EN, + 0); + if (ret) { + dev_err(dev, "unable to init generic GPIO\n"); + return ret; + } + + /* Disable all GPIO interrupts before enabling parent interrupts */ + regmap_write(chip->regs, SIFIVE_GPIO_RISE_IE, 0); + regmap_write(chip->regs, SIFIVE_GPIO_FALL_IE, 0); + regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IE, 0); + regmap_write(chip->regs, SIFIVE_GPIO_LOW_IE, 0); + chip->irq_state = 0; + + chip->gc.base = -1; + chip->gc.ngpio = ngpio; + chip->gc.label = dev_name(dev); + chip->gc.parent = dev; + chip->gc.owner = THIS_MODULE; + girq = &chip->gc.irq; + girq->chip = &sifive_gpio_irqchip; + girq->fwnode = of_node_to_fwnode(node); + girq->parent_domain = parent; + girq->child_to_parent_hwirq = sifive_gpio_child_to_parent_hwirq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + + platform_set_drvdata(pdev, chip); + return gpiochip_add_data(&chip->gc, chip); +} + +static const struct of_device_id sifive_gpio_match[] = { + { .compatible = "sifive,gpio0" }, + { .compatible = "sifive,fu540-c000-gpio" }, + { }, +}; + +static struct platform_driver sifive_gpio_driver = { + .probe = sifive_gpio_probe, + .driver = { + .name = "sifive_gpio", + .of_match_table = of_match_ptr(sifive_gpio_match), + }, +}; +builtin_platform_driver(sifive_gpio_driver) diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 5e375186f90e..866201cf5f65 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -243,4 +243,3 @@ static struct platform_driver tb10x_gpio_driver = { module_platform_driver(tb10x_gpio_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("tb10x gpio."); -MODULE_VERSION("0.0.1"); diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 6fdfe4c5303e..acb99eff9939 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -96,12 +96,12 @@ struct tegra_gpio_info { static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, u32 val, u32 reg) { - __raw_writel(val, tgi->regs + reg); + writel_relaxed(val, tgi->regs + reg); } static inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg) { - return __raw_readl(tgi->regs + reg); + return readl_relaxed(tgi->regs + reg); } static unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port, @@ -416,11 +416,8 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) static int tegra_gpio_resume(struct device *dev) { struct tegra_gpio_info *tgi = dev_get_drvdata(dev); - unsigned long flags; unsigned int b, p; - local_irq_save(flags); - for (b = 0; b < tgi->bank_count; b++) { struct tegra_gpio_bank *bank = &tgi->bank_info[b]; @@ -448,17 +445,14 @@ static int tegra_gpio_resume(struct device *dev) } } - local_irq_restore(flags); return 0; } static int tegra_gpio_suspend(struct device *dev) { struct tegra_gpio_info *tgi = dev_get_drvdata(dev); - unsigned long flags; unsigned int b, p; - local_irq_save(flags); for (b = 0; b < tgi->bank_count; b++) { struct tegra_gpio_bank *bank = &tgi->bank_info[b]; @@ -488,7 +482,7 @@ static int tegra_gpio_suspend(struct device *dev) GPIO_INT_ENB(tgi, gpio)); } } - local_irq_restore(flags); + return 0; } @@ -497,6 +491,11 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); unsigned int gpio = d->hwirq; u32 port, bit, mask; + int err; + + err = irq_set_irq_wake(bank->irq, enable); + if (err) + return err; port = GPIO_PORT(gpio); bit = GPIO_BIT(gpio); @@ -507,7 +506,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) else bank->wake_enb[port] &= ~mask; - return irq_set_irq_wake(bank->irq, enable); + return 0; } #endif @@ -557,7 +556,7 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) #endif static const struct dev_pm_ops tegra_gpio_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) }; static int tegra_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 55b43b7ce88d..de241263d4be 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -448,17 +448,24 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain, return 0; } -static void tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { struct tegra_gpio *gpio = gpiochip_get_data(chip); + struct irq_fwspec *fwspec; + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 3; fwspec->param[0] = gpio->soc->instance; fwspec->param[1] = parent_hwirq; fwspec->param[2] = parent_type; + + return fwspec; } static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip, @@ -621,7 +628,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->chip = &gpio->intc; irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; - irq->populate_parent_fwspec = tegra186_gpio_populate_parent_fwspec; + irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec; irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq; irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate; irq->handler = handle_simple_irq; diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 462770479045..9f66deab46ea 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/spinlock.h> +#include <asm-generic/msi.h> #define GPIO_RX_DAT 0x0 @@ -53,7 +54,6 @@ struct thunderx_line { struct thunderx_gpio { struct gpio_chip chip; u8 __iomem *register_base; - struct irq_domain *irqd; struct msix_entry *msix_entries; /* per line MSI-X */ struct thunderx_line *line_entries; /* per line irq info */ raw_spinlock_t lock; @@ -286,54 +286,60 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, } } -static void thunderx_gpio_irq_ack(struct irq_data *data) +static void thunderx_gpio_irq_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask(struct irq_data *data) +static void thunderx_gpio_irq_mask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask_ack(struct irq_data *data) +static void thunderx_gpio_irq_mask_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_unmask(struct irq_data *data) +static void thunderx_gpio_irq_unmask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1S, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static int thunderx_gpio_irq_set_type(struct irq_data *data, +static int thunderx_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); + struct thunderx_line *txline = + &txgpio->line_entries[irqd_to_hwirq(d)]; u64 bit_cfg; - irqd_set_trigger_type(data, flow_type); + irqd_set_trigger_type(d, flow_type); bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN; if (flow_type & IRQ_TYPE_EDGE_BOTH) { - irq_set_handler_locked(data, handle_fasteoi_ack_irq); + irq_set_handler_locked(d, handle_fasteoi_ack_irq); bit_cfg |= GPIO_BIT_CFG_INT_TYPE; } else { - irq_set_handler_locked(data, handle_fasteoi_mask_irq); + irq_set_handler_locked(d, handle_fasteoi_mask_irq); } raw_spin_lock(&txgpio->lock); @@ -362,33 +368,6 @@ static void thunderx_gpio_irq_disable(struct irq_data *data) irq_chip_disable_parent(data); } -static int thunderx_gpio_irq_request_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - int r; - - r = gpiochip_lock_as_irq(&txgpio->chip, txline->line); - if (r) - return r; - - r = irq_chip_request_resources_parent(data); - if (r) - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); - - return r; -} - -static void thunderx_gpio_irq_release_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - - irq_chip_release_resources_parent(data); - - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); -} - /* * Interrupts are chained from underlying MSI-X vectors. We have * these irq_chip functions to be able to handle level triggering @@ -405,48 +384,42 @@ static struct irq_chip thunderx_gpio_irq_chip = { .irq_unmask = thunderx_gpio_irq_unmask, .irq_eoi = irq_chip_eoi_parent, .irq_set_affinity = irq_chip_set_affinity_parent, - .irq_request_resources = thunderx_gpio_irq_request_resources, - .irq_release_resources = thunderx_gpio_irq_release_resources, .irq_set_type = thunderx_gpio_irq_set_type, .flags = IRQCHIP_SET_TYPE_MASKED }; -static int thunderx_gpio_irq_translate(struct irq_domain *d, - struct irq_fwspec *fwspec, - irq_hw_number_t *hwirq, - unsigned int *type) +static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) { - struct thunderx_gpio *txgpio = d->host_data; + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); + struct irq_data *irqd; + unsigned int irq; - if (WARN_ON(fwspec->param_count < 2)) + irq = txgpio->msix_entries[child].vector; + irqd = irq_domain_get_irq_data(gc->irq.parent_domain, irq); + if (!irqd) return -EINVAL; - if (fwspec->param[0] >= txgpio->chip.ngpio) - return -EINVAL; - *hwirq = fwspec->param[0]; - *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + *parent = irqd_to_hwirq(irqd); + *parent_type = IRQ_TYPE_LEVEL_HIGH; return 0; } -static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq, - unsigned int nr_irqs, void *arg) +static void *thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip, + unsigned int parent_hwirq, + unsigned int parent_type) { - struct thunderx_line *txline = arg; - - return irq_domain_set_hwirq_and_chip(d, virq, txline->line, - &thunderx_gpio_irq_chip, txline); -} - -static const struct irq_domain_ops thunderx_gpio_irqd_ops = { - .alloc = thunderx_gpio_irq_alloc, - .translate = thunderx_gpio_irq_translate -}; + msi_alloc_info_t *info; -static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) -{ - struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; - return irq_find_mapping(txgpio->irqd, offset); + info->hwirq = parent_hwirq; + return info; } static int thunderx_gpio_probe(struct pci_dev *pdev, @@ -456,6 +429,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, struct device *dev = &pdev->dev; struct thunderx_gpio *txgpio; struct gpio_chip *chip; + struct gpio_irq_chip *girq; int ngpio, i; int err = 0; @@ -500,8 +474,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, } txgpio->msix_entries = devm_kcalloc(dev, - ngpio, sizeof(struct msix_entry), - GFP_KERNEL); + ngpio, sizeof(struct msix_entry), + GFP_KERNEL); if (!txgpio->msix_entries) { err = -ENOMEM; goto out; @@ -542,27 +516,6 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, if (err < 0) goto out; - /* - * Push GPIO specific irqdomain on hierarchy created as a side - * effect of the pci_enable_msix() - */ - txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, - 0, 0, of_node_to_fwnode(dev->of_node), - &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) { - err = -ENOMEM; - goto out; - } - - /* Push on irq_data and the domain for each line. */ - for (i = 0; i < ngpio; i++) { - err = irq_domain_push_irq(txgpio->irqd, - txgpio->msix_entries[i].vector, - &txgpio->line_entries[i]); - if (err < 0) - dev_err(dev, "irq_domain_push_irq: %d\n", err); - } - chip->label = KBUILD_MODNAME; chip->parent = dev; chip->owner = THIS_MODULE; @@ -577,11 +530,35 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, chip->set = thunderx_gpio_set; chip->set_multiple = thunderx_gpio_set_multiple; chip->set_config = thunderx_gpio_set_config; - chip->to_irq = thunderx_gpio_to_irq; + girq = &chip->irq; + girq->chip = &thunderx_gpio_irq_chip; + girq->fwnode = of_node_to_fwnode(dev->of_node); + girq->parent_domain = + irq_get_irq_data(txgpio->msix_entries[0].vector)->domain; + girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq; + girq->populate_parent_alloc_arg = thunderx_gpio_populate_parent_alloc_info; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + err = devm_gpiochip_add_data(dev, chip, txgpio); if (err) goto out; + /* Push on irq_data and the domain for each line. */ + for (i = 0; i < ngpio; i++) { + struct irq_fwspec fwspec; + + fwspec.fwnode = of_node_to_fwnode(dev->of_node); + fwspec.param_count = 2; + fwspec.param[0] = i; + fwspec.param[1] = IRQ_TYPE_NONE; + err = irq_domain_push_irq(girq->domain, + txgpio->msix_entries[i].vector, + &fwspec); + if (err < 0) + dev_err(dev, "irq_domain_push_irq: %d\n", err); + } + dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", ngpio, chip->base); return 0; @@ -596,10 +573,10 @@ static void thunderx_gpio_remove(struct pci_dev *pdev) struct thunderx_gpio *txgpio = pci_get_drvdata(pdev); for (i = 0; i < txgpio->chip.ngpio; i++) - irq_domain_pop_irq(txgpio->irqd, + irq_domain_pop_irq(txgpio->chip.irq.domain, txgpio->msix_entries[i].vector); - irq_domain_remove(txgpio->irqd); + irq_domain_remove(txgpio->chip.irq.domain); pci_set_drvdata(pdev, NULL); } diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 4ff146ca32fe..3bf397b8dfbc 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -71,7 +71,7 @@ static inline u_int32_t gpio_o_bit(int i) return 1 << (i + 13); } -/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering: +/* Mapping between numeric GPIO ID and the actual GPIO hardware numbering: * 0..13 GPI 0..13 * 14..26 GPO 0..12 * 27..41 GPIO 0..14 diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c new file mode 100644 index 000000000000..74913f2e5697 --- /dev/null +++ b/drivers/gpio/gpio-wcd934x.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include <linux/module.h> +#include <linux/gpio/driver.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/of_device.h> + +#define WCD_PIN_MASK(p) BIT(p - 1) +#define WCD_REG_DIR_CTL_OFFSET 0x42 +#define WCD_REG_VAL_CTL_OFFSET 0x43 +#define WCD934X_NPINS 5 + +struct wcd_gpio_data { + struct regmap *map; + struct gpio_chip chip; +}; + +static int wcd_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + unsigned int value; + int ret; + + ret = regmap_read(data->map, WCD_REG_DIR_CTL_OFFSET, &value); + if (ret < 0) + return ret; + + if (value & WCD_PIN_MASK(pin)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + + return regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, + WCD_PIN_MASK(pin), 0); +} + +static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, + int val) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + + regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, + WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); + + return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, + WCD_PIN_MASK(pin), + val ? WCD_PIN_MASK(pin) : 0); +} + +static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_data *data = gpiochip_get_data(chip); + int value; + + regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value); + + return !!(value && WCD_PIN_MASK(pin)); +} + +static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) +{ + wcd_gpio_direction_output(chip, pin, val); +} + +static int wcd_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd_gpio_data *data; + struct gpio_chip *chip; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->map = dev_get_regmap(dev->parent, NULL); + if (!data->map) { + dev_err(dev, "%s: failed to get regmap\n", __func__); + return -EINVAL; + } + + chip = &data->chip; + chip->direction_input = wcd_gpio_direction_input; + chip->direction_output = wcd_gpio_direction_output; + chip->get_direction = wcd_gpio_get_direction; + chip->get = wcd_gpio_get; + chip->set = wcd_gpio_set; + chip->parent = dev; + chip->base = -1; + chip->ngpio = WCD934X_NPINS; + chip->label = dev_name(dev); + chip->of_gpio_n_cells = 2; + chip->can_sleep = false; + + return devm_gpiochip_add_data(dev, chip, data); +} + +static const struct of_device_id wcd_gpio_of_match[] = { + { .compatible = "qcom,wcd9340-gpio" }, + { .compatible = "qcom,wcd9341-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd_gpio_of_match); + +static struct platform_driver wcd_gpio_driver = { + .driver = { + .name = "wcd934x-gpio", + .of_match_table = wcd_gpio_of_match, + }, + .probe = wcd_gpio_probe, +}; + +module_platform_driver(wcd_gpio_driver); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c index b21c2e436b61..ad5489a65d54 100644 --- a/drivers/gpio/gpio-xgs-iproc.c +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -251,8 +251,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) /* * Directly request the irq here instead of passing - * a flow-handler to gpiochip_set_chained_irqchip, - * because the irq is shared. + * a flow-handler because the irq is shared. */ ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler, IRQF_SHARED, chip->gc.label, &chip->gc); diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index fbf6b1a0a4fa..23e3d335cd54 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -762,10 +762,9 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) parent = &gdev->dev; /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, parent, - MKDEV(0, 0), - chip, gpiochip_groups, - "gpiochip%d", chip->base); + dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip, + gpiochip_groups, GPIOCHIP_NAME "%d", + chip->base); if (IS_ERR(dev)) return PTR_ERR(dev); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bcfbfded9ba3..99ac27a72e28 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -140,7 +140,7 @@ EXPORT_SYMBOL_GPL(gpio_to_desc); * in the given chip for the specified hardware number. */ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, - u16 hwnum) + unsigned int hwnum) { struct gpio_device *gdev = chip->gpiodev; @@ -232,15 +232,15 @@ int gpiod_get_direction(struct gpio_desc *desc) return -ENOTSUPP; ret = chip->get_direction(chip, offset); - if (ret > 0) { - /* GPIOF_DIR_IN, or other positive */ + if (ret < 0) + return ret; + + /* GPIOF_DIR_IN or other positive, otherwise GPIOF_DIR_OUT */ + if (ret > 0) ret = 1; - clear_bit(FLAG_IS_OUT, &desc->flags); - } - if (ret == 0) { - /* GPIOF_DIR_OUT */ - set_bit(FLAG_IS_OUT, &desc->flags); - } + + assign_bit(FLAG_IS_OUT, &desc->flags, !ret); + return ret; } EXPORT_SYMBOL_GPL(gpiod_get_direction); @@ -492,15 +492,6 @@ static int linehandle_validate_flags(u32 flags) return 0; } -static void linehandle_configure_flag(unsigned long *flagsp, - u32 bit, bool active) -{ - if (active) - set_bit(bit, flagsp); - else - clear_bit(bit, flagsp); -} - static long linehandle_set_config(struct linehandle_state *lh, void __user *ip) { @@ -522,22 +513,22 @@ static long linehandle_set_config(struct linehandle_state *lh, desc = lh->descs[i]; flagsp = &desc->flags; - linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW, + assign_bit(FLAG_ACTIVE_LOW, flagsp, lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); - linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN, + assign_bit(FLAG_OPEN_DRAIN, flagsp, lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); - linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE, + assign_bit(FLAG_OPEN_SOURCE, flagsp, lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); - linehandle_configure_flag(flagsp, FLAG_PULL_UP, + assign_bit(FLAG_PULL_UP, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); - linehandle_configure_flag(flagsp, FLAG_PULL_DOWN, + assign_bit(FLAG_PULL_DOWN, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); - linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE, + assign_bit(FLAG_BIAS_DISABLE, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); /* @@ -686,14 +677,13 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < handlereq.lines; i++) { u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc; + struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); - if (offset >= gdev->ngpio) { - ret = -EINVAL; + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); goto out_free_descs; } - desc = &gdev->descs[offset]; ret = gpiod_request(desc, lh->label); if (ret) goto out_free_descs; @@ -1018,8 +1008,9 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) lflags = eventreq.handleflags; eflags = eventreq.eventflags; - if (offset >= gdev->ngpio) - return -EINVAL; + desc = gpiochip_get_desc(gdev->chip, offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); /* Return an error if a unknown flag is set */ if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || @@ -1057,7 +1048,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) } } - desc = &gdev->descs[offset]; ret = gpiod_request(desc, le->label); if (ret) goto out_free_label; @@ -1184,10 +1174,11 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; - if (lineinfo.line_offset >= gdev->ngpio) - return -EINVAL; - desc = &gdev->descs[lineinfo.line_offset]; + desc = gpiochip_get_desc(chip, lineinfo.line_offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + if (desc->name) { strncpy(lineinfo.name, desc->name, sizeof(lineinfo.name)); @@ -1427,7 +1418,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ret = gdev->id; goto err_free_gdev; } - dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); + dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); device_initialize(&gdev->dev); dev_set_drvdata(&gdev->dev, gdev); if (chip->parent && chip->parent->driver) @@ -1452,7 +1443,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, if (chip->ngpio > FASTPATH_NGPIO) chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n", - chip->ngpio, FASTPATH_NGPIO); + chip->ngpio, FASTPATH_NGPIO); gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { @@ -1495,11 +1486,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, goto err_free_label; } - spin_unlock_irqrestore(&gpio_lock, flags); - for (i = 0; i < chip->ngpio; i++) gdev->descs[i].gdev = gdev; + spin_unlock_irqrestore(&gpio_lock, flags); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -1524,15 +1515,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, struct gpio_desc *desc = &gdev->descs[i]; if (chip->get_direction && gpiochip_line_is_valid(chip, i)) { - if (!chip->get_direction(chip, i)) - set_bit(FLAG_IS_OUT, &desc->flags); - else - clear_bit(FLAG_IS_OUT, &desc->flags); + assign_bit(FLAG_IS_OUT, + &desc->flags, !chip->get_direction(chip, i)); } else { - if (!chip->direction_input) - set_bit(FLAG_IS_OUT, &desc->flags); - else - clear_bit(FLAG_IS_OUT, &desc->flags); + assign_bit(FLAG_IS_OUT, + &desc->flags, !chip->direction_input); } } @@ -1813,7 +1800,7 @@ EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip * @gc: the gpiochip to set the irqchip chain to * @parent_irq: the irq number corresponding to the parent IRQ for this - * chained irqchip + * cascaded 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 @@ -1856,29 +1843,6 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, } /** - * gpiochip_set_chained_irqchip() - connects a chained 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 - * coming out of the gpiochip. - */ -void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, - struct irq_chip *irqchip, - unsigned int parent_irq, - irq_flow_handler_t parent_handler) -{ - if (gpiochip->irq.threaded) { - chip_err(gpiochip, "tried to chain a threaded gpiochip\n"); - return; - } - - gpiochip_set_cascaded_irqchip(gpiochip, parent_irq, parent_handler); -} -EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip); - -/** * gpiochip_set_nested_irqchip() - connects a nested irqchip to a gpiochip * @gpiochip: the gpiochip to set the irqchip nested handler to * @irqchip: the irqchip to nest to the gpiochip @@ -2003,7 +1967,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; struct irq_fwspec *fwspec = data; - struct irq_fwspec parent_fwspec; + void *parent_arg; unsigned int parent_hwirq; unsigned int parent_type; struct gpio_irq_chip *girq = &gc->irq; @@ -2019,7 +1983,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (ret) return ret; - chip_info(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); + chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); ret = girq->child_to_parent_hwirq(gc, hwirq, type, &parent_hwirq, &parent_type); @@ -2027,7 +1991,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, chip_err(gc, "can't look up hwirq %lu\n", hwirq); return ret; } - chip_info(gc, "found parent hwirq %u\n", parent_hwirq); + chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq); /* * We set handle_bad_irq because the .set_type() should @@ -2042,23 +2006,27 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, NULL, NULL); irq_set_probe(irq); - /* - * Create a IRQ fwspec to send up to the parent irqdomain: - * specify the hwirq we address on the parent and tie it - * all together up the chain. - */ - parent_fwspec.fwnode = d->parent->fwnode; /* This parent only handles asserted level IRQs */ - girq->populate_parent_fwspec(gc, &parent_fwspec, parent_hwirq, - parent_type); - chip_info(gc, "alloc_irqs_parent for %d parent hwirq %d\n", + parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type); + if (!parent_arg) + return -ENOMEM; + + chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", irq, parent_hwirq); - ret = irq_domain_alloc_irqs_parent(d, irq, 1, &parent_fwspec); + irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key); + ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg); + /* + * If the parent irqdomain is msi, the interrupts have already + * been allocated, so the EEXIST is good. + */ + if (irq_domain_is_msi(d->parent) && (ret == -EEXIST)) + ret = 0; if (ret) chip_err(gc, "failed to allocate parent hwirq %d for hwirq %lu\n", parent_hwirq, hwirq); + kfree(parent_arg); return ret; } @@ -2095,8 +2063,8 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) if (!gc->irq.child_offset_to_irq) gc->irq.child_offset_to_irq = gpiochip_child_offset_to_irq_noop; - if (!gc->irq.populate_parent_fwspec) - gc->irq.populate_parent_fwspec = + if (!gc->irq.populate_parent_alloc_arg) + gc->irq.populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops); @@ -2122,27 +2090,43 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return !!gc->irq.parent_domain; } -void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 2; fwspec->param[0] = parent_hwirq; fwspec->param[1] = parent_type; + + return fwspec; } EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell); -void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 4; fwspec->param[0] = 0; fwspec->param[1] = parent_hwirq; fwspec->param[2] = 0; fwspec->param[3] = parent_type; + + return fwspec; } EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell); @@ -2998,7 +2982,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * A pointer to the GPIO descriptor, or an ERR_PTR()-encoded negative error * code on failure. */ -struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, +struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, + unsigned int hwnum, const char *label, enum gpio_lookup_flags lflags, enum gpiod_flags dflags) @@ -3050,25 +3035,13 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); * rely on gpio_request() having been called beforehand. */ -static int gpio_set_config(struct gpio_chip *gc, unsigned offset, +static int gpio_set_config(struct gpio_chip *gc, unsigned int offset, enum pin_config_param mode) { - unsigned long config; - unsigned arg; - - switch (mode) { - case PIN_CONFIG_BIAS_DISABLE: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_UP: - arg = 1; - break; - - default: - arg = 0; - } + if (!gc->set_config) + return -ENOTSUPP; - config = PIN_CONF_PACKED(mode, arg); - return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; + return gc->set_config(gc, offset, mode); } static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc) @@ -3302,15 +3275,9 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) VALIDATE_DESC(desc); chip = desc->gdev->chip; - if (!chip->set || !chip->set_config) { - gpiod_dbg(desc, - "%s: missing set() or set_config() operations\n", - __func__); - return -ENOTSUPP; - } config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); - return chip->set_config(chip, gpio_chip_hwgpio(desc), config); + return gpio_set_config(chip, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); @@ -3334,10 +3301,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for * persistence state. */ - if (transitory) - set_bit(FLAG_TRANSITORY, &desc->flags); - else - clear_bit(FLAG_TRANSITORY, &desc->flags); + assign_bit(FLAG_TRANSITORY, &desc->flags, transitory); /* If the driver supports it, set the persistence state now */ chip = desc->gdev->chip; @@ -3347,7 +3311,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, !transitory); gpio = gpio_chip_hwgpio(desc); - rc = chip->set_config(chip, gpio, packed); + rc = gpio_set_config(chip, gpio, packed); if (rc == -ENOTSUPP) { dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", gpio); @@ -3804,10 +3768,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, gpio_set_open_source_value_commit(desc, value); } else { __set_bit(hwgpio, mask); - if (value) - __set_bit(hwgpio, bits); - else - __clear_bit(hwgpio, bits); + __assign_bit(hwgpio, bits, value); count++; } i++; @@ -5124,7 +5085,7 @@ static int __init gpiolib_dev_init(void) return ret; } - ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip"); + ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME); if (ret < 0) { pr_err("gpiolib: failed to allocate char dev region\n"); bus_unregister(&gpio_bus_type); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index ca9bc1e4803c..3e0aab2945d8 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -16,6 +16,8 @@ #include <linux/module.h> #include <linux/cdev.h> +#define GPIOCHIP_NAME "gpiochip" + /** * struct gpio_device - internal state container for GPIO devices * @id: numerical ID number for the GPIO chip @@ -78,7 +80,8 @@ struct gpio_array { unsigned long invert_mask[]; }; -struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); +struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, + unsigned int hwnum); int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, |