diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 27 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-brcmstb.c | 21 | ||||
-rw-r--r-- | drivers/gpio/gpio-cros-ec.c | 8 | ||||
-rw-r--r-- | drivers/gpio/gpio-crystalcove.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-graniterapids.c | 383 | ||||
-rw-r--r-- | drivers/gpio/gpio-lpc32xx.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-npcm-sgpio.c | 10 | ||||
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-pcie-idio-24.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-regmap.c | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-sch.c | 35 | ||||
-rw-r--r-- | drivers/gpio/gpio-tangier.c | 9 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra186.c | 20 | ||||
-rw-r--r-- | drivers/gpio/gpio-wcove.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-acpi.c | 65 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-cdev.c | 76 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-legacy.c | 49 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 23 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-swnode.c | 44 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 117 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.h | 19 |
23 files changed, 734 insertions, 188 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b50d0b470849..3dbddec07028 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -103,6 +103,15 @@ config GPIO_REGMAP select REGMAP tristate +config GPIO_SWNODE_UNDEFINED + bool + help + This adds a special place holder for software nodes to contain an + undefined GPIO reference, this is primarily used by SPI to allow a + list of GPIO chip selects to mark a certain chip select as being + controlled the SPI device's internal chip select mechanism and not + a GPIO. + # put drivers in the right section, in alphabetical order # This symbol is selected by both I2C and SPI expanders @@ -312,6 +321,24 @@ config GPIO_GENERIC_PLATFORM help Say yes here to support basic platform_device memory-mapped GPIO controllers. +config GPIO_GRANITERAPIDS + tristate "Intel Granite Rapids-D vGPIO support" + depends on X86 || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + Select this to enable virtual GPIO support on platforms with the + following SoCs: + + - Intel Granite Rapids-D + + The driver enables basic GPIO functionality and implements interrupt + support. The virtual GPIO driver controls GPIO lines via a firmware + interface. The physical GPIO pins reside on device that is external + from the main SoC package, such as a BMC or a CPLD. + + To compile this driver as a module, choose M here: the module will + be called gpio-graniterapids. + config GPIO_GRGPIO tristate "Aeroflex Gaisler GRGPIO support" depends on OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fdd28c58d890..e2a53013780e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o obj-$(CONFIG_GPIO_FXL6408) += gpio-fxl6408.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o +obj-$(CONFIG_GPIO_GRANITERAPIDS) += gpio-graniterapids.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index a789af4a5c85..8dce78ea7139 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -50,7 +50,6 @@ struct brcmstb_gpio_priv { struct irq_domain *irq_domain; struct irq_chip irq_chip; int parent_irq; - int gpio_base; int num_gpios; int parent_wake_irq; }; @@ -92,7 +91,7 @@ brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank) static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct brcmstb_gpio_bank *bank) { - return hwirq - (bank->gc.base - bank->parent_priv->gpio_base); + return hwirq - bank->gc.offset; } static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, @@ -118,7 +117,7 @@ static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); /* gc_offset is relative to this gpio_chip; want real offset */ - int hwirq = offset + (gc->base - priv->gpio_base); + int hwirq = offset + gc->offset; if (hwirq >= priv->num_gpios) return -ENXIO; @@ -263,7 +262,7 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) { struct brcmstb_gpio_priv *priv = bank->parent_priv; struct irq_domain *domain = priv->irq_domain; - int hwbase = bank->gc.base - priv->gpio_base; + int hwbase = bank->gc.offset; unsigned long status; while ((status = brcmstb_gpio_get_active_irqs(bank))) { @@ -412,7 +411,7 @@ static int brcmstb_gpio_of_xlate(struct gpio_chip *gc, if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) return -EINVAL; - offset = gpiospec->args[0] - (gc->base - priv->gpio_base); + offset = gpiospec->args[0] - bank->gc.offset; if (offset >= gc->ngpio || offset < 0) return -EINVAL; @@ -596,8 +595,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) const __be32 *p; u32 bank_width; int num_banks = 0; + int num_gpios = 0; int err; - static int gpio_base; unsigned long flags = 0; bool need_wakeup_event = false; @@ -611,7 +610,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) if (IS_ERR(reg_base)) return PTR_ERR(reg_base); - priv->gpio_base = gpio_base; priv->reg_base = reg_base; priv->pdev = pdev; @@ -651,7 +649,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) dev_dbg(dev, "Width 0 found: Empty bank @ %d\n", num_banks); num_banks++; - gpio_base += MAX_GPIO_PER_BANK; + num_gpios += MAX_GPIO_PER_BANK; continue; } @@ -691,12 +689,13 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) err = -ENOMEM; goto fail; } - gc->base = gpio_base; gc->of_gpio_n_cells = 2; gc->of_xlate = brcmstb_gpio_of_xlate; /* not all ngpio lines are valid, will use bank width later */ gc->ngpio = MAX_GPIO_PER_BANK; gc->offset = bank->id * MAX_GPIO_PER_BANK; + gc->request = gpiochip_generic_request; + gc->free = gpiochip_generic_free; if (priv->parent_irq > 0) gc->to_irq = brcmstb_gpio_to_irq; @@ -713,7 +712,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) bank->id); goto fail; } - gpio_base += gc->ngpio; + num_gpios += gc->ngpio; dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, gc->base, gc->ngpio, bank->width); @@ -724,7 +723,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) num_banks++; } - priv->num_gpios = gpio_base - priv->gpio_base; + priv->num_gpios = num_gpios; if (priv->parent_irq > 0) { err = brcmstb_gpio_irq_setup(pdev, priv); if (err) diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c index 842e1c060414..0c09bb54dc0c 100644 --- a/drivers/gpio/gpio-cros-ec.c +++ b/drivers/gpio/gpio-cros-ec.c @@ -12,6 +12,7 @@ #include <linux/errno.h> #include <linux/gpio/driver.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> @@ -197,11 +198,18 @@ static int cros_ec_gpio_probe(struct platform_device *pdev) return devm_gpiochip_add_data(dev, gc, cros_ec); } +static const struct platform_device_id cros_ec_gpio_id[] = { + { "cros-ec-gpio", 0 }, + {} +}; +MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id); + static struct platform_driver cros_ec_gpio_driver = { .probe = cros_ec_gpio_probe, .driver = { .name = "cros-ec-gpio", }, + .id_table = cros_ec_gpio_id, }; module_platform_driver(cros_ec_gpio_driver); diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 1ee62cd58582..25db014494a4 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -92,7 +92,7 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type) case 0x5e: return GPIOPANELCTL; default: - return -EOPNOTSUPP; + return -ENOTSUPP; } } diff --git a/drivers/gpio/gpio-graniterapids.c b/drivers/gpio/gpio-graniterapids.c new file mode 100644 index 000000000000..c693fe05d50f --- /dev/null +++ b/drivers/gpio/gpio-graniterapids.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Granite Rapids-D vGPIO driver + * + * Copyright (c) 2024, Intel Corporation. + * + * Author: Aapo Vienamo <aapo.vienamo@linux.intel.com> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gfp_types.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/math.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <linux/gpio/driver.h> + +#define GNR_NUM_PINS 128 +#define GNR_PINS_PER_REG 32 +#define GNR_NUM_REGS DIV_ROUND_UP(GNR_NUM_PINS, GNR_PINS_PER_REG) + +#define GNR_CFG_BAR 0x00 +#define GNR_CFG_LOCK_OFFSET 0x04 +#define GNR_GPI_STATUS_OFFSET 0x20 +#define GNR_GPI_ENABLE_OFFSET 0x24 + +#define GNR_CFG_DW_RX_MASK GENMASK(25, 22) +#define GNR_CFG_DW_RX_DISABLE FIELD_PREP(GNR_CFG_DW_RX_MASK, 2) +#define GNR_CFG_DW_RX_EDGE FIELD_PREP(GNR_CFG_DW_RX_MASK, 1) +#define GNR_CFG_DW_RX_LEVEL FIELD_PREP(GNR_CFG_DW_RX_MASK, 0) +#define GNR_CFG_DW_RXDIS BIT(4) +#define GNR_CFG_DW_TXDIS BIT(3) +#define GNR_CFG_DW_RXSTATE BIT(1) +#define GNR_CFG_DW_TXSTATE BIT(0) + +/** + * struct gnr_gpio - Intel Granite Rapids-D vGPIO driver state + * @gc: GPIO controller interface + * @reg_base: base address of the GPIO registers + * @ro_bitmap: bitmap of read-only pins + * @lock: guard the registers + * @pad_backup: backup of the register state for suspend + */ +struct gnr_gpio { + struct gpio_chip gc; + void __iomem *reg_base; + DECLARE_BITMAP(ro_bitmap, GNR_NUM_PINS); + raw_spinlock_t lock; + u32 pad_backup[]; +}; + +static void __iomem *gnr_gpio_get_padcfg_addr(const struct gnr_gpio *priv, + unsigned int gpio) +{ + return priv->reg_base + gpio * sizeof(u32); +} + +static int gnr_gpio_configure_line(struct gpio_chip *gc, unsigned int gpio, + u32 clear_mask, u32 set_mask) +{ + struct gnr_gpio *priv = gpiochip_get_data(gc); + void __iomem *addr = gnr_gpio_get_padcfg_addr(priv, gpio); + u32 dw; + + if (test_bit(gpio, priv->ro_bitmap)) + return -EACCES; + + guard(raw_spinlock_irqsave)(&priv->lock); + + dw = readl(addr); + dw &= ~clear_mask; + dw |= set_mask; + writel(dw, addr); + + return 0; +} + +static int gnr_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + const struct gnr_gpio *priv = gpiochip_get_data(gc); + u32 dw; + + dw = readl(gnr_gpio_get_padcfg_addr(priv, gpio)); + + return !!(dw & GNR_CFG_DW_RXSTATE); +} + +static void gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +{ + u32 clear = 0; + u32 set = 0; + + if (value) + set = GNR_CFG_DW_TXSTATE; + else + clear = GNR_CFG_DW_TXSTATE; + + gnr_gpio_configure_line(gc, gpio, clear, set); +} + +static int gnr_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct gnr_gpio *priv = gpiochip_get_data(gc); + u32 dw; + + dw = readl(gnr_gpio_get_padcfg_addr(priv, gpio)); + + if (dw & GNR_CFG_DW_TXDIS) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int gnr_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio) +{ + return gnr_gpio_configure_line(gc, gpio, GNR_CFG_DW_RXDIS, 0); +} + +static int gnr_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, int value) +{ + u32 clear = GNR_CFG_DW_TXDIS; + u32 set = value ? GNR_CFG_DW_TXSTATE : 0; + + return gnr_gpio_configure_line(gc, gpio, clear, set); +} + +static const struct gpio_chip gnr_gpio_chip = { + .owner = THIS_MODULE, + .get = gnr_gpio_get, + .set = gnr_gpio_set, + .get_direction = gnr_gpio_get_direction, + .direction_input = gnr_gpio_direction_input, + .direction_output = gnr_gpio_direction_output, +}; + +static void __iomem *gnr_gpio_get_reg_addr(const struct gnr_gpio *priv, + unsigned int base, + unsigned int gpio) +{ + return priv->reg_base + base + gpio * sizeof(u32); +} + +static void gnr_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gnr_gpio *priv = gpiochip_get_data(gc); + irq_hw_number_t gpio = irqd_to_hwirq(d); + unsigned int reg_idx = gpio / GNR_PINS_PER_REG; + unsigned int bit_idx = gpio % GNR_PINS_PER_REG; + void __iomem *addr = gnr_gpio_get_reg_addr(priv, GNR_GPI_STATUS_OFFSET, reg_idx); + u32 reg; + + guard(raw_spinlock_irqsave)(&priv->lock); + + reg = readl(addr); + reg &= ~BIT(bit_idx); + writel(reg, addr); +} + +static void gnr_gpio_irq_mask_unmask(struct gpio_chip *gc, unsigned long gpio, bool mask) +{ + struct gnr_gpio *priv = gpiochip_get_data(gc); + unsigned int reg_idx = gpio / GNR_PINS_PER_REG; + unsigned int bit_idx = gpio % GNR_PINS_PER_REG; + void __iomem *addr = gnr_gpio_get_reg_addr(priv, GNR_GPI_ENABLE_OFFSET, reg_idx); + u32 reg; + + guard(raw_spinlock_irqsave)(&priv->lock); + + reg = readl(addr); + if (mask) + reg &= ~BIT(bit_idx); + else + reg |= BIT(bit_idx); + writel(reg, addr); +} + +static void gnr_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gnr_gpio_irq_mask_unmask(gc, hwirq, true); + gpiochip_disable_irq(gc, hwirq); +} + +static void gnr_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + gnr_gpio_irq_mask_unmask(gc, hwirq, false); +} + +static int gnr_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t pin = irqd_to_hwirq(d); + u32 mask = GNR_CFG_DW_RX_MASK; + u32 set; + + /* Falling edge and level low triggers not supported by the GPIO controller */ + switch (type) { + case IRQ_TYPE_NONE: + set = GNR_CFG_DW_RX_DISABLE; + break; + case IRQ_TYPE_EDGE_RISING: + set = GNR_CFG_DW_RX_EDGE; + irq_set_handler_locked(d, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + set = GNR_CFG_DW_RX_LEVEL; + irq_set_handler_locked(d, handle_level_irq); + break; + default: + return -EINVAL; + } + + return gnr_gpio_configure_line(gc, pin, mask, set); +} + +static const struct irq_chip gnr_gpio_irq_chip = { + .irq_ack = gnr_gpio_irq_ack, + .irq_mask = gnr_gpio_irq_mask, + .irq_unmask = gnr_gpio_irq_unmask, + .irq_set_type = gnr_gpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void gnr_gpio_init_pin_ro_bits(struct device *dev, + const void __iomem *cfg_lock_base, + unsigned long *ro_bitmap) +{ + u32 tmp[GNR_NUM_REGS]; + + memcpy_fromio(tmp, cfg_lock_base, sizeof(tmp)); + bitmap_from_arr32(ro_bitmap, tmp, GNR_NUM_PINS); +} + +static irqreturn_t gnr_gpio_irq(int irq, void *data) +{ + struct gnr_gpio *priv = data; + unsigned int handled = 0; + + for (unsigned int i = 0; i < GNR_NUM_REGS; i++) { + const void __iomem *reg = priv->reg_base + i * sizeof(u32); + unsigned long pending; + unsigned long enabled; + unsigned int bit_idx; + + scoped_guard(raw_spinlock, &priv->lock) { + pending = readl(reg + GNR_GPI_STATUS_OFFSET); + enabled = readl(reg + GNR_GPI_ENABLE_OFFSET); + } + + /* Only enabled interrupts */ + pending &= enabled; + + for_each_set_bit(bit_idx, &pending, GNR_PINS_PER_REG) { + unsigned int hwirq = i * GNR_PINS_PER_REG + bit_idx; + + generic_handle_domain_irq(priv->gc.irq.domain, hwirq); + } + + handled += pending ? 1 : 0; + + } + return IRQ_RETVAL(handled); +} + +static int gnr_gpio_probe(struct platform_device *pdev) +{ + size_t num_backup_pins = IS_ENABLED(CONFIG_PM_SLEEP) ? GNR_NUM_PINS : 0; + struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; + struct gnr_gpio *priv; + void __iomem *regs; + int irq, ret; + + priv = devm_kzalloc(dev, struct_size(priv, pad_backup, num_backup_pins), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, gnr_gpio_irq, IRQF_SHARED | IRQF_NO_THREAD, + dev_name(dev), priv); + if (ret) + return dev_err_probe(dev, ret, "failed to request interrupt\n"); + + priv->reg_base = regs + readl(regs + GNR_CFG_BAR); + + gnr_gpio_init_pin_ro_bits(dev, priv->reg_base + GNR_CFG_LOCK_OFFSET, + priv->ro_bitmap); + + priv->gc = gnr_gpio_chip; + priv->gc.label = dev_name(dev); + priv->gc.parent = dev; + priv->gc.ngpio = GNR_NUM_PINS; + priv->gc.base = -1; + + girq = &priv->gc.irq; + gpio_irq_chip_set_chip(girq, &gnr_gpio_irq_chip); + girq->chip->name = dev_name(dev); + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + + platform_set_drvdata(pdev, priv); + + return devm_gpiochip_add_data(dev, &priv->gc, priv); +} + +static int gnr_gpio_suspend(struct device *dev) +{ + struct gnr_gpio *priv = dev_get_drvdata(dev); + unsigned int i; + + guard(raw_spinlock_irqsave)(&priv->lock); + + for_each_clear_bit(i, priv->ro_bitmap, priv->gc.ngpio) + priv->pad_backup[i] = readl(gnr_gpio_get_padcfg_addr(priv, i)); + + return 0; +} + +static int gnr_gpio_resume(struct device *dev) +{ + struct gnr_gpio *priv = dev_get_drvdata(dev); + unsigned int i; + + guard(raw_spinlock_irqsave)(&priv->lock); + + for_each_clear_bit(i, priv->ro_bitmap, priv->gc.ngpio) + writel(priv->pad_backup[i], gnr_gpio_get_padcfg_addr(priv, i)); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(gnr_gpio_pm_ops, gnr_gpio_suspend, gnr_gpio_resume); + +static const struct acpi_device_id gnr_gpio_acpi_match[] = { + { "INTC1109" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, gnr_gpio_acpi_match); + +static struct platform_driver gnr_gpio_driver = { + .driver = { + .name = "gpio-graniterapids", + .pm = pm_sleep_ptr(&gnr_gpio_pm_ops), + .acpi_match_table = gnr_gpio_acpi_match, + }, + .probe = gnr_gpio_probe, +}; +module_platform_driver(gnr_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aapo Vienamo <aapo.vienamo@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Granite Rapids-D vGPIO driver"); diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 5ef8af824980..c097e310c9e8 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -529,6 +529,7 @@ static const struct of_device_id lpc32xx_gpio_of_match[] = { { .compatible = "nxp,lpc3220-gpio", }, { }, }; +MODULE_DEVICE_TABLE(of, lpc32xx_gpio_of_match); static struct platform_driver lpc32xx_gpio_driver = { .driver = { diff --git a/drivers/gpio/gpio-npcm-sgpio.c b/drivers/gpio/gpio-npcm-sgpio.c index d31788b43abc..260570614543 100644 --- a/drivers/gpio/gpio-npcm-sgpio.c +++ b/drivers/gpio/gpio-npcm-sgpio.c @@ -434,7 +434,7 @@ static void npcm_sgpio_irq_handler(struct irq_desc *desc) struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct irq_chip *ic = irq_desc_get_chip(desc); struct npcm_sgpio *gpio = gpiochip_get_data(gc); - unsigned int i, j, girq; + unsigned int i, j; unsigned long reg; chained_irq_enter(ic, desc); @@ -443,11 +443,9 @@ static void npcm_sgpio_irq_handler(struct irq_desc *desc) const struct npcm_sgpio_bank *bank = &npcm_sgpio_banks[i]; reg = ioread8(bank_reg(gpio, bank, EVENT_STS)); - for_each_set_bit(j, ®, 8) { - girq = irq_find_mapping(gc->irq.domain, - i * 8 + gpio->nout_sgpio + j); - generic_handle_domain_irq(gc->irq.domain, girq); - } + for_each_set_bit(j, ®, 8) + generic_handle_domain_irq(gc->irq.domain, + i * 8 + gpio->nout_sgpio + j); } chained_irq_exit(ic, desc); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 00ffa168e405..77a2812f2974 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -144,7 +144,7 @@ static int pca953x_acpi_get_irq(struct device *dev) if (ret) dev_warn(dev, "can't add GPIO ACPI mapping\n"); - ret = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev), "irq-gpios", 0); + ret = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev), "irq", 0); if (ret < 0) return ret; diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index 2efd1b1a0805..7f7f95ad4343 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -267,7 +267,7 @@ static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned case IDIO_24_CONTROL_REG: /* We can only set direction for TTL/CMOS lines */ if (offset < 48) - return -EOPNOTSUPP; + return -ENOTSUPP; *reg = IDIO_24_CONTROL_REG; *mask = CONTROL_REG_OUT_MODE; diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index c08c8e528867..71684dee2ca5 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -129,7 +129,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, base = gpio_regmap_addr(gpio->reg_dir_in_base); invert = 1; } else { - return -EOPNOTSUPP; + return -ENOTSUPP; } ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); @@ -160,7 +160,7 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip, base = gpio_regmap_addr(gpio->reg_dir_in_base); invert = 1; } else { - return -EOPNOTSUPP; + return -ENOTSUPP; } ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index e48392074e4b..ff0341b1222f 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -38,8 +38,8 @@ struct sch_gpio { struct gpio_chip chip; + void __iomem *regs; spinlock_t lock; - unsigned short iobase; unsigned short resume_base; /* GPE handling */ @@ -75,7 +75,7 @@ static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned in offset = sch_gpio_offset(sch, gpio, reg); bit = sch_gpio_bit(sch, gpio); - reg_val = !!(inb(sch->iobase + offset) & BIT(bit)); + reg_val = !!(ioread8(sch->regs + offset) & BIT(bit)); return reg_val; } @@ -89,12 +89,14 @@ static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned i offset = sch_gpio_offset(sch, gpio, reg); bit = sch_gpio_bit(sch, gpio); - reg_val = inb(sch->iobase + offset); + reg_val = ioread8(sch->regs + offset); if (val) - outb(reg_val | BIT(bit), sch->iobase + offset); + reg_val |= BIT(bit); else - outb((reg_val & ~BIT(bit)), sch->iobase + offset); + reg_val &= ~BIT(bit); + + iowrite8(reg_val, sch->regs + offset); } static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) @@ -267,8 +269,8 @@ static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) spin_lock_irqsave(&sch->lock, flags); - core_status = inl(sch->iobase + CORE_BANK_OFFSET + GTS); - resume_status = inl(sch->iobase + RESUME_BANK_OFFSET + GTS); + core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); + resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); spin_unlock_irqrestore(&sch->lock, flags); @@ -319,12 +321,14 @@ static int sch_gpio_install_gpe_handler(struct sch_gpio *sch) static int sch_gpio_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct gpio_irq_chip *girq; struct sch_gpio *sch; struct resource *res; + void __iomem *regs; int ret; - sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL); + sch = devm_kzalloc(dev, sizeof(*sch), GFP_KERNEL); if (!sch) return -ENOMEM; @@ -332,15 +336,16 @@ static int sch_gpio_probe(struct platform_device *pdev) if (!res) return -EBUSY; - if (!devm_request_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) + regs = devm_ioport_map(dev, res->start, resource_size(res)); + if (!regs) return -EBUSY; + sch->regs = regs; + spin_lock_init(&sch->lock); - sch->iobase = res->start; sch->chip = sch_gpio_chip; - sch->chip.label = dev_name(&pdev->dev); - sch->chip.parent = &pdev->dev; + sch->chip.label = dev_name(dev); + sch->chip.parent = dev; switch (pdev->id) { case PCI_DEVICE_ID_INTEL_SCH_LPC: @@ -394,9 +399,9 @@ static int sch_gpio_probe(struct platform_device *pdev) ret = sch_gpio_install_gpe_handler(sch); if (ret) - dev_warn(&pdev->dev, "Can't setup GPE, no IRQ support\n"); + dev_warn(dev, "Can't setup GPE, no IRQ support\n"); - return devm_gpiochip_add_data(&pdev->dev, &sch->chip, sch); + return devm_gpiochip_add_data(dev, &sch->chip, sch); } static struct platform_driver sch_gpio_driver = { diff --git a/drivers/gpio/gpio-tangier.c b/drivers/gpio/gpio-tangier.c index b75e0b12087a..4b29abafecf6 100644 --- a/drivers/gpio/gpio-tangier.c +++ b/drivers/gpio/gpio-tangier.c @@ -195,7 +195,8 @@ static int tng_gpio_set_config(struct gpio_chip *chip, unsigned int offset, static void tng_irq_ack(struct irq_data *d) { - struct tng_gpio *priv = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tng_gpio *priv = gpiochip_get_data(gc); irq_hw_number_t gpio = irqd_to_hwirq(d); void __iomem *gisr; u8 shift; @@ -227,7 +228,8 @@ static void tng_irq_unmask_mask(struct tng_gpio *priv, u32 gpio, bool unmask) static void tng_irq_mask(struct irq_data *d) { - struct tng_gpio *priv = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tng_gpio *priv = gpiochip_get_data(gc); irq_hw_number_t gpio = irqd_to_hwirq(d); tng_irq_unmask_mask(priv, gpio, false); @@ -236,7 +238,8 @@ static void tng_irq_mask(struct irq_data *d) static void tng_irq_unmask(struct irq_data *d) { - struct tng_gpio *priv = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tng_gpio *priv = gpiochip_get_data(gc); irq_hw_number_t gpio = irqd_to_hwirq(d); gpiochip_enable_irq(&priv->chip, gpio); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index d87dd06db40d..9130c691a2dd 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -36,12 +36,6 @@ #define TEGRA186_GPIO_SCR_SEC_REN BIT(27) #define TEGRA186_GPIO_SCR_SEC_G1W BIT(9) #define TEGRA186_GPIO_SCR_SEC_G1R BIT(1) -#define TEGRA186_GPIO_FULL_ACCESS (TEGRA186_GPIO_SCR_SEC_WEN | \ - TEGRA186_GPIO_SCR_SEC_REN | \ - TEGRA186_GPIO_SCR_SEC_G1R | \ - TEGRA186_GPIO_SCR_SEC_G1W) -#define TEGRA186_GPIO_SCR_SEC_ENABLE (TEGRA186_GPIO_SCR_SEC_WEN | \ - TEGRA186_GPIO_SCR_SEC_REN) /* control registers */ #define TEGRA186_GPIO_ENABLE_CONFIG 0x00 @@ -177,10 +171,18 @@ static inline bool tegra186_gpio_is_accessible(struct tegra_gpio *gpio, unsigned value = __raw_readl(secure + TEGRA186_GPIO_SCR); - if ((value & TEGRA186_GPIO_SCR_SEC_ENABLE) == 0) - return true; + /* + * When SCR_SEC_[R|W]EN is unset, then we have full read/write access to all the + * registers for given GPIO pin. + * When SCR_SEC[R|W]EN is set, then there is need to further check the accompanying + * SCR_SEC_G1[R|W] bit to determine read/write access to all the registers for given + * GPIO pin. + */ - if ((value & TEGRA186_GPIO_FULL_ACCESS) == TEGRA186_GPIO_FULL_ACCESS) + if (((value & TEGRA186_GPIO_SCR_SEC_REN) == 0 || + ((value & TEGRA186_GPIO_SCR_SEC_REN) && (value & TEGRA186_GPIO_SCR_SEC_G1R))) && + ((value & TEGRA186_GPIO_SCR_SEC_WEN) == 0 || + ((value & TEGRA186_GPIO_SCR_SEC_WEN) && (value & TEGRA186_GPIO_SCR_SEC_G1W)))) return true; return false; diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index c18b6b47384f..94ca9d03c094 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -104,7 +104,7 @@ static inline int to_reg(int gpio, enum ctrl_register type) unsigned int reg = type == CTRL_IN ? GPIO_IN_CTRL_BASE : GPIO_OUT_CTRL_BASE; if (gpio >= WCOVE_GPIO_NUM) - return -EOPNOTSUPP; + return -ENOTSUPP; return reg + gpio; } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 7f140df40f35..553a5f94c00a 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -873,9 +873,6 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, struct acpi_gpio_lookup lookup; int ret; - if (!adev) - return ERR_PTR(-ENODEV); - memset(&lookup, 0, sizeof(lookup)); lookup.index = index; @@ -948,14 +945,11 @@ static bool acpi_can_fallback_to_crs(struct acpi_device *adev, return con_id == NULL; } -struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, - const char *con_id, - unsigned int idx, - enum gpiod_flags *dflags, - unsigned long *lookupflags) +static struct gpio_desc * +__acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int idx, + bool can_fallback, struct acpi_gpio_info *info) { struct acpi_device *adev = to_acpi_device_node(fwnode); - struct acpi_gpio_info info; struct gpio_desc *desc; char propname[32]; int i; @@ -972,25 +966,38 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, if (adev) desc = acpi_get_gpiod_by_index(adev, - propname, idx, &info); + propname, idx, info); else desc = acpi_get_gpiod_from_data(fwnode, - propname, idx, &info); - if (!IS_ERR(desc)) - break; + propname, idx, info); if (PTR_ERR(desc) == -EPROBE_DEFER) return ERR_CAST(desc); + + if (!IS_ERR(desc)) + return desc; } /* Then from plain _CRS GPIOs */ - if (IS_ERR(desc)) { - if (!adev || !acpi_can_fallback_to_crs(adev, con_id)) - return ERR_PTR(-ENOENT); + if (!adev || !can_fallback) + return ERR_PTR(-ENOENT); - desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info); - if (IS_ERR(desc)) - return desc; - } + return acpi_get_gpiod_by_index(adev, NULL, idx, info); +} + +struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, + const char *con_id, + unsigned int idx, + enum gpiod_flags *dflags, + unsigned long *lookupflags) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + bool can_fallback = acpi_can_fallback_to_crs(adev, con_id); + struct acpi_gpio_info info; + struct gpio_desc *desc; + + desc = __acpi_find_gpio(fwnode, con_id, idx, can_fallback, &info); + if (IS_ERR(desc)) + return desc; if (info.gpioint && (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { @@ -1006,7 +1013,7 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, /** * acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number * @adev: pointer to a ACPI device to get IRQ from - * @name: optional name of GpioInt resource + * @con_id: optional name of GpioInt resource * @index: index of GpioInt resource (starting from %0) * @wake_capable: Set to true if the IRQ is wake capable * @@ -1017,17 +1024,18 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, * The function is idempotent, though each time it runs it will configure GPIO * pin direction according to the flags in GpioInt resource. * - * The function takes optional @name parameter. If the resource has a property - * name, then only those will be taken into account. + * The function takes optional @con_id parameter. If the resource has + * a @con_id in a property, then only those will be taken into account. * * The GPIO is considered wake capable if the GpioInt resource specifies * SharedAndWake or ExclusiveAndWake. * * Return: Linux IRQ number (> %0) on success, negative errno on failure. */ -int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, int index, +int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *con_id, int index, bool *wake_capable) { + struct fwnode_handle *fwnode = acpi_fwnode_handle(adev); int idx, i; unsigned int irq_flags; int ret; @@ -1036,9 +1044,8 @@ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, in struct acpi_gpio_info info; struct gpio_desc *desc; - desc = acpi_get_gpiod_by_index(adev, name, i, &info); - /* Ignore -EPROBE_DEFER, it only matters if idx matches */ + desc = __acpi_find_gpio(fwnode, con_id, i, true, &info); if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) return PTR_ERR(desc); @@ -1058,7 +1065,11 @@ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, in acpi_gpio_update_gpiod_flags(&dflags, &info); acpi_gpio_update_gpiod_lookup_flags(&lflags, &info); - snprintf(label, sizeof(label), "GpioInt() %d", index); + snprintf(label, sizeof(label), "%pfwP GpioInt(%d)", fwnode, index); + ret = gpiod_set_consumer_name(desc, con_id ?: label); + if (ret) + return ret; + ret = gpiod_configure_flags(desc, label, lflags, dflags); if (ret < 0) return ret; diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index f384fa278764..9dad67ea2597 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -728,6 +728,25 @@ static u32 line_event_id(int level) GPIO_V2_LINE_EVENT_FALLING_EDGE; } +static inline char *make_irq_label(const char *orig) +{ + char *new; + + if (!orig) + return NULL; + + new = kstrdup_and_replace(orig, '/', ':', GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + return new; +} + +static inline void free_irq_label(const char *label) +{ + kfree(label); +} + #ifdef CONFIG_HTE static enum hte_return process_hw_ts_thread(void *p) @@ -1015,6 +1034,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us) { unsigned long irqflags; int ret, level, irq; + char *label; /* try hardware */ ret = gpiod_set_debounce(line->desc, debounce_period_us); @@ -1037,11 +1057,17 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us) if (irq < 0) return -ENXIO; + label = make_irq_label(line->req->label); + if (IS_ERR(label)) + return -ENOMEM; + irqflags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING; ret = request_irq(irq, debounce_irq_handler, irqflags, - line->req->label, line); - if (ret) + label, line); + if (ret) { + free_irq_label(label); return ret; + } line->irq = irq; } else { ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH); @@ -1086,7 +1112,7 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc, static void edge_detector_stop(struct line *line) { if (line->irq) { - free_irq(line->irq, line); + free_irq_label(free_irq(line->irq, line)); line->irq = 0; } @@ -1110,6 +1136,7 @@ static int edge_detector_setup(struct line *line, unsigned long irqflags = 0; u64 eflags; int irq, ret; + char *label; eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; if (eflags && !kfifo_initialized(&line->req->events)) { @@ -1146,11 +1173,17 @@ static int edge_detector_setup(struct line *line, IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; irqflags |= IRQF_ONESHOT; + label = make_irq_label(line->req->label); + if (IS_ERR(label)) + return PTR_ERR(label); + /* Request a thread to read the events */ ret = request_threaded_irq(irq, edge_irq_handler, edge_irq_thread, - irqflags, line->req->label, line); - if (ret) + irqflags, label, line); + if (ret) { + free_irq_label(label); return ret; + } line->irq = irq; return 0; @@ -1160,6 +1193,8 @@ static int edge_detector_update(struct line *line, struct gpio_v2_line_config *lc, unsigned int line_idx, u64 edflags) { + u64 eflags; + int ret; u64 active_edflags = READ_ONCE(line->edflags); unsigned int debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); @@ -1171,6 +1206,18 @@ static int edge_detector_update(struct line *line, /* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) { line_set_debounce_period(line, debounce_period_us); + /* + * ensure event fifo is initialised if edge detection + * is now enabled. + */ + eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; + if (eflags && !kfifo_initialized(&line->req->events)) { + ret = kfifo_alloc(&line->req->events, + line->req->event_buffer_size, + GFP_KERNEL); + if (ret) + return ret; + } return 0; } @@ -1973,7 +2020,7 @@ static void lineevent_free(struct lineevent_state *le) blocking_notifier_chain_unregister(&le->gdev->device_notifier, &le->device_unregistered_nb); if (le->irq) - free_irq(le->irq, le); + free_irq_label(free_irq(le->irq, le)); if (le->desc) gpiod_free(le->desc); kfree(le->label); @@ -2114,6 +2161,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) int fd; int ret; int irq, irqflags = 0; + char *label; if (copy_from_user(&eventreq, ip, sizeof(eventreq))) return -EFAULT; @@ -2198,15 +2246,23 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (ret) goto out_free_le; + label = make_irq_label(le->label); + if (IS_ERR(label)) { + ret = PTR_ERR(label); + goto out_free_le; + } + /* Request a thread to read the events */ ret = request_threaded_irq(irq, lineevent_irq_handler, lineevent_irq_thread, irqflags, - le->label, + label, le); - if (ret) + if (ret) { + free_irq_label(label); goto out_free_le; + } le->irq = irq; @@ -2309,7 +2365,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, dflags = READ_ONCE(desc->flags); - scoped_guard(srcu, &desc->srcu) { + scoped_guard(srcu, &desc->gdev->desc_srcu) { label = gpiod_get_label(desc); if (label && test_bit(FLAG_REQUESTED, &dflags)) strscpy(info->consumer, label, @@ -2757,11 +2813,11 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file) struct gpio_chardev_data *cdev = file->private_data; struct gpio_device *gdev = cdev->gdev; - bitmap_free(cdev->watched_lines); blocking_notifier_chain_unregister(&gdev->device_notifier, &cdev->device_unregistered_nb); blocking_notifier_chain_unregister(&gdev->line_state_notifier, &cdev->lineinfo_changed_nb); + bitmap_free(cdev->watched_lines); gpio_device_put(gdev); kfree(cdev); diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index b138682fec3d..5a9911ae9125 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -28,10 +28,9 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) struct gpio_desc *desc; int err; - desc = gpio_to_desc(gpio); - /* Compatibility: assume unavailable "valid" GPIOs will appear later */ - if (!desc && gpio_is_valid(gpio)) + desc = gpio_to_desc(gpio); + if (!desc) return -EPROBE_DEFER; err = gpiod_request(desc, label); @@ -63,51 +62,13 @@ EXPORT_SYMBOL_GPL(gpio_request_one); */ int gpio_request(unsigned gpio, const char *label) { - struct gpio_desc *desc = gpio_to_desc(gpio); + struct gpio_desc *desc; /* Compatibility: assume unavailable "valid" GPIOs will appear later */ - if (!desc && gpio_is_valid(gpio)) + desc = gpio_to_desc(gpio); + if (!desc) return -EPROBE_DEFER; return gpiod_request(desc, label); } EXPORT_SYMBOL_GPL(gpio_request); - -/** - * gpio_request_array - request multiple GPIOs in a single call - * @array: array of the 'struct gpio' - * @num: how many GPIOs in the array - * - * **DEPRECATED** This function is deprecated and must not be used in new code. - */ -int gpio_request_array(const struct gpio *array, size_t num) -{ - int i, err; - - for (i = 0; i < num; i++, array++) { - err = gpio_request_one(array->gpio, array->flags, array->label); - if (err) - goto err_free; - } - return 0; - -err_free: - while (i--) - gpio_free((--array)->gpio); - return err; -} -EXPORT_SYMBOL_GPL(gpio_request_array); - -/** - * gpio_free_array - release multiple GPIOs in a single call - * @array: array of the 'struct gpio' - * @num: how many GPIOs in the array - * - * **DEPRECATED** This function is deprecated and must not be used in new code. - */ -void gpio_free_array(const struct gpio *array, size_t num) -{ - while (num--) - gpio_free((array++)->gpio); -} -EXPORT_SYMBOL_GPL(gpio_free_array); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index cb0cefaec37e..d75f6ee37028 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1037,7 +1037,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) struct of_phandle_args pinspec; struct pinctrl_dev *pctldev; struct device_node *np; - int index = 0, ret; + int index = 0, ret, trim; const char *name; static const char group_names_propname[] = "gpio-ranges-group-names"; struct property *group_names; @@ -1059,7 +1059,14 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) return -EPROBE_DEFER; + /* Ignore ranges outside of this GPIO chip */ + if (pinspec.args[0] >= (chip->offset + chip->ngpio)) + continue; + if (pinspec.args[0] + pinspec.args[2] <= chip->offset) + continue; + if (pinspec.args[2]) { + /* npins != 0: linear range */ if (group_names) { of_property_read_string_index(np, group_names_propname, @@ -1070,7 +1077,19 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) break; } } - /* npins != 0: linear range */ + + /* Trim the range to fit this GPIO chip */ + if (chip->offset > pinspec.args[0]) { + trim = chip->offset - pinspec.args[0]; + pinspec.args[2] -= trim; + pinspec.args[1] += trim; + pinspec.args[0] = 0; + } else { + pinspec.args[0] -= chip->offset; + } + if ((pinspec.args[0] + pinspec.args[2]) > chip->ngpio) + pinspec.args[2] = chip->ngpio - pinspec.args[0]; + ret = gpiochip_add_pin_range(chip, pinctrl_dev_get_devname(pctldev), pinspec.args[0], diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index fa52bdb1a29a..cec1ab878af8 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -4,8 +4,13 @@ * * Copyright 2022 Google LLC */ + +#define pr_fmt(fmt) "gpiolib: swnode: " fmt + #include <linux/err.h> #include <linux/errno.h> +#include <linux/export.h> +#include <linux/init.h> #include <linux/kernel.h> #include <linux/printk.h> #include <linux/property.h> @@ -17,6 +22,8 @@ #include "gpiolib.h" #include "gpiolib-swnode.h" +#define GPIOLIB_SWNODE_UNDEFINED_NAME "swnode-gpio-undefined" + static void swnode_format_propname(const char *con_id, char *propname, size_t max_size) { @@ -40,6 +47,14 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) if (!gdev_node || !gdev_node->name) return ERR_PTR(-EINVAL); + /* + * Check for a special node that identifies undefined GPIOs, this is + * primarily used as a key for internal chip selects in SPI bindings. + */ + if (IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED) && + !strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME)) + return ERR_PTR(-ENOENT); + gdev = gpio_device_find_by_label(gdev_node->name); return gdev ?: ERR_PTR(-EPROBE_DEFER); } @@ -121,3 +136,32 @@ int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) return count ?: -ENOENT; } + +#if IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED) +/* + * A special node that identifies undefined GPIOs, this is primarily used as + * a key for internal chip selects in SPI bindings. + */ +const struct software_node swnode_gpio_undefined = { + .name = GPIOLIB_SWNODE_UNDEFINED_NAME, +}; +EXPORT_SYMBOL_NS_GPL(swnode_gpio_undefined, GPIO_SWNODE); + +static int __init swnode_gpio_init(void) +{ + int ret; + + ret = software_node_register(&swnode_gpio_undefined); + if (ret < 0) + pr_err("failed to register swnode: %d\n", ret); + + return ret; +} +subsys_initcall(swnode_gpio_init); + +static void __exit swnode_gpio_cleanup(void) +{ + software_node_unregister(&swnode_gpio_undefined); +} +__exitcall(swnode_gpio_cleanup); +#endif diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 6853ecd98bcb..26202586fd39 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -412,7 +412,7 @@ static ssize_t base_show(struct device *dev, { const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%d\n", gdev->base); + return sysfs_emit(buf, "%u\n", gdev->base); } static DEVICE_ATTR_RO(base); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ce94e37bcbee..fa62367ee929 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -101,6 +101,7 @@ static bool gpiolib_initialized; const char *gpiod_get_label(struct gpio_desc *desc) { + struct gpio_desc_label *label; unsigned long flags; flags = READ_ONCE(desc->flags); @@ -108,23 +109,36 @@ const char *gpiod_get_label(struct gpio_desc *desc) !test_bit(FLAG_REQUESTED, &flags)) return "interrupt"; - return test_bit(FLAG_REQUESTED, &flags) ? - srcu_dereference(desc->label, &desc->srcu) : NULL; + if (!test_bit(FLAG_REQUESTED, &flags)) + return NULL; + + label = srcu_dereference_check(desc->label, &desc->gdev->desc_srcu, + srcu_read_lock_held(&desc->gdev->desc_srcu)); + + return label->str; +} + +static void desc_free_label(struct rcu_head *rh) +{ + kfree(container_of(rh, struct gpio_desc_label, rh)); } static int desc_set_label(struct gpio_desc *desc, const char *label) { - const char *new = NULL, *old; + struct gpio_desc_label *new = NULL, *old; if (label) { - new = kstrdup_const(label, GFP_KERNEL); + new = kzalloc(struct_size(new, str, strlen(label) + 1), + GFP_KERNEL); if (!new) return -ENOMEM; + + strcpy(new->str, label); } old = rcu_replace_pointer(desc->label, new, 1); - synchronize_srcu(&desc->srcu); - kfree_const(old); + if (old) + call_srcu(&desc->gdev->desc_srcu, &old->rh, desc_free_label); return 0; } @@ -150,9 +164,6 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) } } - if (!gpio_is_valid(gpio)) - pr_warn("invalid GPIO %d\n", gpio); - return NULL; } EXPORT_SYMBOL_GPL(gpio_to_desc); @@ -297,10 +308,10 @@ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev) EXPORT_SYMBOL_GPL(gpio_device_get_chip); /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ -static int gpiochip_find_base_unlocked(int ngpio) +static int gpiochip_find_base_unlocked(u16 ngpio) { + unsigned int base = GPIO_DYNAMIC_BASE; struct gpio_device *gdev; - int base = GPIO_DYNAMIC_BASE; list_for_each_entry_srcu(gdev, &gpio_devices, list, lockdep_is_held(&gpio_devices_lock)) { @@ -311,9 +322,11 @@ static int gpiochip_find_base_unlocked(int ngpio) base = gdev->base + gdev->ngpio; if (base < GPIO_DYNAMIC_BASE) base = GPIO_DYNAMIC_BASE; + if (base > GPIO_DYNAMIC_MAX - ngpio) + break; } - if (gpio_is_valid(base)) { + if (base <= GPIO_DYNAMIC_MAX - ngpio) { pr_debug("%s: found new base at %d\n", __func__, base); return base; } else { @@ -365,7 +378,10 @@ int gpiod_get_direction(struct gpio_desc *desc) if (ret < 0) return ret; - /* GPIOF_DIR_IN or other positive, otherwise GPIOF_DIR_OUT */ + /* + * GPIO_LINE_DIRECTION_IN or other positive, + * otherwise GPIO_LINE_DIRECTION_OUT. + */ if (ret > 0) ret = 1; @@ -695,10 +711,10 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); static void gpiodev_release(struct device *dev) { struct gpio_device *gdev = to_gpio_device(dev); - unsigned int i; - for (i = 0; i < gdev->ngpio; i++) - cleanup_srcu_struct(&gdev->descs[i].srcu); + /* Call pending kfree()s for descriptor labels. */ + synchronize_srcu(&gdev->desc_srcu); + cleanup_srcu_struct(&gdev->desc_srcu); ida_free(&gpio_ida, gdev->id); kfree_const(gdev->label); @@ -746,7 +762,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) if (ret) goto err_remove_device; - dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base, + dev_dbg(&gdev->dev, "registered GPIOs %u to %u on %s\n", gdev->base, gdev->base + gdev->ngpio - 1, gdev->label); return 0; @@ -975,6 +991,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_remove_from_list; + ret = init_srcu_struct(&gdev->desc_srcu); + if (ret) + goto err_cleanup_gdev_srcu; + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -982,23 +1002,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (gc->names) { ret = gpiochip_set_desc_names(gc); if (ret) - goto err_cleanup_gdev_srcu; + goto err_cleanup_desc_srcu; } ret = gpiochip_set_names(gc); if (ret) - goto err_cleanup_gdev_srcu; + goto err_cleanup_desc_srcu; ret = gpiochip_init_valid_mask(gc); if (ret) - goto err_cleanup_gdev_srcu; + goto err_cleanup_desc_srcu; for (desc_index = 0; desc_index < gc->ngpio; desc_index++) { struct gpio_desc *desc = &gdev->descs[desc_index]; - ret = init_srcu_struct(&desc->srcu); - if (ret) - goto err_cleanup_desc_srcu; - if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) { assign_bit(FLAG_IS_OUT, &desc->flags, !gc->get_direction(gc, desc_index)); @@ -1010,7 +1026,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = of_gpiochip_add(gc); if (ret) - goto err_cleanup_desc_srcu; + goto err_free_valid_mask; ret = gpiochip_add_pin_ranges(gc); if (ret) @@ -1057,10 +1073,10 @@ err_free_hogs: gpiochip_remove_pin_ranges(gc); err_remove_of_chip: of_gpiochip_remove(gc); -err_cleanup_desc_srcu: - while (desc_index--) - cleanup_srcu_struct(&gdev->descs[desc_index].srcu); +err_free_valid_mask: gpiochip_free_valid_mask(gc); +err_cleanup_desc_srcu: + cleanup_srcu_struct(&gdev->desc_srcu); err_cleanup_gdev_srcu: cleanup_srcu_struct(&gdev->srcu); err_remove_from_list: @@ -1175,6 +1191,9 @@ struct gpio_device *gpio_device_find(const void *data, list_for_each_entry_srcu(gdev, &gpio_devices, list, srcu_read_lock_held(&gpio_devices_srcu)) { + if (!device_is_registered(&gdev->dev)) + continue; + guard(srcu)(&gdev->srcu); gc = srcu_dereference(gdev->chip, &gdev->srcu); @@ -2387,7 +2406,7 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) if (!test_bit(FLAG_REQUESTED, &desc->flags)) return NULL; - guard(srcu)(&desc->srcu); + guard(srcu)(&desc->gdev->desc_srcu); label = kstrdup(gpiod_get_label(desc), GFP_KERNEL); if (!label) @@ -2397,6 +2416,11 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_dup_line_label); +static inline const char *function_name_or_default(const char *con_id) +{ + return con_id ?: "(default)"; +} + /** * gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor * @gc: GPIO chip @@ -2425,10 +2449,11 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, enum gpiod_flags dflags) { struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); + const char *name = function_name_or_default(label); int ret; if (IS_ERR(desc)) { - chip_err(gc, "failed to get GPIO descriptor\n"); + chip_err(gc, "failed to get GPIO %s descriptor\n", name); return desc; } @@ -2438,8 +2463,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, ret = gpiod_configure_flags(desc, label, lflags, dflags); if (ret) { - chip_err(gc, "setup of own GPIO %s failed\n", label); gpiod_free_commit(desc); + chip_err(gc, "setup of own GPIO %s failed\n", name); return ERR_PTR(ret); } @@ -4153,19 +4178,17 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode, enum gpiod_flags *flags, unsigned long *lookupflags) { + const char *name = function_name_or_default(con_id); struct gpio_desc *desc = ERR_PTR(-ENOENT); if (is_of_node(fwnode)) { - dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n", - fwnode, con_id); + dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n", fwnode, name); desc = of_find_gpio(to_of_node(fwnode), con_id, idx, lookupflags); } else if (is_acpi_node(fwnode)) { - dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n", - fwnode, con_id); + dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n", fwnode, name); desc = acpi_find_gpio(fwnode, con_id, idx, flags, lookupflags); } else if (is_software_node(fwnode)) { - dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n", - fwnode, con_id); + dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n", fwnode, name); desc = swnode_find_gpio(fwnode, con_id, idx, lookupflags); } @@ -4181,6 +4204,7 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, bool platform_lookup_allowed) { unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT; + const char *name = function_name_or_default(con_id); /* * scoped_guard() is implemented as a for loop, meaning static * analyzers will complain about these two not being initialized. @@ -4203,8 +4227,7 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, } if (IS_ERR(desc)) { - dev_dbg(consumer, "No GPIO consumer %s found\n", - con_id); + dev_dbg(consumer, "No GPIO consumer %s found\n", name); return desc; } @@ -4226,15 +4249,14 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, * * FIXME: Make this more sane and safe. */ - dev_info(consumer, - "nonexclusive access to GPIO for %s\n", con_id); + dev_info(consumer, "nonexclusive access to GPIO for %s\n", name); return desc; } ret = gpiod_configure_flags(desc, con_id, lookupflags, flags); if (ret < 0) { - dev_dbg(consumer, "setup of GPIO %s failed\n", con_id); gpiod_put(desc); + dev_err(consumer, "setup of GPIO %s failed: %d\n", name, ret); return ERR_PTR(ret); } @@ -4350,6 +4372,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional); int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags) { + const char *name = function_name_or_default(con_id); int ret; if (lflags & GPIO_ACTIVE_LOW) @@ -4393,7 +4416,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { - gpiod_dbg(desc, "no flags found for %s\n", con_id); + gpiod_dbg(desc, "no flags found for GPIO %s\n", name); return 0; } @@ -4774,21 +4797,21 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) } for_each_gpio_desc(gc, desc) { - guard(srcu)(&desc->srcu); + guard(srcu)(&desc->gdev->desc_srcu); if (test_bit(FLAG_REQUESTED, &desc->flags)) { gpiod_get_direction(desc); is_out = test_bit(FLAG_IS_OUT, &desc->flags); value = gpio_chip_get_value(gc, desc); is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags); active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags); - seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s\n", + seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n", gpio, desc->name ?: "", gpiod_get_label(desc), is_out ? "out" : "in ", value >= 0 ? (value ? "hi" : "lo") : "? ", is_irq ? "IRQ " : "", active_low ? "ACTIVE LOW" : ""); } else if (desc->name) { - seq_printf(s, " gpio-%-3d (%-20.20s)\n", gpio, desc->name); + seq_printf(s, " gpio-%-3u (%-20.20s)\n", gpio, desc->name); } gpio++; @@ -4860,7 +4883,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) return 0; } - seq_printf(s, "%s%s: GPIOs %d-%d", priv->newline ? "\n" : "", + seq_printf(s, "%s%s: GPIOs %u-%u", priv->newline ? "\n" : "", dev_name(&gdev->dev), gdev->base, gdev->base + gdev->ngpio - 1); parent = gc->parent; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index f67d5991ab1c..48e086c2f416 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -31,6 +31,7 @@ * @chip: pointer to the corresponding gpiochip, holding static * data for this device * @descs: array of ngpio descriptors. + * @desc_srcu: ensures consistent state of GPIO descriptors exposed to users * @ngpio: the number of GPIO lines on this GPIO device, equal to the size * of the @descs array. * @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep @@ -61,7 +62,8 @@ struct gpio_device { struct module *owner; struct gpio_chip __rcu *chip; struct gpio_desc *descs; - int base; + struct srcu_struct desc_srcu; + unsigned int base; u16 ngpio; bool can_sleep; const char *label; @@ -137,6 +139,11 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); +struct gpio_desc_label { + struct rcu_head rh; + char str[]; +}; + /** * struct gpio_desc - Opaque descriptor for a GPIO * @@ -145,7 +152,6 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); * @label: Name of the consumer * @name: Line name * @hog: Pointer to the device node that hogs this line (if any) - * @srcu: SRCU struct protecting the label pointer. * * These are obtained using gpiod_get() and are preferable to the old * integer-based handles. @@ -177,13 +183,12 @@ struct gpio_desc { #define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ /* Connection label */ - const char __rcu *label; + struct gpio_desc_label __rcu *label; /* Name of the GPIO */ const char *name; #ifdef CONFIG_OF_DYNAMIC struct device_node *hog; #endif - struct srcu_struct srcu; }; #define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) @@ -251,7 +256,7 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) #define gpiod_err(desc, fmt, ...) \ do { \ - scoped_guard(srcu, &desc->srcu) { \ + scoped_guard(srcu, &desc->gdev->desc_srcu) { \ pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ } \ @@ -259,7 +264,7 @@ do { \ #define gpiod_warn(desc, fmt, ...) \ do { \ - scoped_guard(srcu, &desc->srcu) { \ + scoped_guard(srcu, &desc->gdev->desc_srcu) { \ pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ } \ @@ -267,7 +272,7 @@ do { \ #define gpiod_dbg(desc, fmt, ...) \ do { \ - scoped_guard(srcu, &desc->srcu) { \ + scoped_guard(srcu, &desc->gdev->desc_srcu) { \ pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ } \ |