diff options
Diffstat (limited to 'drivers/gpio')
51 files changed, 2167 insertions, 818 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7ee3afbc2b05..c74da29253e8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -6,6 +6,9 @@ config GPIOLIB_LEGACY def_bool y +config HAVE_SHARED_GPIOS + bool + menuconfig GPIOLIB bool "GPIO Support" help @@ -42,13 +45,10 @@ config GPIOLIB_IRQCHIP select IRQ_DOMAIN bool -config OF_GPIO_MM_GPIOCHIP - bool - help - This adds support for the legacy 'struct of_mm_gpio_chip' interface - from PowerPC. Existing drivers using this interface need to select - this symbol, but new drivers should use the generic gpio-regmap - infrastructure instead. +config GPIO_SHARED + def_bool y + depends on HAVE_SHARED_GPIOS || COMPILE_TEST + select AUXILIARY_BUS config DEBUG_GPIO bool "Debug GPIO calls" @@ -476,7 +476,6 @@ config GPIO_MENZ127 config GPIO_MM_LANTIQ bool "Lantiq Memory mapped GPIOs" depends on LANTIQ && SOC_XWAY - select OF_GPIO_MM_GPIOCHIP help This enables support for memory mapped GPIOs on the External Bus Unit (EBU) found on Lantiq SoCs. The GPIOs are output only as they are @@ -1413,7 +1412,7 @@ config HTC_EGPIO config GPIO_ELKHARTLAKE tristate "Intel Elkhart Lake PSE GPIO support" - depends on X86 || COMPILE_TEST + depends on INTEL_EHL_PSE_IO select GPIO_TANGIER help Select this option to enable GPIO support for Intel Elkhart Lake @@ -1565,6 +1564,15 @@ config GPIO_PMIC_EIC_SPRD help Say yes here to support Spreadtrum PMIC EIC device. +config GPIO_QIXIS_FPGA + tristate "NXP QIXIS FPGA GPIO support" + depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST + select GPIO_REGMAP + help + This enables support for the GPIOs found in the QIXIS FPGA which is + integrated on some NXP Layerscape boards such as LX2160ARDB and + LS1046AQDS. + config GPIO_RC5T583 bool "RICOH RC5T583 GPIO" depends on MFD_RC5T583 @@ -2017,6 +2025,15 @@ config GPIO_SIM This enables the GPIO simulator - a configfs-based GPIO testing driver. +config GPIO_SHARED_PROXY + tristate "Proxy driver for non-exclusive GPIOs" + default m + depends on GPIO_SHARED || COMPILE_TEST + select AUXILIARY_BUS + help + This enables the GPIO shared proxy driver - an abstraction layer + for GPIO pins that are shared by multiple devices. + endmenu menu "GPIO Debugging utilities" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ec296fa14bfd..2421a8fd3733 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o +obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o @@ -146,6 +147,7 @@ obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o +obj-$(CONFIG_GPIO_QIXIS_FPGA) += gpio-qixis-fpga.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o @@ -159,6 +161,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_SHARED_PROXY) += gpio-shared-proxy.o obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o obj-$(CONFIG_GPIO_SIM) += gpio-sim.o obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 8ed74e05903a..5acaeab029ec 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -86,17 +86,6 @@ Work items: ------------------------------------------------------------------------------- -Get rid of <linux/gpio/legacy-of-mm-gpiochip.h> - -Work items: - -- Get rid of struct of_mm_gpio_chip altogether: use the generic MMIO - GPIO for all current users (see below). Delete struct of_mm_gpio_chip, - to_of_mm_gpio_chip(), of_mm_gpiochip_add_data(), of_mm_gpiochip_remove(), - CONFIG_OF_GPIO_MM_GPIOCHIP from the kernel. - -------------------------------------------------------------------------------- - Collect drivers Collect GPIO drivers from arch/* and other places that should be placed diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index ffe7e1cb6b23..fe5c10cd5c32 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -59,6 +59,7 @@ static const struct regmap_config idio_16_regmap_config = { .reg_stride = 1, .val_bits = 8, .io_port = true, + .max_register = 0x5, .wr_table = &idio_16_wr_table, .rd_table = &idio_16_rd_table, .volatile_table = &idio_16_rd_table, diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 37600faf4a4b..416f265d09d0 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -723,6 +723,7 @@ struct gpiochip_fwd *devm_gpiochip_fwd_alloc(struct device *dev, chip->get_multiple = gpio_fwd_get_multiple_locked; chip->set = gpio_fwd_set; chip->set_multiple = gpio_fwd_set_multiple_locked; + chip->set_config = gpio_fwd_set_config; chip->to_irq = gpio_fwd_to_irq; chip->base = -1; chip->ngpio = ngpios; diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 7953a9c4e36d..2e0ae953dd99 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -24,12 +24,11 @@ /* * These two headers aren't meant to be used by GPIO drivers. We need - * them in order to access gpio_chip_hwgpio() which we need to implement + * them in order to access gpiod_hwgpio() which we need to implement * the aspeed specific API which allows the coprocessor to request * access to some GPIOs and to arbitrate between coprocessor and ARM. */ #include <linux/gpio/consumer.h> -#include "gpiolib.h" /* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) @@ -942,7 +941,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, { struct gpio_chip *chip = gpiod_to_chip(desc); struct aspeed_gpio *gpio = gpiochip_get_data(chip); - int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); + int rc = 0, bindex, offset = gpiod_hwgpio(desc); const struct aspeed_gpio_bank *bank = to_bank(offset); if (!aspeed_gpio_support_copro(gpio)) @@ -987,7 +986,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) { struct gpio_chip *chip = gpiod_to_chip(desc); struct aspeed_gpio *gpio = gpiochip_get_data(chip); - int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); + int rc = 0, bindex, offset = gpiod_hwgpio(desc); if (!aspeed_gpio_support_copro(gpio)) return -EOPNOTSUPP; diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index f40c9472588b..af9287ff5dc4 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -533,7 +533,6 @@ static void brcmstb_gpio_shutdown(struct platform_device *pdev) brcmstb_gpio_quiesce(&pdev->dev, false); } -#ifdef CONFIG_PM_SLEEP static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv, struct brcmstb_gpio_bank *bank) { @@ -572,14 +571,9 @@ static int brcmstb_gpio_resume(struct device *dev) return 0; } -#else -#define brcmstb_gpio_suspend NULL -#define brcmstb_gpio_resume NULL -#endif /* CONFIG_PM_SLEEP */ - static const struct dev_pm_ops brcmstb_gpio_pm_ops = { - .suspend_noirq = brcmstb_gpio_suspend, - .resume_noirq = brcmstb_gpio_resume, + .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend), + .resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume), }; static int brcmstb_gpio_probe(struct platform_device *pdev) @@ -755,7 +749,7 @@ static struct platform_driver brcmstb_gpio_driver = { .driver = { .name = "brcmstb-gpio", .of_match_table = brcmstb_gpio_of_match, - .pm = &brcmstb_gpio_pm_ops, + .pm = pm_sleep_ptr(&brcmstb_gpio_pm_ops), }, .probe = brcmstb_gpio_probe, .remove = brcmstb_gpio_remove, diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 05401da03ca3..324eeb77dbd5 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -52,10 +52,8 @@ struct bt8xxgpio { struct pci_dev *pdev; struct gpio_chip gpio; -#ifdef CONFIG_PM u32 saved_outen; u32 saved_data; -#endif }; #define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) @@ -224,9 +222,10 @@ static void bt8xxgpio_remove(struct pci_dev *pdev) pci_disable_device(pdev); } -#ifdef CONFIG_PM -static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) + +static int bt8xxgpio_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct bt8xxgpio *bg = pci_get_drvdata(pdev); scoped_guard(spinlock_irqsave, &bg->lock) { @@ -238,23 +237,13 @@ static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) bgwrite(0x0, BT848_GPIO_OUT_EN); } - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; } -static int bt8xxgpio_resume(struct pci_dev *pdev) +static int bt8xxgpio_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct bt8xxgpio *bg = pci_get_drvdata(pdev); - int err; - - pci_set_power_state(pdev, PCI_D0); - err = pci_enable_device(pdev); - if (err) - return err; - pci_restore_state(pdev); guard(spinlock_irqsave)(&bg->lock); @@ -267,10 +256,8 @@ static int bt8xxgpio_resume(struct pci_dev *pdev) return 0; } -#else -#define bt8xxgpio_suspend NULL -#define bt8xxgpio_resume NULL -#endif /* CONFIG_PM */ + +static DEFINE_SIMPLE_DEV_PM_OPS(bt8xxgpio_pm_ops, bt8xxgpio_suspend, bt8xxgpio_resume); static const struct pci_device_id bt8xxgpio_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, @@ -286,8 +273,7 @@ static struct pci_driver bt8xxgpio_pci_driver = { .id_table = bt8xxgpio_pci_tbl, .probe = bt8xxgpio_probe, .remove = bt8xxgpio_remove, - .suspend = bt8xxgpio_suspend, - .resume = bt8xxgpio_resume, + .driver.pm = &bt8xxgpio_pm_ops, }; module_pci_driver(bt8xxgpio_pci_driver); diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index b42ff46d292b..4986c465c9a8 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -79,7 +79,6 @@ struct dwapb_platform_data { unsigned int nports; }; -#ifdef CONFIG_PM_SLEEP /* Store GPIO context across system-wide suspend/resume transitions */ struct dwapb_context { u32 data; @@ -92,7 +91,6 @@ struct dwapb_context { u32 int_deb; u32 wake_en; }; -#endif struct dwapb_gpio_port_irqchip { unsigned int nr_irqs; @@ -103,9 +101,7 @@ struct dwapb_gpio_port { struct gpio_generic_chip chip; struct dwapb_gpio_port_irqchip *pirq; struct dwapb_gpio *gpio; -#ifdef CONFIG_PM_SLEEP struct dwapb_context *ctx; -#endif unsigned int idx; }; @@ -363,7 +359,6 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) return 0; } -#ifdef CONFIG_PM_SLEEP static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); @@ -378,9 +373,6 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) return 0; } -#else -#define dwapb_irq_set_wake NULL -#endif static const struct irq_chip dwapb_irq_chip = { .name = DWAPB_DRIVER_NAME, @@ -390,7 +382,7 @@ static const struct irq_chip dwapb_irq_chip = { .irq_set_type = dwapb_irq_set_type, .irq_enable = dwapb_irq_enable, .irq_disable = dwapb_irq_disable, - .irq_set_wake = dwapb_irq_set_wake, + .irq_set_wake = pm_sleep_ptr(dwapb_irq_set_wake), .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; @@ -759,7 +751,6 @@ static int dwapb_gpio_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int dwapb_gpio_suspend(struct device *dev) { struct dwapb_gpio *gpio = dev_get_drvdata(dev); @@ -844,15 +835,14 @@ static int dwapb_gpio_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend, - dwapb_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, + dwapb_gpio_suspend, dwapb_gpio_resume); static struct platform_driver dwapb_gpio_driver = { .driver = { .name = DWAPB_DRIVER_NAME, - .pm = &dwapb_gpio_pm_ops, + .pm = pm_sleep_ptr(&dwapb_gpio_pm_ops), .of_match_table = dwapb_of_match, .acpi_match_table = dwapb_acpi_match, }, diff --git a/drivers/gpio/gpio-elkhartlake.c b/drivers/gpio/gpio-elkhartlake.c index 95de52d2cc63..b96e7928b6e5 100644 --- a/drivers/gpio/gpio-elkhartlake.c +++ b/drivers/gpio/gpio-elkhartlake.c @@ -2,43 +2,46 @@ /* * Intel Elkhart Lake PSE GPIO driver * - * Copyright (c) 2023 Intel Corporation. + * Copyright (c) 2023, 2025 Intel Corporation. * * Authors: Pandith N <pandith.n@intel.com> * Raag Jadav <raag.jadav@intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/device.h> #include <linux/err.h> #include <linux/module.h> -#include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/ehl_pse_io_aux.h> + #include "gpio-tangier.h" /* Each Intel EHL PSE GPIO Controller has 30 GPIO pins */ #define EHL_PSE_NGPIO 30 -static int ehl_gpio_probe(struct platform_device *pdev) +static int ehl_gpio_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { - struct device *dev = &pdev->dev; + struct device *dev = &adev->dev; + struct ehl_pse_io_data *data; struct tng_gpio *priv; - int irq, ret; + int ret; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + data = dev_get_platdata(dev); + if (!data) + return -ENODATA; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->reg_base = devm_platform_ioremap_resource(pdev, 0); + priv->reg_base = devm_ioremap_resource(dev, &data->mem); if (IS_ERR(priv->reg_base)) return PTR_ERR(priv->reg_base); priv->dev = dev; - priv->irq = irq; + priv->irq = data->irq; priv->info.base = -1; priv->info.ngpio = EHL_PSE_NGPIO; @@ -51,25 +54,24 @@ static int ehl_gpio_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "tng_gpio_probe error\n"); - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(adev, priv); return 0; } -static const struct platform_device_id ehl_gpio_ids[] = { - { "gpio-elkhartlake" }, +static const struct auxiliary_device_id ehl_gpio_ids[] = { + { EHL_PSE_IO_NAME "." EHL_PSE_GPIO_NAME }, { } }; -MODULE_DEVICE_TABLE(platform, ehl_gpio_ids); +MODULE_DEVICE_TABLE(auxiliary, ehl_gpio_ids); -static struct platform_driver ehl_gpio_driver = { +static struct auxiliary_driver ehl_gpio_driver = { .driver = { - .name = "gpio-elkhartlake", .pm = pm_sleep_ptr(&tng_gpio_pm_ops), }, .probe = ehl_gpio_probe, .id_table = ehl_gpio_ids, }; -module_platform_driver(ehl_gpio_driver); +module_auxiliary_driver(ehl_gpio_driver); MODULE_AUTHOR("Pandith N <pandith.n@intel.com>"); MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>"); diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c index 86ebc66b1104..afc1b8461dab 100644 --- a/drivers/gpio/gpio-fxl6408.c +++ b/drivers/gpio/gpio-fxl6408.c @@ -123,6 +123,8 @@ static int fxl6408_probe(struct i2c_client *client) if (ret) return ret; + i2c_set_clientdata(client, gpio_config.regmap); + /* Disable High-Z of outputs, so that our OUTPUT updates actually take effect. */ ret = regmap_write(gpio_config.regmap, FXL6408_REG_OUTPUT_HIGH_Z, 0); if (ret) @@ -131,6 +133,16 @@ static int fxl6408_probe(struct i2c_client *client) return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); } +static int fxl6408_resume(struct device *dev) +{ + struct regmap *regmap = dev_get_drvdata(dev); + + regcache_mark_dirty(regmap); + return regcache_sync(regmap); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(fxl6408_pm_ops, NULL, fxl6408_resume); + static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = { { .compatible = "fcs,fxl6408" }, { } @@ -146,6 +158,7 @@ MODULE_DEVICE_TABLE(i2c, fxl6408_id); static struct i2c_driver fxl6408_driver = { .driver = { .name = "fxl6408", + .pm = pm_sleep_ptr(&fxl6408_pm_ops), .of_match_table = fxl6408_dt_ids, }, .probe = fxl6408_probe, diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 0c0f97fa14fc..e4fa84e22726 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -46,7 +46,7 @@ /* Structure for an irq of the core - called an underlying irq */ struct grgpio_uirq { - u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */ + atomic_t refcnt; /* Reference counter to manage requesting/freeing of uirq */ u8 uirq; /* Underlying irq of the gpio driver */ }; @@ -242,30 +242,22 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, irq, offset); gpio_generic_chip_lock_irqsave(&priv->chip, flags); - - /* Request underlying irq if not already requested */ lirq->irq = irq; uirq = &priv->uirqs[lirq->index]; - if (uirq->refcnt == 0) { - /* - * FIXME: This is not how locking works at all, you can't just - * release the lock for a moment to do something that can't - * sleep... - */ - gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); + gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); + + /* Request underlying irq if not already requested */ + if (atomic_fetch_add(1, &uirq->refcnt) == 0) { 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); + atomic_dec(&uirq->refcnt); /* rollback */ return ret; } - gpio_generic_chip_lock_irqsave(&priv->chip, flags); } - uirq->refcnt++; - - gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); /* Setup irq */ irq_set_chip_data(irq, priv); @@ -306,8 +298,7 @@ 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 (atomic_dec_and_test(&uirq->refcnt)) { gpio_generic_chip_unlock_irqrestore(&priv->chip, flags); free_irq(uirq->uirq, priv); return; @@ -434,6 +425,7 @@ static int grgpio_probe(struct platform_device *ofdev) continue; } priv->uirqs[lirq->index].uirq = ret; + atomic_set(&priv->uirqs[lirq->index].refcnt, 0); } } diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 2eaed83214d8..72935d6dbebf 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -364,21 +364,20 @@ static int __init egpio_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int egpio_suspend(struct platform_device *pdev, pm_message_t state) +static int egpio_suspend(struct device *dev) { - struct egpio_info *ei = platform_get_drvdata(pdev); + struct egpio_info *ei = dev_get_drvdata(dev); - if (ei->chained_irq && device_may_wakeup(&pdev->dev)) + if (ei->chained_irq && device_may_wakeup(dev)) enable_irq_wake(ei->chained_irq); return 0; } -static int egpio_resume(struct platform_device *pdev) +static int egpio_resume(struct device *dev) { - struct egpio_info *ei = platform_get_drvdata(pdev); + struct egpio_info *ei = dev_get_drvdata(dev); - if (ei->chained_irq && device_may_wakeup(&pdev->dev)) + if (ei->chained_irq && device_may_wakeup(dev)) disable_irq_wake(ei->chained_irq); /* Update registers from the cache, in case @@ -386,19 +385,15 @@ static int egpio_resume(struct platform_device *pdev) egpio_write_cache(ei); return 0; } -#else -#define egpio_suspend NULL -#define egpio_resume NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(egpio_pm_ops, egpio_suspend, egpio_resume); static struct platform_driver egpio_driver = { .driver = { .name = "htc-egpio", .suppress_bind_attrs = true, + .pm = pm_sleep_ptr(&egpio_pm_ops), }, - .suspend = egpio_suspend, - .resume = egpio_resume, }; static int __init egpio_init(void) diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c index 0103be977c66..4fbae6f6a497 100644 --- a/drivers/gpio/gpio-idio-16.c +++ b/drivers/gpio/gpio-idio-16.c @@ -6,6 +6,7 @@ #define DEFAULT_SYMBOL_NAMESPACE "GPIO_IDIO_16" +#include <linux/bitmap.h> #include <linux/bits.h> #include <linux/device.h> #include <linux/err.h> @@ -107,6 +108,7 @@ int devm_idio_16_regmap_register(struct device *const dev, struct idio_16_data *data; struct regmap_irq_chip *chip; struct regmap_irq_chip_data *chip_data; + DECLARE_BITMAP(fixed_direction_output, IDIO_16_NGPIO); if (!config->parent) return -EINVAL; @@ -164,6 +166,9 @@ int devm_idio_16_regmap_register(struct device *const dev, gpio_config.irq_domain = regmap_irq_get_domain(chip_data); gpio_config.reg_mask_xlate = idio_16_reg_mask_xlate; + bitmap_from_u64(fixed_direction_output, GENMASK_U64(15, 0)); + gpio_config.fixed_direction_output = fixed_direction_output; + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); } EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register); diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index c64aaa896766..452a9ce61488 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -48,8 +48,6 @@ #include <linux/property.h> #include <linux/delay.h> -#include "gpiolib.h" - struct gpio_latch_priv { struct gpio_chip gc; struct gpio_descs *clk_gpios; diff --git a/drivers/gpio/gpio-ljca.c b/drivers/gpio/gpio-ljca.c index 3b4f8830c741..f32d1d237795 100644 --- a/drivers/gpio/gpio-ljca.c +++ b/drivers/gpio/gpio-ljca.c @@ -286,22 +286,14 @@ static void ljca_gpio_event_cb(void *context, u8 cmd, const void *evt_data, { const struct ljca_gpio_packet *packet = evt_data; struct ljca_gpio_dev *ljca_gpio = context; - int i, irq; + int i; if (cmd != LJCA_GPIO_INT_EVENT) return; for (i = 0; i < packet->num; i++) { - irq = irq_find_mapping(ljca_gpio->gc.irq.domain, - packet->item[i].index); - if (!irq) { - dev_err(ljca_gpio->gc.parent, - "gpio_id %u does not mapped to IRQ yet\n", - packet->item[i].index); - return; - } - - generic_handle_domain_irq(ljca_gpio->gc.irq.domain, irq); + generic_handle_domain_irq(ljca_gpio->gc.irq.domain, + packet->item[i].index); set_bit(packet->item[i].index, ljca_gpio->reenable_irqs); } diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index 02f181cb219e..77d07e31366f 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -312,6 +312,7 @@ static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio lgpio->chip.gc.direction_output = loongson_gpio_direction_output; lgpio->chip.gc.set = loongson_gpio_set; lgpio->chip.gc.parent = &pdev->dev; + lgpio->chip.gc.base = -1; spin_lock_init(&lgpio->lock); } @@ -407,11 +408,11 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0 = { static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { .label = "ls2k2000_gpio", - .mode = BIT_CTRL_MODE, - .conf_offset = 0x0, - .in_offset = 0x20, - .out_offset = 0x10, - .inten_offset = 0x30, + .mode = BYTE_CTRL_MODE, + .conf_offset = 0x800, + .in_offset = 0xa00, + .out_offset = 0x900, + .inten_offset = 0xb00, }; static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = { diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c index da2bf9381cc4..52b13c6ae496 100644 --- a/drivers/gpio/gpio-menz127.c +++ b/drivers/gpio/gpio-menz127.c @@ -24,6 +24,11 @@ #define MEN_Z127_ODER 0x1C #define GPIO_TO_DBCNT_REG(gpio) ((gpio * 4) + 0x80) +/* MEN Z127 supported model ids*/ +#define MEN_Z127_ID 0x7f +#define MEN_Z034_ID 0x22 +#define MEN_Z037_ID 0x25 + #define MEN_Z127_DB_MIN_US 50 /* 16 bit compare register. Each bit represents 50us */ #define MEN_Z127_DB_MAX_US (0xffff * MEN_Z127_DB_MIN_US) @@ -140,6 +145,7 @@ static int men_z127_probe(struct mcb_device *mdev, struct men_z127_gpio *men_z127_gpio; struct device *dev = &mdev->dev; int ret; + unsigned long sz; men_z127_gpio = devm_kzalloc(dev, sizeof(struct men_z127_gpio), GFP_KERNEL); @@ -163,9 +169,21 @@ static int men_z127_probe(struct mcb_device *mdev, mcb_set_drvdata(mdev, men_z127_gpio); + switch (mdev->id) { + case MEN_Z127_ID: + sz = 4; + break; + case MEN_Z034_ID: + case MEN_Z037_ID: + sz = 1; + break; + default: + return dev_err_probe(&mdev->dev, -EINVAL, "no size found for id %d", mdev->id); + } + config = (struct gpio_generic_chip_config) { .dev = &mdev->dev, - .sz = 4, + .sz = sz, .dat = men_z127_gpio->reg_base + MEN_Z127_PSR, .set = men_z127_gpio->reg_base + MEN_Z127_CTRL, .dirout = men_z127_gpio->reg_base + MEN_Z127_GPIODR, @@ -186,7 +204,9 @@ static int men_z127_probe(struct mcb_device *mdev, } static const struct mcb_device_id men_z127_ids[] = { - { .device = 0x7f }, + { .device = MEN_Z127_ID }, + { .device = MEN_Z034_ID }, + { .device = MEN_Z037_ID }, { } }; MODULE_DEVICE_TABLE(mcb, men_z127_ids); @@ -201,7 +221,7 @@ static struct mcb_driver men_z127_driver = { module_mcb_driver(men_z127_driver); MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); -MODULE_DESCRIPTION("MEN 16z127 GPIO Controller"); +MODULE_DESCRIPTION("MEN GPIO Controller"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mcb:16z127"); MODULE_IMPORT_NS("MCB"); diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index f6af81bf2b13..6576e5dcb0ee 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -160,7 +160,7 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) /* * Save register configuration and disable interrupts. */ -static void __maybe_unused ioh_gpio_save_reg_conf(struct ioh_gpio *chip) +static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) { int i; @@ -186,7 +186,7 @@ static void __maybe_unused ioh_gpio_save_reg_conf(struct ioh_gpio *chip) /* * This function restores the register configuration of the GPIO device. */ -static void __maybe_unused ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) +static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) { int i; @@ -479,7 +479,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, return 0; } -static int __maybe_unused ioh_gpio_suspend(struct device *dev) +static int ioh_gpio_suspend(struct device *dev) { struct ioh_gpio *chip = dev_get_drvdata(dev); unsigned long flags; @@ -491,7 +491,7 @@ static int __maybe_unused ioh_gpio_suspend(struct device *dev) return 0; } -static int __maybe_unused ioh_gpio_resume(struct device *dev) +static int ioh_gpio_resume(struct device *dev) { struct ioh_gpio *chip = dev_get_drvdata(dev); unsigned long flags; @@ -505,7 +505,7 @@ static int __maybe_unused ioh_gpio_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(ioh_gpio_pm_ops, ioh_gpio_suspend, ioh_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ioh_gpio_pm_ops, ioh_gpio_suspend, ioh_gpio_resume); static const struct pci_device_id ioh_gpio_pcidev_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) }, @@ -518,7 +518,7 @@ static struct pci_driver ioh_gpio_driver = { .id_table = ioh_gpio_pcidev_id, .probe = ioh_gpio_probe, .driver = { - .pm = &ioh_gpio_pm_ops, + .pm = pm_sleep_ptr(&ioh_gpio_pm_ops), }, }; diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index abffce3894fc..6668686a28ff 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -424,7 +424,7 @@ mlxbf2_gpio_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev) +static int mlxbf2_gpio_suspend(struct device *dev) { struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); @@ -436,7 +436,7 @@ static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev) return 0; } -static int __maybe_unused mlxbf2_gpio_resume(struct device *dev) +static int mlxbf2_gpio_resume(struct device *dev) { struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev); @@ -447,7 +447,7 @@ static int __maybe_unused mlxbf2_gpio_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume); static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = { { "MLNXBF22", 0 }, @@ -459,7 +459,7 @@ static struct platform_driver mlxbf2_gpio_driver = { .driver = { .name = "mlxbf2_gpio", .acpi_match_table = mlxbf2_gpio_acpi_match, - .pm = &mlxbf2_pm_ops, + .pm = pm_sleep_ptr(&mlxbf2_pm_ops), }, .probe = mlxbf2_gpio_probe, }; diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index 8f1405733d98..1bd98c50a459 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -10,7 +10,6 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/gpio/driver.h> -#include <linux/gpio/legacy-of-mm-gpiochip.h> #include <linux/of.h> #include <linux/io.h> #include <linux/slab.h> @@ -27,7 +26,8 @@ #define LTQ_EBU_WP 0x80000000 /* write protect bit */ struct ltq_mm { - struct of_mm_gpio_chip mmchip; + struct gpio_chip gc; + void __iomem *regs; u16 shadow; /* shadow the latches state */ }; @@ -44,7 +44,7 @@ static void ltq_mm_apply(struct ltq_mm *chip) spin_lock_irqsave(&ebu_lock, flags); ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1); - __raw_writew(chip->shadow, chip->mmchip.regs); + __raw_writew(chip->shadow, chip->regs); ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1); spin_unlock_irqrestore(&ebu_lock, flags); } @@ -52,8 +52,8 @@ static void ltq_mm_apply(struct ltq_mm *chip) /** * ltq_mm_set() - gpio_chip->set - set gpios. * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. + * @offset: GPIO signal number. + * @value: Value to be written to specified signal. * * Set the shadow value and call ltq_mm_apply. Always returns 0. */ @@ -73,8 +73,8 @@ static int ltq_mm_set(struct gpio_chip *gc, unsigned int offset, int value) /** * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction. * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. + * @offset: GPIO signal number. + * @value: Value to be written to specified signal. * * Same as ltq_mm_set, always returns 0. */ @@ -85,21 +85,21 @@ static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value) /** * ltq_mm_save_regs() - Set initial values of GPIO pins - * @mm_gc: pointer to memory mapped GPIO chip structure + * @chip: Pointer to our private data structure. */ -static void ltq_mm_save_regs(struct of_mm_gpio_chip *mm_gc) +static void ltq_mm_save_regs(struct ltq_mm *chip) { - struct ltq_mm *chip = - container_of(mm_gc, struct ltq_mm, mmchip); - /* tell the ebu controller which memory address we will be using */ - ltq_ebu_w32(CPHYSADDR(chip->mmchip.regs) | 0x1, LTQ_EBU_ADDRSEL1); + ltq_ebu_w32(CPHYSADDR((__force void *)chip->regs) | 0x1, LTQ_EBU_ADDRSEL1); ltq_mm_apply(chip); } static int ltq_mm_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct gpio_chip *gc; struct ltq_mm *chip; u32 shadow; @@ -107,25 +107,29 @@ static int ltq_mm_probe(struct platform_device *pdev) if (!chip) return -ENOMEM; - platform_set_drvdata(pdev, chip); + gc = &chip->gc; + + gc->base = -1; + gc->ngpio = 16; + gc->direction_output = ltq_mm_dir_out; + gc->set = ltq_mm_set; + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); + if (!gc->label) + return -ENOMEM; + + chip->regs = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(chip->regs)) + return PTR_ERR(chip->regs); - chip->mmchip.gc.ngpio = 16; - chip->mmchip.gc.direction_output = ltq_mm_dir_out; - chip->mmchip.gc.set = ltq_mm_set; - chip->mmchip.save_regs = ltq_mm_save_regs; + ltq_mm_save_regs(chip); /* store the shadow value if one was passed by the devicetree */ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow)) chip->shadow = shadow; - return of_mm_gpiochip_add_data(pdev->dev.of_node, &chip->mmchip, chip); -} - -static void ltq_mm_remove(struct platform_device *pdev) -{ - struct ltq_mm *chip = platform_get_drvdata(pdev); - - of_mm_gpiochip_remove(&chip->mmchip); + return devm_gpiochip_add_data(dev, gc, chip); } static const struct of_device_id ltq_mm_match[] = { @@ -136,7 +140,6 @@ MODULE_DEVICE_TABLE(of, ltq_mm_match); static struct platform_driver ltq_mm_driver = { .probe = ltq_mm_probe, - .remove = ltq_mm_remove, .driver = { .name = "gpio-mm-ltq", .of_match_table = ltq_mm_match, diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 7d6dd36cf1ae..b3a26a06260b 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -41,6 +41,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/compiler.h> #include <linux/err.h> #include <linux/init.h> @@ -61,69 +62,69 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #include "gpiolib.h" -static void bgpio_write8(void __iomem *reg, unsigned long data) +static void gpio_mmio_write8(void __iomem *reg, unsigned long data) { writeb(data, reg); } -static unsigned long bgpio_read8(void __iomem *reg) +static unsigned long gpio_mmio_read8(void __iomem *reg) { return readb(reg); } -static void bgpio_write16(void __iomem *reg, unsigned long data) +static void gpio_mmio_write16(void __iomem *reg, unsigned long data) { writew(data, reg); } -static unsigned long bgpio_read16(void __iomem *reg) +static unsigned long gpio_mmio_read16(void __iomem *reg) { return readw(reg); } -static void bgpio_write32(void __iomem *reg, unsigned long data) +static void gpio_mmio_write32(void __iomem *reg, unsigned long data) { writel(data, reg); } -static unsigned long bgpio_read32(void __iomem *reg) +static unsigned long gpio_mmio_read32(void __iomem *reg) { return readl(reg); } #if BITS_PER_LONG >= 64 -static void bgpio_write64(void __iomem *reg, unsigned long data) +static void gpio_mmio_write64(void __iomem *reg, unsigned long data) { writeq(data, reg); } -static unsigned long bgpio_read64(void __iomem *reg) +static unsigned long gpio_mmio_read64(void __iomem *reg) { return readq(reg); } #endif /* BITS_PER_LONG >= 64 */ -static void bgpio_write16be(void __iomem *reg, unsigned long data) +static void gpio_mmio_write16be(void __iomem *reg, unsigned long data) { iowrite16be(data, reg); } -static unsigned long bgpio_read16be(void __iomem *reg) +static unsigned long gpio_mmio_read16be(void __iomem *reg) { return ioread16be(reg); } -static void bgpio_write32be(void __iomem *reg, unsigned long data) +static void gpio_mmio_write32be(void __iomem *reg, unsigned long data) { iowrite32be(data, reg); } -static unsigned long bgpio_read32be(void __iomem *reg) +static unsigned long gpio_mmio_read32be(void __iomem *reg) { return ioread32be(reg); } -static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line) +static unsigned long gpio_mmio_line2mask(struct gpio_chip *gc, unsigned int line) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); @@ -132,10 +133,10 @@ static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line) return BIT(line); } -static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) +static int gpio_mmio_get_set(struct gpio_chip *gc, unsigned int gpio) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long pinmask = bgpio_line2mask(gc, gpio); + unsigned long pinmask = gpio_mmio_line2mask(gc, gpio); bool dir = !!(chip->sdir & pinmask); if (dir) @@ -148,8 +149,8 @@ static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) * This assumes that the bits in the GPIO register are in native endianness. * We only assign the function pointer if we have that. */ -static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); unsigned long get_mask = 0, set_mask = 0; @@ -168,18 +169,18 @@ static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask, return 0; } -static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) +static int gpio_mmio_get(struct gpio_chip *gc, unsigned int gpio) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - return !!(chip->read_reg(chip->reg_dat) & bgpio_line2mask(gc, gpio)); + return !!(chip->read_reg(chip->reg_dat) & gpio_mmio_line2mask(gc, gpio)); } /* * This only works if the bits in the GPIO register are in native endianness. */ -static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_get_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); @@ -192,8 +193,8 @@ static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, /* * With big endian mirrored bit order it becomes more tedious. */ -static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); unsigned long readmask = 0; @@ -205,7 +206,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, /* Create a mirrored mask */ for_each_set_bit(bit, mask, gc->ngpio) - readmask |= bgpio_line2mask(gc, bit); + readmask |= gpio_mmio_line2mask(gc, bit); /* Read the register */ val = chip->read_reg(chip->reg_dat) & readmask; @@ -215,23 +216,22 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, * in bit 0 ... line 31 in bit 31 for a 32bit register. */ for_each_set_bit(bit, &val, gc->ngpio) - *bits |= bgpio_line2mask(gc, bit); + *bits |= gpio_mmio_line2mask(gc, bit); return 0; } -static int bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) +static int gpio_mmio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) { return 0; } -static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int gpio_mmio_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long mask = bgpio_line2mask(gc, gpio); - unsigned long flags; + unsigned long mask = gpio_mmio_line2mask(gc, gpio); - raw_spin_lock_irqsave(&chip->lock, flags); + guard(raw_spinlock)(&chip->lock); if (val) chip->sdata |= mask; @@ -240,16 +240,14 @@ static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) chip->write_reg(chip->reg_dat, chip->sdata); - raw_spin_unlock_irqrestore(&chip->lock, flags); - return 0; } -static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, + int val) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long mask = bgpio_line2mask(gc, gpio); + unsigned long mask = gpio_mmio_line2mask(gc, gpio); if (val) chip->write_reg(chip->reg_set, mask); @@ -259,12 +257,12 @@ static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, return 0; } -static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int gpio_mmio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long mask = bgpio_line2mask(gc, gpio), flags; + unsigned long mask = gpio_mmio_line2mask(gc, gpio); - raw_spin_lock_irqsave(&chip->lock, flags); + guard(raw_spinlock)(&chip->lock); if (val) chip->sdata |= mask; @@ -273,15 +271,14 @@ static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) chip->write_reg(chip->reg_set, chip->sdata); - raw_spin_unlock_irqrestore(&chip->lock, flags); - return 0; } -static void bgpio_multiple_get_masks(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits, - unsigned long *set_mask, - unsigned long *clear_mask) +static void gpio_mmio_multiple_get_masks(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits, + unsigned long *set_mask, + unsigned long *clear_mask) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); int i; @@ -291,60 +288,58 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc, for_each_set_bit(i, mask, chip->bits) { if (test_bit(i, bits)) - *set_mask |= bgpio_line2mask(gc, i); + *set_mask |= gpio_mmio_line2mask(gc, i); else - *clear_mask |= bgpio_line2mask(gc, i); + *clear_mask |= gpio_mmio_line2mask(gc, i); } } -static void bgpio_set_multiple_single_reg(struct gpio_chip *gc, - unsigned long *mask, - unsigned long *bits, - void __iomem *reg) +static void gpio_mmio_set_multiple_single_reg(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits, + void __iomem *reg) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long flags, set_mask, clear_mask; + unsigned long set_mask, clear_mask; - raw_spin_lock_irqsave(&chip->lock, flags); + guard(raw_spinlock)(&chip->lock); - bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); + gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); chip->sdata |= set_mask; chip->sdata &= ~clear_mask; chip->write_reg(reg, chip->sdata); - - raw_spin_unlock_irqrestore(&chip->lock, flags); } -static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_dat); + gpio_mmio_set_multiple_single_reg(gc, mask, bits, chip->reg_dat); return 0; } -static int bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_set); + gpio_mmio_set_multiple_single_reg(gc, mask, bits, chip->reg_set); return 0; } -static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, - unsigned long *mask, - unsigned long *bits) +static int gpio_mmio_set_multiple_with_clear(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); unsigned long set_mask, clear_mask; - bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); + gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); if (set_mask) chip->write_reg(chip->reg_set, set_mask); @@ -354,7 +349,8 @@ static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, return 0; } -static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out) +static int gpio_mmio_dir_return(struct gpio_chip *gc, unsigned int gpio, + bool dir_out) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); @@ -367,131 +363,125 @@ static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_ou return pinctrl_gpio_direction_input(gc, gpio); } -static int bgpio_dir_in_err(struct gpio_chip *gc, unsigned int gpio) +static int gpio_mmio_dir_in_err(struct gpio_chip *gc, unsigned int gpio) { return -EINVAL; } -static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) +static int gpio_mmio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) { - return bgpio_dir_return(gc, gpio, false); + return gpio_mmio_dir_return(gc, gpio, false); } -static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, + int val) { return -EINVAL; } -static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, + int val) { gc->set(gc, gpio, val); - return bgpio_dir_return(gc, gpio, true); + return gpio_mmio_dir_return(gc, gpio, true); } -static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long flags; - raw_spin_lock_irqsave(&chip->lock, flags); + scoped_guard(raw_spinlock, &chip->lock) { + chip->sdir &= ~gpio_mmio_line2mask(gc, gpio); - chip->sdir &= ~bgpio_line2mask(gc, gpio); - - if (chip->reg_dir_in) - chip->write_reg(chip->reg_dir_in, ~chip->sdir); - if (chip->reg_dir_out) - chip->write_reg(chip->reg_dir_out, chip->sdir); - - raw_spin_unlock_irqrestore(&chip->lock, flags); + if (chip->reg_dir_in) + chip->write_reg(chip->reg_dir_in, ~chip->sdir); + if (chip->reg_dir_out) + chip->write_reg(chip->reg_dir_out, chip->sdir); + } - return bgpio_dir_return(gc, gpio, false); + return gpio_mmio_dir_return(gc, gpio, false); } -static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) +static int gpio_mmio_get_dir(struct gpio_chip *gc, unsigned int gpio) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); /* Return 0 if output, 1 if input */ if (chip->dir_unreadable) { - if (chip->sdir & bgpio_line2mask(gc, gpio)) + if (chip->sdir & gpio_mmio_line2mask(gc, gpio)) return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_IN; } if (chip->reg_dir_out) { - if (chip->read_reg(chip->reg_dir_out) & bgpio_line2mask(gc, gpio)) + if (chip->read_reg(chip->reg_dir_out) & gpio_mmio_line2mask(gc, gpio)) return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_IN; } if (chip->reg_dir_in) - if (!(chip->read_reg(chip->reg_dir_in) & bgpio_line2mask(gc, gpio))) + if (!(chip->read_reg(chip->reg_dir_in) & gpio_mmio_line2mask(gc, gpio))) return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_IN; } -static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +static void gpio_mmio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); - unsigned long flags; - raw_spin_lock_irqsave(&chip->lock, flags); + guard(raw_spinlock)(&chip->lock); - chip->sdir |= bgpio_line2mask(gc, gpio); + chip->sdir |= gpio_mmio_line2mask(gc, gpio); if (chip->reg_dir_in) chip->write_reg(chip->reg_dir_in, ~chip->sdir); if (chip->reg_dir_out) chip->write_reg(chip->reg_dir_out, chip->sdir); - - raw_spin_unlock_irqrestore(&chip->lock, flags); } -static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, + int val) { - bgpio_dir_out(gc, gpio, val); + gpio_mmio_dir_out(gc, gpio, val); gc->set(gc, gpio, val); - return bgpio_dir_return(gc, gpio, true); + return gpio_mmio_dir_return(gc, gpio, true); } -static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, - int val) +static int gpio_mmio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, + int val) { gc->set(gc, gpio, val); - bgpio_dir_out(gc, gpio, val); - return bgpio_dir_return(gc, gpio, true); + gpio_mmio_dir_out(gc, gpio, val); + return gpio_mmio_dir_return(gc, gpio, true); } -static int bgpio_setup_accessors(struct device *dev, - struct gpio_generic_chip *chip, - bool byte_be) +static int gpio_mmio_setup_accessors(struct device *dev, + struct gpio_generic_chip *chip, + bool byte_be) { switch (chip->bits) { case 8: - chip->read_reg = bgpio_read8; - chip->write_reg = bgpio_write8; + chip->read_reg = gpio_mmio_read8; + chip->write_reg = gpio_mmio_write8; break; case 16: if (byte_be) { - chip->read_reg = bgpio_read16be; - chip->write_reg = bgpio_write16be; + chip->read_reg = gpio_mmio_read16be; + chip->write_reg = gpio_mmio_write16be; } else { - chip->read_reg = bgpio_read16; - chip->write_reg = bgpio_write16; + chip->read_reg = gpio_mmio_read16; + chip->write_reg = gpio_mmio_write16; } break; case 32: if (byte_be) { - chip->read_reg = bgpio_read32be; - chip->write_reg = bgpio_write32be; + chip->read_reg = gpio_mmio_read32be; + chip->write_reg = gpio_mmio_write32be; } else { - chip->read_reg = bgpio_read32; - chip->write_reg = bgpio_write32; + chip->read_reg = gpio_mmio_read32; + chip->write_reg = gpio_mmio_write32; } break; #if BITS_PER_LONG >= 64 @@ -501,8 +491,8 @@ static int bgpio_setup_accessors(struct device *dev, "64 bit big endian byte order unsupported\n"); return -EINVAL; } else { - chip->read_reg = bgpio_read64; - chip->write_reg = bgpio_write64; + chip->read_reg = gpio_mmio_read64; + chip->write_reg = gpio_mmio_write64; } break; #endif /* BITS_PER_LONG >= 64 */ @@ -536,8 +526,8 @@ static int bgpio_setup_accessors(struct device *dev, * - an input direction register (named "dirin") where a 1 bit indicates * the GPIO is an input. */ -static int bgpio_setup_io(struct gpio_generic_chip *chip, - const struct gpio_generic_chip_config *cfg) +static int gpio_mmio_setup_io(struct gpio_generic_chip *chip, + const struct gpio_generic_chip_config *cfg) { struct gpio_chip *gc = &chip->gc; @@ -548,25 +538,25 @@ static int bgpio_setup_io(struct gpio_generic_chip *chip, if (cfg->set && cfg->clr) { chip->reg_set = cfg->set; chip->reg_clr = cfg->clr; - gc->set = bgpio_set_with_clear; - gc->set_multiple = bgpio_set_multiple_with_clear; + gc->set = gpio_mmio_set_with_clear; + gc->set_multiple = gpio_mmio_set_multiple_with_clear; } else if (cfg->set && !cfg->clr) { chip->reg_set = cfg->set; - gc->set = bgpio_set_set; - gc->set_multiple = bgpio_set_multiple_set; + gc->set = gpio_mmio_set_set; + gc->set_multiple = gpio_mmio_set_multiple_set; } else if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) { - gc->set = bgpio_set_none; + gc->set = gpio_mmio_set_none; gc->set_multiple = NULL; } else { - gc->set = bgpio_set; - gc->set_multiple = bgpio_set_multiple; + gc->set = gpio_mmio_set; + gc->set_multiple = gpio_mmio_set_multiple; } if (!(cfg->flags & GPIO_GENERIC_UNREADABLE_REG_SET) && (cfg->flags & GPIO_GENERIC_READ_OUTPUT_REG_SET)) { - gc->get = bgpio_get_set; + gc->get = gpio_mmio_get_set; if (!chip->be_bits) - gc->get_multiple = bgpio_get_set_multiple; + gc->get_multiple = gpio_mmio_get_set_multiple; /* * We deliberately avoid assigning the ->get_multiple() call * for big endian mirrored registers which are ALSO reflecting @@ -575,18 +565,18 @@ static int bgpio_setup_io(struct gpio_generic_chip *chip, * reading each line individually in that fringe case. */ } else { - gc->get = bgpio_get; + gc->get = gpio_mmio_get; if (chip->be_bits) - gc->get_multiple = bgpio_get_multiple_be; + gc->get_multiple = gpio_mmio_get_multiple_be; else - gc->get_multiple = bgpio_get_multiple; + gc->get_multiple = gpio_mmio_get_multiple; } return 0; } -static int bgpio_setup_direction(struct gpio_generic_chip *chip, - const struct gpio_generic_chip_config *cfg) +static int gpio_mmio_setup_direction(struct gpio_generic_chip *chip, + const struct gpio_generic_chip_config *cfg) { struct gpio_chip *gc = &chip->gc; @@ -594,27 +584,27 @@ static int bgpio_setup_direction(struct gpio_generic_chip *chip, chip->reg_dir_out = cfg->dirout; chip->reg_dir_in = cfg->dirin; if (cfg->flags & GPIO_GENERIC_NO_SET_ON_INPUT) - gc->direction_output = bgpio_dir_out_dir_first; + gc->direction_output = gpio_mmio_dir_out_dir_first; else - gc->direction_output = bgpio_dir_out_val_first; - gc->direction_input = bgpio_dir_in; - gc->get_direction = bgpio_get_dir; + gc->direction_output = gpio_mmio_dir_out_val_first; + gc->direction_input = gpio_mmio_dir_in; + gc->get_direction = gpio_mmio_get_dir; } else { if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) - gc->direction_output = bgpio_dir_out_err; + gc->direction_output = gpio_mmio_dir_out_err; else - gc->direction_output = bgpio_simple_dir_out; + gc->direction_output = gpio_mmio_simple_dir_out; if (cfg->flags & GPIO_GENERIC_NO_INPUT) - gc->direction_input = bgpio_dir_in_err; + gc->direction_input = gpio_mmio_dir_in_err; else - gc->direction_input = bgpio_simple_dir_in; + gc->direction_input = gpio_mmio_simple_dir_in; } return 0; } -static int bgpio_request(struct gpio_chip *gc, unsigned int gpio_pin) +static int gpio_mmio_request(struct gpio_chip *gc, unsigned int gpio_pin) { struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); @@ -653,23 +643,23 @@ int gpio_generic_chip_init(struct gpio_generic_chip *chip, gc->parent = dev; gc->label = dev_name(dev); gc->base = -1; - gc->request = bgpio_request; + gc->request = gpio_mmio_request; chip->be_bits = !!(flags & GPIO_GENERIC_BIG_ENDIAN); ret = gpiochip_get_ngpios(gc, dev); if (ret) gc->ngpio = chip->bits; - ret = bgpio_setup_io(chip, cfg); + ret = gpio_mmio_setup_io(chip, cfg); if (ret) return ret; - ret = bgpio_setup_accessors(dev, chip, + ret = gpio_mmio_setup_accessors(dev, chip, flags & GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER); if (ret) return ret; - ret = bgpio_setup_direction(chip, cfg); + ret = gpio_mmio_setup_direction(chip, cfg); if (ret) return ret; @@ -680,7 +670,7 @@ int gpio_generic_chip_init(struct gpio_generic_chip *chip, } chip->sdata = chip->read_reg(chip->reg_dat); - if (gc->set == bgpio_set_set && + if (gc->set == gpio_mmio_set_set && !(flags & GPIO_GENERIC_UNREADABLE_REG_SET)) chip->sdata = chip->read_reg(chip->reg_set); @@ -712,9 +702,8 @@ EXPORT_SYMBOL_GPL(gpio_generic_chip_init); #if IS_ENABLED(CONFIG_GPIO_GENERIC_PLATFORM) -static void __iomem *bgpio_map(struct platform_device *pdev, - const char *name, - resource_size_t sane_sz) +static void __iomem *gpio_mmio_map(struct platform_device *pdev, + const char *name, resource_size_t sane_sz) { struct resource *r; resource_size_t sz; @@ -730,16 +719,16 @@ static void __iomem *bgpio_map(struct platform_device *pdev, return devm_ioremap_resource(&pdev->dev, r); } -static const struct of_device_id bgpio_of_match[] = { +static const struct of_device_id gpio_mmio_of_match[] = { { .compatible = "brcm,bcm6345-gpio" }, { .compatible = "wd,mbl-gpio" }, { .compatible = "ni,169445-nand-gpio" }, { .compatible = "intel,ixp4xx-expansion-bus-mmio-gpio" }, { } }; -MODULE_DEVICE_TABLE(of, bgpio_of_match); +MODULE_DEVICE_TABLE(of, gpio_mmio_of_match); -static int bgpio_pdev_probe(struct platform_device *pdev) +static int gpio_mmio_pdev_probe(struct platform_device *pdev) { struct gpio_generic_chip_config config; struct gpio_generic_chip *gen_gc; @@ -762,23 +751,23 @@ static int bgpio_pdev_probe(struct platform_device *pdev) sz = resource_size(r); - dat = bgpio_map(pdev, "dat", sz); + dat = gpio_mmio_map(pdev, "dat", sz); if (IS_ERR(dat)) return PTR_ERR(dat); - set = bgpio_map(pdev, "set", sz); + set = gpio_mmio_map(pdev, "set", sz); if (IS_ERR(set)) return PTR_ERR(set); - clr = bgpio_map(pdev, "clr", sz); + clr = gpio_mmio_map(pdev, "clr", sz); if (IS_ERR(clr)) return PTR_ERR(clr); - dirout = bgpio_map(pdev, "dirout", sz); + dirout = gpio_mmio_map(pdev, "dirout", sz); if (IS_ERR(dirout)) return PTR_ERR(dirout); - dirin = bgpio_map(pdev, "dirin", sz); + dirin = gpio_mmio_map(pdev, "dirin", sz); if (IS_ERR(dirin)) return PTR_ERR(dirin); @@ -824,25 +813,25 @@ static int bgpio_pdev_probe(struct platform_device *pdev) return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL); } -static const struct platform_device_id bgpio_id_table[] = { +static const struct platform_device_id gpio_mmio_id_table[] = { { .name = "basic-mmio-gpio", .driver_data = 0, }, { } }; -MODULE_DEVICE_TABLE(platform, bgpio_id_table); +MODULE_DEVICE_TABLE(platform, gpio_mmio_id_table); -static struct platform_driver bgpio_driver = { +static struct platform_driver gpio_mmio_driver = { .driver = { .name = "basic-mmio-gpio", - .of_match_table = bgpio_of_match, + .of_match_table = gpio_mmio_of_match, }, - .id_table = bgpio_id_table, - .probe = bgpio_pdev_probe, + .id_table = gpio_mmio_id_table, + .probe = gpio_mmio_pdev_probe, }; -module_platform_driver(bgpio_driver); +module_platform_driver(gpio_mmio_driver); #endif /* CONFIG_GPIO_GENERIC_PLATFORM */ diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 9f42bb30b4ec..ace652ba4df1 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -10,6 +10,7 @@ #include <linux/cleanup.h> #include <linux/gpio/driver.h> #include <linux/mutex.h> +#include <linux/spinlock.h> #include <linux/usb.h> struct mpsse_priv { @@ -17,8 +18,10 @@ struct mpsse_priv { struct usb_device *udev; /* USB device encompassing all MPSSEs */ struct usb_interface *intf; /* USB interface for this MPSSE */ u8 intf_id; /* USB interface number for this MPSSE */ - struct work_struct irq_work; /* polling work thread */ + struct list_head workers; /* polling work threads */ struct mutex irq_mutex; /* lock over irq_data */ + struct mutex irq_race; /* race for polling worker teardown */ + raw_spinlock_t irq_spin; /* protects worker list */ atomic_t irq_type[16]; /* pin -> edge detection type */ atomic_t irq_enabled; int id; @@ -26,6 +29,9 @@ struct mpsse_priv { u8 gpio_outputs[2]; /* Output states for GPIOs [L, H] */ u8 gpio_dir[2]; /* Directions for GPIOs [L, H] */ + unsigned long dir_in; /* Bitmask of valid input pins */ + unsigned long dir_out; /* Bitmask of valid output pins */ + u8 *bulk_in_buf; /* Extra recv buffer to grab status bytes */ struct usb_endpoint_descriptor *bulk_in; @@ -34,6 +40,14 @@ struct mpsse_priv { struct mutex io_mutex; /* sync I/O with disconnect */ }; +struct mpsse_worker { + struct mpsse_priv *priv; + struct work_struct work; + atomic_t cancelled; + struct list_head list; /* linked list */ + struct list_head destroy; /* teardown linked list */ +}; + struct bulk_desc { bool tx; /* direction of bulk transfer */ u8 *data; /* input (tx) or output (rx) */ @@ -43,8 +57,27 @@ struct bulk_desc { int timeout; }; +#define MPSSE_NGPIO 16 + +struct mpsse_quirk { + const char *names[MPSSE_NGPIO]; /* Pin names, if applicable */ + unsigned long dir_in; /* Bitmask of valid input pins */ + unsigned long dir_out; /* Bitmask of valid output pins */ +}; + +static struct mpsse_quirk bryx_brik_quirk = { + .names = { + [3] = "Push to Talk", + [5] = "Channel Activity", + }, + .dir_out = BIT(3), /* Push to Talk */ + .dir_in = BIT(5), /* Channel Activity */ +}; + static const struct usb_device_id gpio_mpsse_table[] = { { USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */ + { USB_DEVICE(0x0403, 0x6988), /* FTDI, assigned to Bryx */ + .driver_info = (kernel_ulong_t)&bryx_brik_quirk}, { } /* Terminating entry */ }; @@ -160,6 +193,32 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank) return buf; } +static int mpsse_ensure_supported(struct gpio_chip *chip, + unsigned long mask, int direction) +{ + unsigned long supported, unsupported; + char *type = "input"; + struct mpsse_priv *priv = gpiochip_get_data(chip); + + supported = priv->dir_in; + if (direction == GPIO_LINE_DIRECTION_OUT) { + supported = priv->dir_out; + type = "output"; + } + + /* An invalid bit was in the provided mask */ + unsupported = mask & ~supported; + if (unsupported) { + dev_err(&priv->udev->dev, + "mpsse: GPIO %lu doesn't support %s\n", + find_first_bit(&unsupported, sizeof(unsupported) * 8), + type); + return -EOPNOTSUPP; + } + + return 0; +} + static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { @@ -167,6 +226,10 @@ static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, int ret; struct mpsse_priv *priv = gpiochip_get_data(chip); + ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_OUT); + if (ret) + return ret; + guard(mutex)(&priv->io_mutex); for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { bank = i / 8; @@ -194,6 +257,10 @@ static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask, int ret; struct mpsse_priv *priv = gpiochip_get_data(chip); + ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_IN); + if (ret) + return ret; + guard(mutex)(&priv->io_mutex); for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { bank = i / 8; @@ -242,10 +309,15 @@ static int gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, static int gpio_mpsse_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { + int ret; struct mpsse_priv *priv = gpiochip_get_data(chip); int bank = (offset & 8) >> 3; int bank_offset = offset & 7; + ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_OUT); + if (ret) + return ret; + scoped_guard(mutex, &priv->io_mutex) priv->gpio_dir[bank] |= BIT(bank_offset); @@ -255,15 +327,19 @@ static int gpio_mpsse_direction_output(struct gpio_chip *chip, static int gpio_mpsse_direction_input(struct gpio_chip *chip, unsigned int offset) { + int ret; struct mpsse_priv *priv = gpiochip_get_data(chip); int bank = (offset & 8) >> 3; int bank_offset = offset & 7; + ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_IN); + if (ret) + return ret; + guard(mutex)(&priv->io_mutex); priv->gpio_dir[bank] &= ~BIT(bank_offset); - gpio_mpsse_set_bank(priv, bank); - return 0; + return gpio_mpsse_set_bank(priv, bank); } static int gpio_mpsse_get_direction(struct gpio_chip *chip, @@ -284,18 +360,62 @@ static int gpio_mpsse_get_direction(struct gpio_chip *chip, return ret; } -static void gpio_mpsse_poll(struct work_struct *work) +/* + * Stops all workers except `my_worker`. + * Safe to call only when `irq_race` is held. + */ +static void gpio_mpsse_stop_all_except(struct mpsse_priv *priv, + struct mpsse_worker *my_worker) +{ + struct mpsse_worker *worker, *worker_tmp; + struct list_head destructors = LIST_HEAD_INIT(destructors); + + scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) { + list_for_each_entry_safe(worker, worker_tmp, + &priv->workers, list) { + /* Don't stop ourselves */ + if (worker == my_worker) + continue; + + list_del(&worker->list); + + /* Give worker a chance to terminate itself */ + atomic_set(&worker->cancelled, 1); + /* Keep track of stuff to cancel */ + INIT_LIST_HEAD(&worker->destroy); + list_add(&worker->destroy, &destructors); + } + } + + list_for_each_entry_safe(worker, worker_tmp, + &destructors, destroy) { + list_del(&worker->destroy); + cancel_work_sync(&worker->work); + kfree(worker); + } +} + +static void gpio_mpsse_poll(struct work_struct *my_work) { unsigned long pin_mask, pin_states, flags; int irq_enabled, offset, err, value, fire_irq, irq, old_value[16], irq_type[16]; - struct mpsse_priv *priv = container_of(work, struct mpsse_priv, - irq_work); + struct mpsse_worker *my_worker = container_of(my_work, struct mpsse_worker, work); + struct mpsse_priv *priv = my_worker->priv; for (offset = 0; offset < priv->gpio.ngpio; ++offset) old_value[offset] = -1; - while ((irq_enabled = atomic_read(&priv->irq_enabled))) { + /* + * We only want one worker. Workers race to acquire irq_race and tear + * down all other workers. This is a cond guard so that we don't deadlock + * trying to cancel a worker. + */ + scoped_cond_guard(mutex_try, return, &priv->irq_race) + gpio_mpsse_stop_all_except(priv, my_worker); + + while ((irq_enabled = atomic_read(&priv->irq_enabled)) && + !atomic_read(&my_worker->cancelled)) { usleep_range(MPSSE_POLL_INTERVAL, MPSSE_POLL_INTERVAL + 1000); /* Cleanup will trigger at the end of the loop */ guard(mutex)(&priv->irq_mutex); @@ -370,21 +490,45 @@ static int gpio_mpsse_set_irq_type(struct irq_data *irqd, unsigned int type) static void gpio_mpsse_irq_disable(struct irq_data *irqd) { + struct mpsse_worker *worker; struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); atomic_and(~BIT(irqd->hwirq), &priv->irq_enabled); gpiochip_disable_irq(&priv->gpio, irqd->hwirq); + + /* + * Can't actually do teardown in IRQ context (it blocks). + * As a result, these workers will stick around until irq is reenabled + * or device gets disconnected + */ + scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) + list_for_each_entry(worker, &priv->workers, list) + atomic_set(&worker->cancelled, 1); } static void gpio_mpsse_irq_enable(struct irq_data *irqd) { + struct mpsse_worker *worker; struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); gpiochip_enable_irq(&priv->gpio, irqd->hwirq); /* If no-one else was using the IRQ, enable it */ if (!atomic_fetch_or(BIT(irqd->hwirq), &priv->irq_enabled)) { - INIT_WORK(&priv->irq_work, gpio_mpsse_poll); - schedule_work(&priv->irq_work); + /* + * Can't be devm because it uses a non-raw spinlock (illegal in + * this context, where a raw spinlock is held by our caller) + */ + worker = kzalloc(sizeof(*worker), GFP_NOWAIT); + if (!worker) + return; + + worker->priv = priv; + INIT_LIST_HEAD(&worker->list); + INIT_WORK(&worker->work, gpio_mpsse_poll); + schedule_work(&worker->work); + + scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) + list_add(&worker->list, &priv->workers); } } @@ -404,18 +548,49 @@ static void gpio_mpsse_ida_remove(void *data) ida_free(&gpio_mpsse_ida, priv->id); } +static int mpsse_init_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct mpsse_priv *priv = gpiochip_get_data(chip); + + if (WARN_ON(priv == NULL)) + return -ENODEV; + + *valid_mask = priv->dir_in | priv->dir_out; + + return 0; +} + +static void mpsse_irq_init_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct mpsse_priv *priv = gpiochip_get_data(chip); + + if (WARN_ON(priv == NULL)) + return; + + /* Can only use IRQ on input capable pins */ + *valid_mask = priv->dir_in; +} + static int gpio_mpsse_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct mpsse_priv *priv; struct device *dev; + char *serial; int err; + struct mpsse_quirk *quirk = (void *)id->driver_info; dev = &interface->dev; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + INIT_LIST_HEAD(&priv->workers); + priv->udev = usb_get_dev(interface_to_usbdev(interface)); priv->intf = interface; priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber; @@ -436,9 +611,21 @@ static int gpio_mpsse_probe(struct usb_interface *interface, if (err) return err; + err = devm_mutex_init(dev, &priv->irq_race); + if (err) + return err; + + raw_spin_lock_init(&priv->irq_spin); + + serial = priv->udev->serial; + if (!serial) + serial = "NONE"; + priv->gpio.label = devm_kasprintf(dev, GFP_KERNEL, - "gpio-mpsse.%d.%d", - priv->id, priv->intf_id); + "MPSSE%04x:%04x.%d.%d.%s", + id->idVendor, id->idProduct, + priv->intf_id, priv->id, + serial); if (!priv->gpio.label) return -ENOMEM; @@ -452,10 +639,20 @@ static int gpio_mpsse_probe(struct usb_interface *interface, priv->gpio.get_multiple = gpio_mpsse_get_multiple; priv->gpio.set_multiple = gpio_mpsse_set_multiple; priv->gpio.base = -1; - priv->gpio.ngpio = 16; + priv->gpio.ngpio = MPSSE_NGPIO; priv->gpio.offset = priv->intf_id * priv->gpio.ngpio; priv->gpio.can_sleep = 1; + if (quirk) { + priv->dir_out = quirk->dir_out; + priv->dir_in = quirk->dir_in; + priv->gpio.names = quirk->names; + priv->gpio.init_valid_mask = mpsse_init_valid_mask; + } else { + priv->dir_in = U16_MAX; + priv->dir_out = U16_MAX; + } + err = usb_find_common_endpoints(interface->cur_altsetting, &priv->bulk_in, &priv->bulk_out, NULL, NULL); @@ -494,6 +691,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface, priv->gpio.irq.parents = NULL; priv->gpio.irq.default_type = IRQ_TYPE_NONE; priv->gpio.irq.handler = handle_simple_irq; + priv->gpio.irq.init_valid_mask = mpsse_irq_init_valid_mask; err = devm_gpiochip_add_data(dev, &priv->gpio, priv); if (err) @@ -506,6 +704,13 @@ static void gpio_mpsse_disconnect(struct usb_interface *intf) { struct mpsse_priv *priv = usb_get_intfdata(intf); + /* + * Lock prevents double-free of worker from here and the teardown + * step at the beginning of gpio_mpsse_poll + */ + scoped_guard(mutex, &priv->irq_race) + gpio_mpsse_stop_all_except(priv, NULL); + priv->intf = NULL; usb_set_intfdata(intf, NULL); usb_put_dev(priv->udev); diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index b0cccd856840..7345afdc78de 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -694,7 +694,7 @@ static const struct of_device_id msc313_gpio_of_match[] = { * SoC goes into suspend to memory mode so we need to save some * of the register bits before suspending and put it back when resuming */ -static int __maybe_unused msc313_gpio_suspend(struct device *dev) +static int msc313_gpio_suspend(struct device *dev) { struct msc313_gpio *gpio = dev_get_drvdata(dev); int i; @@ -705,7 +705,7 @@ static int __maybe_unused msc313_gpio_suspend(struct device *dev) return 0; } -static int __maybe_unused msc313_gpio_resume(struct device *dev) +static int msc313_gpio_resume(struct device *dev) { struct msc313_gpio *gpio = dev_get_drvdata(dev); int i; @@ -716,13 +716,13 @@ static int __maybe_unused msc313_gpio_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume); static struct platform_driver msc313_gpio_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = msc313_gpio_of_match, - .pm = &msc313_gpio_ops, + .pm = pm_sleep_ptr(&msc313_gpio_ops), }, .probe = msc313_gpio_probe, }; diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index ac799fced950..22c36b79e249 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -573,11 +573,10 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) for (i = 0; i < mvchip->chip.ngpio; i++) { int irq; - irq = irq_find_mapping(mvchip->domain, i); - if (!(cause & BIT(i))) continue; + irq = irq_find_mapping(mvchip->domain, i); type = irq_get_trigger_type(irq); if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { /* Swap polarity (race with GPIO line) */ diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index a268c76bdca6..e136e81794df 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1503,7 +1503,7 @@ static void omap_gpio_remove(struct platform_device *pdev) clk_unprepare(bank->dbck); } -static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev) +static int omap_gpio_runtime_suspend(struct device *dev) { struct gpio_bank *bank = dev_get_drvdata(dev); unsigned long flags; @@ -1516,7 +1516,7 @@ static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) +static int omap_gpio_runtime_resume(struct device *dev) { struct gpio_bank *bank = dev_get_drvdata(dev); unsigned long flags; @@ -1529,7 +1529,7 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused omap_gpio_suspend(struct device *dev) +static int omap_gpio_suspend(struct device *dev) { struct gpio_bank *bank = dev_get_drvdata(dev); @@ -1541,7 +1541,7 @@ static int __maybe_unused omap_gpio_suspend(struct device *dev) return omap_gpio_runtime_suspend(dev); } -static int __maybe_unused omap_gpio_resume(struct device *dev) +static int omap_gpio_resume(struct device *dev) { struct gpio_bank *bank = dev_get_drvdata(dev); @@ -1554,9 +1554,8 @@ static int __maybe_unused omap_gpio_resume(struct device *dev) } static const struct dev_pm_ops gpio_pm_ops = { - SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, - NULL) - SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) + RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL) + LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) }; static struct platform_driver omap_gpio_driver = { @@ -1564,7 +1563,7 @@ static struct platform_driver omap_gpio_driver = { .remove = omap_gpio_remove, .driver = { .name = "omap_gpio", - .pm = &gpio_pm_ops, + .pm = pm_ptr(&gpio_pm_ops), .of_match_table = omap_gpio_match, }, }; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b46927f55038..0a3916cc2772 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -306,7 +306,7 @@ static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned int of * Interrupt mask register 0x40 + 5 * bank_size RW * Interrupt status register 0x40 + 6 * bank_size R * - * - Registers with bit 0x80 set, the AI bit + * - Registers with bit 0x80 set, the AI bit (auto increment) * The bit is cleared and the registers fall into one of the * categories above. */ @@ -854,10 +854,13 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) int level; if (chip->driver_data & PCA_PCAL) { + DECLARE_BITMAP(latched_inputs, MAX_LINE); guard(mutex)(&chip->i2c_lock); - /* Enable latch on interrupt-enabled inputs */ - pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); + /* Enable latch on edge-triggered interrupt-enabled inputs */ + bitmap_or(latched_inputs, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); + bitmap_and(latched_inputs, latched_inputs, chip->irq_mask, gc->ngpio); + pca953x_write_regs(chip, PCAL953X_IN_LATCH, latched_inputs); bitmap_complement(irq_mask, chip->irq_mask, gc->ngpio); @@ -1203,10 +1206,10 @@ static int pca953x_probe(struct i2c_client *client) pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK); if (NBANK(chip) > 2 || PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { - dev_info(dev, "using AI\n"); + dev_info(dev, "using auto increment\n"); regmap_config = &pca953x_ai_i2c_regmap; } else { - dev_info(dev, "using no AI\n"); + dev_info(dev, "using no auto increment\n"); regmap_config = &pca953x_i2c_regmap; } diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 9925687e05fb..4ffa0955a9e3 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -171,7 +171,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) /* * Save register configuration and disable interrupts. */ -static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip) +static void pch_gpio_save_reg_conf(struct pch_gpio *chip) { chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien); chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask); @@ -187,7 +187,7 @@ static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip) /* * This function restores the register configuration of the GPIO device. */ -static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip) +static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) { iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien); iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask); @@ -402,7 +402,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); } -static int __maybe_unused pch_gpio_suspend(struct device *dev) +static int pch_gpio_suspend(struct device *dev) { struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; @@ -414,7 +414,7 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) return 0; } -static int __maybe_unused pch_gpio_resume(struct device *dev) +static int pch_gpio_resume(struct device *dev) { struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; @@ -428,7 +428,7 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume); static const struct pci_device_id pch_gpio_pcidev_id[] = { { PCI_DEVICE_DATA(INTEL, EG20T_PCH, INTEL_EG20T_PCH) }, @@ -444,7 +444,7 @@ static struct pci_driver pch_gpio_driver = { .id_table = pch_gpio_pcidev_id, .probe = pch_gpio_probe, .driver = { - .pm = &pch_gpio_pm_ops, + .pm = pm_sleep_ptr(&pch_gpio_pm_ops), }, }; diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 476cea1b5ed7..9d28ca8e1d6f 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -41,6 +41,7 @@ static const struct regmap_config idio_16_regmap_config = { .reg_stride = 1, .val_bits = 8, .io_port = true, + .max_register = 0x7, .wr_table = &idio_16_wr_table, .rd_table = &idio_16_rd_table, .volatile_table = &idio_16_rd_table, diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 02e4ffcf5a6f..919cf86fd590 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -37,7 +37,6 @@ #define PL061_GPIO_NR 8 -#ifdef CONFIG_PM struct pl061_context_save_regs { u8 gpio_data; u8 gpio_dir; @@ -46,7 +45,6 @@ struct pl061_context_save_regs { u8 gpio_iev; u8 gpio_ie; }; -#endif struct pl061 { raw_spinlock_t lock; @@ -55,9 +53,7 @@ struct pl061 { struct gpio_chip gc; int parent_irq; -#ifdef CONFIG_PM struct pl061_context_save_regs csave_regs; -#endif }; static int pl061_get_direction(struct gpio_chip *gc, unsigned offset) @@ -367,7 +363,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -#ifdef CONFIG_PM static int pl061_suspend(struct device *dev) { struct pl061 *pl061 = dev_get_drvdata(dev); @@ -411,13 +406,7 @@ static int pl061_resume(struct device *dev) return 0; } -static const struct dev_pm_ops pl061_dev_pm_ops = { - .suspend = pl061_suspend, - .resume = pl061_resume, - .freeze = pl061_suspend, - .restore = pl061_resume, -}; -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume); static const struct amba_id pl061_ids[] = { { @@ -431,9 +420,7 @@ MODULE_DEVICE_TABLE(amba, pl061_ids); static struct amba_driver pl061_gpio_driver = { .drv = { .name = "pl061_gpio", -#ifdef CONFIG_PM - .pm = &pl061_dev_pm_ops, -#endif + .pm = pm_sleep_ptr(&pl061_dev_pm_ops), }, .id_table = pl061_ids, .probe = pl061_probe, diff --git a/drivers/gpio/gpio-qixis-fpga.c b/drivers/gpio/gpio-qixis-fpga.c new file mode 100644 index 000000000000..6e67f43ac0bd --- /dev/null +++ b/drivers/gpio/gpio-qixis-fpga.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Layerscape GPIO QIXIS FPGA driver + * + * Copyright 2025 NXP + */ + +#include <linux/device.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +struct qixis_cpld_gpio_config { + u64 output_lines; +}; + +static const struct qixis_cpld_gpio_config lx2160ardb_sfp_cfg = { + .output_lines = BIT(0), +}; + +static const struct qixis_cpld_gpio_config ls1046aqds_stat_pres2_cfg = { + .output_lines = 0x0, +}; + +static const struct regmap_config regmap_config_8r_8v = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int qixis_cpld_gpio_probe(struct platform_device *pdev) +{ + DECLARE_BITMAP(fixed_direction_output, 8); + const struct qixis_cpld_gpio_config *cfg; + struct gpio_regmap_config config = {0}; + struct regmap *regmap; + void __iomem *reg; + u32 base; + int ret; + + if (!pdev->dev.parent) + return -ENODEV; + + cfg = device_get_match_data(&pdev->dev); + + ret = device_property_read_u32(&pdev->dev, "reg", &base); + if (ret) + return ret; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + /* In case there is no regmap configured by the parent device, + * create our own from the MMIO space. + */ + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + regmap = devm_regmap_init_mmio(&pdev->dev, reg, ®map_config_8r_8v); + if (!regmap) + return -ENODEV; + + /* In this case, the offset of our register is 0 inside the + * regmap area that we just created. + */ + base = 0; + } + config.reg_dat_base = GPIO_REGMAP_ADDR(base); + config.reg_set_base = GPIO_REGMAP_ADDR(base); + + config.drvdata = (void *)cfg; + config.regmap = regmap; + config.parent = &pdev->dev; + config.ngpio_per_reg = 8; + config.ngpio = 8; + + bitmap_from_u64(fixed_direction_output, cfg->output_lines); + config.fixed_direction_output = fixed_direction_output; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config)); +} + +static const struct of_device_id qixis_cpld_gpio_of_match[] = { + { + .compatible = "fsl,lx2160ardb-fpga-gpio-sfp", + .data = &lx2160ardb_sfp_cfg, + }, + { + .compatible = "fsl,ls1046aqds-fpga-gpio-stat-pres2", + .data = &ls1046aqds_stat_pres2_cfg, + }, + + {} +}; +MODULE_DEVICE_TABLE(of, qixis_cpld_gpio_of_match); + +static struct platform_driver qixis_cpld_gpio_driver = { + .probe = qixis_cpld_gpio_probe, + .driver = { + .name = "gpio-qixis-cpld", + .of_match_table = qixis_cpld_gpio_of_match, + }, +}; +module_platform_driver(qixis_cpld_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>"); +MODULE_DESCRIPTION("Layerscape GPIO QIXIS FPGA driver"); diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index ab9e4077fa60..e5ba38e65c10 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -31,6 +31,7 @@ struct gpio_regmap { unsigned int reg_clr_base; unsigned int reg_dir_in_base; unsigned int reg_dir_out_base; + unsigned long *fixed_direction_output; #ifdef CONFIG_REGMAP_IRQ int regmap_irq_line; @@ -81,7 +82,11 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) if (ret) return ret; - ret = regmap_read(gpio->regmap, reg, &val); + /* ensure we don't spoil any register cache with pin input values */ + if (gpio->reg_dat_base == gpio->reg_set_base) + ret = regmap_read_bypassed(gpio->regmap, reg, &val); + else + ret = regmap_read(gpio->regmap, reg, &val); if (ret) return ret; @@ -93,7 +98,7 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, { struct gpio_regmap *gpio = gpiochip_get_data(chip); unsigned int base = gpio_regmap_addr(gpio->reg_set_base); - unsigned int reg, mask; + unsigned int reg, mask, mask_val; int ret; ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); @@ -101,9 +106,15 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, return ret; if (val) - ret = regmap_update_bits(gpio->regmap, reg, mask, mask); + mask_val = mask; else - ret = regmap_update_bits(gpio->regmap, reg, mask, 0); + mask_val = 0; + + /* ignore input values which shadow the old output value */ + if (gpio->reg_dat_base == gpio->reg_set_base) + ret = regmap_write_bits(gpio->regmap, reg, mask, mask_val); + else + ret = regmap_update_bits(gpio->regmap, reg, mask, mask_val); return ret; } @@ -134,6 +145,13 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; + if (gpio->fixed_direction_output) { + if (test_bit(offset, gpio->fixed_direction_output)) + return GPIO_LINE_DIRECTION_OUT; + else + return GPIO_LINE_DIRECTION_IN; + } + if (gpio->reg_dat_base && !gpio->reg_set_base) return GPIO_LINE_DIRECTION_IN; if (gpio->reg_set_base && !gpio->reg_dat_base) @@ -284,6 +302,17 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config goto err_free_gpio; } + if (config->fixed_direction_output) { + gpio->fixed_direction_output = bitmap_alloc(chip->ngpio, + GFP_KERNEL); + if (!gpio->fixed_direction_output) { + ret = -ENOMEM; + goto err_free_gpio; + } + bitmap_copy(gpio->fixed_direction_output, + config->fixed_direction_output, chip->ngpio); + } + /* if not set, assume there is only one register */ gpio->ngpio_per_reg = config->ngpio_per_reg; if (!gpio->ngpio_per_reg) @@ -300,7 +329,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config ret = gpiochip_add_data(chip, gpio); if (ret < 0) - goto err_free_gpio; + goto err_free_bitmap; #ifdef CONFIG_REGMAP_IRQ if (config->regmap_irq_chip) { @@ -309,7 +338,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config config->regmap_irq_line, config->regmap_irq_flags, 0, config->regmap_irq_chip, &gpio->irq_chip_data); if (ret) - goto err_free_gpio; + goto err_free_bitmap; irq_domain = regmap_irq_get_domain(gpio->irq_chip_data); } else @@ -326,6 +355,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config err_remove_gpiochip: gpiochip_remove(chip); +err_free_bitmap: + bitmap_free(gpio->fixed_direction_output); err_free_gpio: kfree(gpio); return ERR_PTR(ret); @@ -344,6 +375,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio) #endif gpiochip_remove(&gpio->gpio_chip); + bitmap_free(gpio->fixed_direction_output); kfree(gpio); } EXPORT_SYMBOL_GPL(gpio_regmap_unregister); diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c new file mode 100644 index 000000000000..29d7d2e4dfc0 --- /dev/null +++ b/drivers/gpio/gpio-shared-proxy.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Linaro Ltd. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/string_choices.h> +#include <linux/types.h> + +#include "gpiolib-shared.h" + +struct gpio_shared_proxy_data { + struct gpio_chip gc; + struct gpio_shared_desc *shared_desc; + struct device *dev; + bool voted_high; +}; + +static int +gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, + int (*set_func)(struct gpio_desc *desc, int value), + int value) +{ + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int ret = 0; + + gpio_shared_lockdep_assert(shared_desc); + + if (value) { + /* User wants to set value to high. */ + if (proxy->voted_high) + /* Already voted for high, nothing to do. */ + goto out; + + /* Haven't voted for high yet. */ + if (!shared_desc->highcnt) { + /* + * Current value is low, need to actually set value + * to high. + */ + ret = set_func(desc, 1); + if (ret) + goto out; + } + + shared_desc->highcnt++; + proxy->voted_high = true; + + goto out; + } + + /* Desired value is low. */ + if (!proxy->voted_high) + /* We didn't vote for high, nothing to do. */ + goto out; + + /* We previously voted for high. */ + if (shared_desc->highcnt == 1) { + /* This is the last remaining vote for high, set value to low. */ + ret = set_func(desc, 0); + if (ret) + goto out; + } + + shared_desc->highcnt--; + proxy->voted_high = false; + +out: + if (shared_desc->highcnt) + dev_dbg(proxy->dev, + "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n", + str_high_low(value), shared_desc->highcnt); + else + dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n"); + + return ret; +} + +static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + + guard(gpio_shared_desc_lock)(shared_desc); + + proxy->shared_desc->usecnt++; + + dev_dbg(proxy->dev, "Shared GPIO requested, number of users: %u\n", + proxy->shared_desc->usecnt); + + return 0; +} + +static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + + guard(gpio_shared_desc_lock)(shared_desc); + + proxy->shared_desc->usecnt--; + + dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n", + proxy->shared_desc->usecnt); +} + +static int gpio_shared_proxy_set_config(struct gpio_chip *gc, + unsigned int offset, unsigned long cfg) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int ret; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt > 1) { + if (shared_desc->cfg != cfg) { + dev_dbg(proxy->dev, + "Shared GPIO's configuration already set, accepting changes but users may conflict!!\n"); + } else { + dev_dbg(proxy->dev, "Equal config requested, nothing to do\n"); + return 0; + } + } + + ret = gpiod_set_config(desc, cfg); + if (ret && ret != -ENOTSUPP) + return ret; + + shared_desc->cfg = cfg; + return 0; +} + +static int gpio_shared_proxy_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int dir; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt == 1) { + dev_dbg(proxy->dev, + "Only one user of this shared GPIO, allowing to set direction to input\n"); + + return gpiod_direction_input(desc); + } + + dir = gpiod_get_direction(desc); + if (dir < 0) + return dir; + + if (dir == GPIO_LINE_DIRECTION_OUT) { + dev_dbg(proxy->dev, + "Shared GPIO's direction already set to output, refusing to change\n"); + return -EPERM; + } + + return 0; +} + +static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int ret, dir; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt == 1) { + dev_dbg(proxy->dev, + "Only one user of this shared GPIO, allowing to set direction to output with value '%s'\n", + str_high_low(value)); + + ret = gpiod_direction_output(desc, value); + if (ret) + return ret; + + if (value) { + proxy->voted_high = true; + shared_desc->highcnt = 1; + } else { + proxy->voted_high = false; + shared_desc->highcnt = 0; + } + + return 0; + } + + dir = gpiod_get_direction(desc); + if (dir < 0) + return dir; + + if (dir == GPIO_LINE_DIRECTION_IN) { + dev_dbg(proxy->dev, + "Shared GPIO's direction already set to input, refusing to change\n"); + return -EPERM; + } + + return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value); +} + +static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + + return gpiod_get_value(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + + return gpiod_get_value_cansleep(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy, + int (*set_func)(struct gpio_desc *desc, int value), + int value) +{ + guard(gpio_shared_desc_lock)(proxy->shared_desc); + + return gpio_shared_proxy_set_unlocked(proxy, set_func, value); +} + +static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + + return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value); +} + +static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + + return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value); +} + +static int gpio_shared_proxy_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + + return gpiod_get_direction(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + + return gpiod_to_irq(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct gpio_shared_proxy_data *proxy; + struct gpio_shared_desc *shared_desc; + struct device *dev = &adev->dev; + struct gpio_chip *gc; + + shared_desc = devm_gpiod_shared_get(dev); + if (IS_ERR(shared_desc)) + return PTR_ERR(shared_desc); + + proxy = devm_kzalloc(dev, sizeof(*proxy), GFP_KERNEL); + if (!proxy) + return -ENOMEM; + + proxy->shared_desc = shared_desc; + proxy->dev = dev; + + gc = &proxy->gc; + gc->base = -1; + gc->ngpio = 1; + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->can_sleep = shared_desc->can_sleep; + + gc->request = gpio_shared_proxy_request; + gc->free = gpio_shared_proxy_free; + gc->set_config = gpio_shared_proxy_set_config; + gc->direction_input = gpio_shared_proxy_direction_input; + gc->direction_output = gpio_shared_proxy_direction_output; + if (gc->can_sleep) { + gc->set = gpio_shared_proxy_set_cansleep; + gc->get = gpio_shared_proxy_get_cansleep; + } else { + gc->set = gpio_shared_proxy_set; + gc->get = gpio_shared_proxy_get; + } + gc->get_direction = gpio_shared_proxy_get_direction; + gc->to_irq = gpio_shared_proxy_to_irq; + + return devm_gpiochip_add_data(dev, &proxy->gc, proxy); +} + +static const struct auxiliary_device_id gpio_shared_proxy_id_table[] = { + { .name = "gpiolib_shared.proxy" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table); + +static struct auxiliary_driver gpio_shared_proxy_driver = { + .driver = { + .name = "gpio-shared-proxy", + .suppress_bind_attrs = true, + }, + .probe = gpio_shared_proxy_probe, + .id_table = gpio_shared_proxy_id_table, +}; +module_auxiliary_driver(gpio_shared_proxy_driver); + +MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>"); +MODULE_DESCRIPTION("Shared GPIO mux driver."); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 09a448ce3eec..3c8fd322a713 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -50,25 +50,6 @@ static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs) return ioread32(gpio->base + offs); } -static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs, - u32 val) -{ - iowrite32(val, gpio->base + offs); -} - -static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs, - u32 mask, u32 val) -{ - u32 r; - - guard(gpio_generic_lock_irqsave)(&gpio->chip); - - r = tb10x_reg_read(gpio, offs); - r = (r & ~mask) | (val & mask); - - tb10x_reg_write(gpio, offs, r); -} - static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 4d3db6e06eeb..b1498b59a921 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2022 NVIDIA Corporation + * Copyright (c) 2016-2025 NVIDIA Corporation * * Author: Thierry Reding <treding@nvidia.com> * Dipen Patel <dpatel@nvidia.com> @@ -69,6 +69,30 @@ #define TEGRA186_GPIO_INTERRUPT_STATUS(x) (0x100 + (x) * 4) +/* Tegra410 GPIOs implemented by the COMPUTE GPIO controller */ +#define TEGRA410_COMPUTE_GPIO_PORT_A 0 +#define TEGRA410_COMPUTE_GPIO_PORT_B 1 +#define TEGRA410_COMPUTE_GPIO_PORT_C 2 +#define TEGRA410_COMPUTE_GPIO_PORT_D 3 +#define TEGRA410_COMPUTE_GPIO_PORT_E 4 + +/* Tegra410 GPIOs implemented by the SYSTEM GPIO controller */ +#define TEGRA410_SYSTEM_GPIO_PORT_A 0 +#define TEGRA410_SYSTEM_GPIO_PORT_B 1 +#define TEGRA410_SYSTEM_GPIO_PORT_C 2 +#define TEGRA410_SYSTEM_GPIO_PORT_D 3 +#define TEGRA410_SYSTEM_GPIO_PORT_E 4 +#define TEGRA410_SYSTEM_GPIO_PORT_I 5 +#define TEGRA410_SYSTEM_GPIO_PORT_J 6 +#define TEGRA410_SYSTEM_GPIO_PORT_K 7 +#define TEGRA410_SYSTEM_GPIO_PORT_L 8 +#define TEGRA410_SYSTEM_GPIO_PORT_M 9 +#define TEGRA410_SYSTEM_GPIO_PORT_N 10 +#define TEGRA410_SYSTEM_GPIO_PORT_P 11 +#define TEGRA410_SYSTEM_GPIO_PORT_Q 12 +#define TEGRA410_SYSTEM_GPIO_PORT_R 13 +#define TEGRA410_SYSTEM_GPIO_PORT_V 14 + struct tegra_gpio_port { const char *name; unsigned int bank; @@ -85,6 +109,7 @@ struct tegra_gpio_soc { const struct tegra_gpio_port *ports; unsigned int num_ports; const char *name; + const char *prefix; unsigned int instance; unsigned int num_irqs_per_bank; @@ -916,8 +941,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev) char *name; for (j = 0; j < port->pins; j++) { - name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, - "P%s.%02x", port->name, j); + if (gpio->soc->prefix) + name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "%s-P%s.%02x", + gpio->soc->prefix, port->name, j); + else + name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "P%s.%02x", + port->name, j); if (!name) return -ENOMEM; @@ -1002,14 +1031,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio); } -#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA186_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ +#define TEGRA_GPIO_PORT(_prefix, _name, _bank, _port, _pins) \ + [_prefix##_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } +#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA186_MAIN, _name, _bank, _port, _pins) + static const struct tegra_gpio_port tegra186_main_ports[] = { TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7), TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7), @@ -1045,13 +1077,8 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .has_vm_support = false, }; -#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA186_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA186_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra186_aon_ports[] = { TEGRA186_AON_GPIO_PORT( S, 0, 1, 5), @@ -1073,13 +1100,8 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .has_vm_support = false, }; -#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA194_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA194_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra194_main_ports[] = { TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8), @@ -1129,13 +1151,8 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .has_vm_support = true, }; -#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA194_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA194_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra194_aon_ports[] = { TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8), @@ -1155,13 +1172,8 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .has_vm_support = false, }; -#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA234_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA234_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra234_main_ports[] = { TEGRA234_MAIN_GPIO_PORT( A, 0, 0, 8), @@ -1200,13 +1212,8 @@ static const struct tegra_gpio_soc tegra234_main_soc = { .has_vm_support = true, }; -#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA234_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA234_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra234_aon_ports[] = { TEGRA234_AON_GPIO_PORT(AA, 0, 4, 8), @@ -1227,13 +1234,8 @@ static const struct tegra_gpio_soc tegra234_aon_soc = { .has_vm_support = false, }; -#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA241_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra241_main_ports[] = { TEGRA241_MAIN_GPIO_PORT(A, 0, 0, 8), @@ -1258,13 +1260,8 @@ static const struct tegra_gpio_soc tegra241_main_soc = { .has_vm_support = false, }; -#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA241_AON_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA241_AON, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra241_aon_ports[] = { TEGRA241_AON_GPIO_PORT(AA, 0, 0, 8), @@ -1280,13 +1277,8 @@ static const struct tegra_gpio_soc tegra241_aon_soc = { .has_vm_support = false, }; -#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ - [TEGRA256_MAIN_GPIO_PORT_##_name] = { \ - .name = #_name, \ - .bank = _bank, \ - .port = _port, \ - .pins = _pins, \ - } +#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins) static const struct tegra_gpio_port tegra256_main_ports[] = { TEGRA256_MAIN_GPIO_PORT(A, 0, 0, 8), @@ -1304,6 +1296,56 @@ static const struct tegra_gpio_soc tegra256_main_soc = { .has_vm_support = true, }; +#define TEGRA410_COMPUTE_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA410_COMPUTE, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra410_compute_ports[] = { + TEGRA410_COMPUTE_GPIO_PORT(A, 0, 0, 3), + TEGRA410_COMPUTE_GPIO_PORT(B, 1, 0, 8), + TEGRA410_COMPUTE_GPIO_PORT(C, 1, 1, 3), + TEGRA410_COMPUTE_GPIO_PORT(D, 2, 0, 8), + TEGRA410_COMPUTE_GPIO_PORT(E, 2, 1, 8), +}; + +static const struct tegra_gpio_soc tegra410_compute_soc = { + .num_ports = ARRAY_SIZE(tegra410_compute_ports), + .ports = tegra410_compute_ports, + .name = "tegra410-gpio-compute", + .prefix = "COMPUTE", + .num_irqs_per_bank = 8, + .instance = 0, +}; + +#define TEGRA410_SYSTEM_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA410_SYSTEM, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra410_system_ports[] = { + TEGRA410_SYSTEM_GPIO_PORT(A, 0, 0, 7), + TEGRA410_SYSTEM_GPIO_PORT(B, 0, 1, 8), + TEGRA410_SYSTEM_GPIO_PORT(C, 0, 2, 8), + TEGRA410_SYSTEM_GPIO_PORT(D, 0, 3, 8), + TEGRA410_SYSTEM_GPIO_PORT(E, 0, 4, 6), + TEGRA410_SYSTEM_GPIO_PORT(I, 1, 0, 8), + TEGRA410_SYSTEM_GPIO_PORT(J, 1, 1, 7), + TEGRA410_SYSTEM_GPIO_PORT(K, 1, 2, 7), + TEGRA410_SYSTEM_GPIO_PORT(L, 1, 3, 7), + TEGRA410_SYSTEM_GPIO_PORT(M, 2, 0, 7), + TEGRA410_SYSTEM_GPIO_PORT(N, 2, 1, 6), + TEGRA410_SYSTEM_GPIO_PORT(P, 2, 2, 8), + TEGRA410_SYSTEM_GPIO_PORT(Q, 2, 3, 3), + TEGRA410_SYSTEM_GPIO_PORT(R, 2, 4, 2), + TEGRA410_SYSTEM_GPIO_PORT(V, 1, 4, 2), +}; + +static const struct tegra_gpio_soc tegra410_system_soc = { + .num_ports = ARRAY_SIZE(tegra410_system_ports), + .ports = tegra410_system_ports, + .name = "tegra410-gpio-system", + .prefix = "SYSTEM", + .num_irqs_per_bank = 8, + .instance = 0, +}; + static const struct of_device_id tegra186_gpio_of_match[] = { { .compatible = "nvidia,tegra186-gpio", @@ -1339,6 +1381,8 @@ static const struct acpi_device_id tegra186_gpio_acpi_match[] = { { .id = "NVDA0408", .driver_data = (kernel_ulong_t)&tegra194_aon_soc }, { .id = "NVDA0508", .driver_data = (kernel_ulong_t)&tegra241_main_soc }, { .id = "NVDA0608", .driver_data = (kernel_ulong_t)&tegra241_aon_soc }, + { .id = "NVDA0708", .driver_data = (kernel_ulong_t)&tegra410_compute_soc }, + { .id = "NVDA0808", .driver_data = (kernel_ulong_t)&tegra410_system_soc }, {} }; MODULE_DEVICE_TABLE(acpi, tegra186_gpio_acpi_match); diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 27dd09273292..eedfc0e371e3 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -279,19 +279,18 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) } /* Minimal runtime PM is needed by the IRQ subsystem */ -static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev) +static int tqmx86_gpio_runtime_suspend(struct device *dev) { return 0; } -static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev) +static int tqmx86_gpio_runtime_resume(struct device *dev) { return 0; } static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = { - SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, - tqmx86_gpio_runtime_resume, NULL) + RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, tqmx86_gpio_runtime_resume, NULL) }; static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip, @@ -425,7 +424,7 @@ out_pm_dis: static struct platform_driver tqmx86_gpio_driver = { .driver = { .name = "tqmx86-gpio", - .pm = &tqmx86_gpio_dev_pm_ops, + .pm = pm_ptr(&tqmx86_gpio_dev_pm_ops), }, .probe = tqmx86_gpio_probe, }; diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 197bb1d22b3c..0574dde5b5bb 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -426,7 +426,7 @@ static void uniphier_gpio_remove(struct platform_device *pdev) irq_domain_remove(priv->domain); } -static int __maybe_unused uniphier_gpio_suspend(struct device *dev) +static int uniphier_gpio_suspend(struct device *dev) { struct uniphier_gpio_priv *priv = dev_get_drvdata(dev); unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio); @@ -448,7 +448,7 @@ static int __maybe_unused uniphier_gpio_suspend(struct device *dev) return 0; } -static int __maybe_unused uniphier_gpio_resume(struct device *dev) +static int uniphier_gpio_resume(struct device *dev) { struct uniphier_gpio_priv *priv = dev_get_drvdata(dev); unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio); @@ -473,8 +473,7 @@ static int __maybe_unused uniphier_gpio_resume(struct device *dev) } static const struct dev_pm_ops uniphier_gpio_pm_ops = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend, - uniphier_gpio_resume) + LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend, uniphier_gpio_resume) }; static const struct of_device_id uniphier_gpio_match[] = { @@ -489,7 +488,7 @@ static struct platform_driver uniphier_gpio_driver = { .driver = { .name = "uniphier-gpio", .of_match_table = uniphier_gpio_match, - .pm = &uniphier_gpio_pm_ops, + .pm = pm_sleep_ptr(&uniphier_gpio_pm_ops), }, }; module_platform_driver(uniphier_gpio_driver); diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index a10eab7d2617..37f2ce20f1ae 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -500,9 +500,7 @@ static int gpio_virtuser_value_set(void *data, u64 val) if (val > 1) return -EINVAL; - gpiod_set_value_cansleep(ld->ad.desc, (int)val); - - return 0; + return gpiod_set_value_cansleep(ld->ad.desc, (int)val); } DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_fops, @@ -543,7 +541,7 @@ static void gpio_virtuser_set_value_atomic(struct irq_work *work) struct gpio_virtuser_irq_work_context *ctx = to_gpio_virtuser_irq_work_context(work); - gpiod_set_value(ctx->desc, ctx->val); + ctx->ret = gpiod_set_value(ctx->desc, ctx->val); complete(&ctx->work_completion); } @@ -562,7 +560,7 @@ static int gpio_virtuser_value_atomic_set(void *data, u64 val) gpio_virtuser_irq_work_queue_sync(&ctx); - return 0; + return ctx.ret; } DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_atomic_fops, diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c index 4f627de3f56c..809668449dbe 100644 --- a/drivers/gpio/gpio-xgene.c +++ b/drivers/gpio/gpio-xgene.c @@ -130,7 +130,7 @@ static int xgene_gpio_dir_out(struct gpio_chip *gc, return 0; } -static __maybe_unused int xgene_gpio_suspend(struct device *dev) +static int xgene_gpio_suspend(struct device *dev) { struct xgene_gpio *gpio = dev_get_drvdata(dev); unsigned long bank_offset; @@ -143,7 +143,7 @@ static __maybe_unused int xgene_gpio_suspend(struct device *dev) return 0; } -static __maybe_unused int xgene_gpio_resume(struct device *dev) +static int xgene_gpio_resume(struct device *dev) { struct xgene_gpio *gpio = dev_get_drvdata(dev); unsigned long bank_offset; @@ -156,7 +156,7 @@ static __maybe_unused int xgene_gpio_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume); static int xgene_gpio_probe(struct platform_device *pdev) { @@ -204,7 +204,7 @@ static struct platform_driver xgene_gpio_driver = { .name = "xgene-gpio", .of_match_table = xgene_gpio_of_match, .acpi_match_table = ACPI_PTR(xgene_gpio_acpi_match), - .pm = &xgene_gpio_pm, + .pm = pm_sleep_ptr(&xgene_gpio_pm), }, .probe = xgene_gpio_probe, }; diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 83675ac81077..be4b4d730547 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -286,7 +286,7 @@ static void xgpio_free(struct gpio_chip *chip, unsigned int offset) pm_runtime_put(chip->parent); } -static int __maybe_unused xgpio_suspend(struct device *dev) +static int xgpio_suspend(struct device *dev) { struct xgpio_instance *gpio = dev_get_drvdata(dev); struct irq_data *data = irq_get_irq_data(gpio->irq); @@ -327,7 +327,7 @@ static void xgpio_irq_ack(struct irq_data *irq_data) { } -static int __maybe_unused xgpio_resume(struct device *dev) +static int xgpio_resume(struct device *dev) { struct xgpio_instance *gpio = dev_get_drvdata(dev); struct irq_data *data = irq_get_irq_data(gpio->irq); @@ -343,7 +343,7 @@ static int __maybe_unused xgpio_resume(struct device *dev) return 0; } -static int __maybe_unused xgpio_runtime_suspend(struct device *dev) +static int xgpio_runtime_suspend(struct device *dev) { struct xgpio_instance *gpio = dev_get_drvdata(dev); @@ -352,7 +352,7 @@ static int __maybe_unused xgpio_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused xgpio_runtime_resume(struct device *dev) +static int xgpio_runtime_resume(struct device *dev) { struct xgpio_instance *gpio = dev_get_drvdata(dev); @@ -360,9 +360,8 @@ static int __maybe_unused xgpio_runtime_resume(struct device *dev) } static const struct dev_pm_ops xgpio_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume) - SET_RUNTIME_PM_OPS(xgpio_runtime_suspend, - xgpio_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume) + RUNTIME_PM_OPS(xgpio_runtime_suspend, xgpio_runtime_resume, NULL) }; /** @@ -682,7 +681,7 @@ static struct platform_driver xgpio_plat_driver = { .driver = { .name = "gpio-xilinx", .of_match_table = xgpio_of_match, - .pm = &xgpio_dev_pm_ops, + .pm = pm_ptr(&xgpio_dev_pm_ops), }, }; diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 0ffd76e8951f..97780c57ab56 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -735,7 +735,7 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio) } } -static int __maybe_unused zynq_gpio_suspend(struct device *dev) +static int zynq_gpio_suspend(struct device *dev) { struct zynq_gpio *gpio = dev_get_drvdata(dev); struct irq_data *data = irq_get_irq_data(gpio->irq); @@ -756,7 +756,7 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev) return 0; } -static int __maybe_unused zynq_gpio_resume(struct device *dev) +static int zynq_gpio_resume(struct device *dev) { struct zynq_gpio *gpio = dev_get_drvdata(dev); struct irq_data *data = irq_get_irq_data(gpio->irq); @@ -779,7 +779,7 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev) return 0; } -static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev) +static int zynq_gpio_runtime_suspend(struct device *dev) { struct zynq_gpio *gpio = dev_get_drvdata(dev); @@ -788,7 +788,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev) +static int zynq_gpio_runtime_resume(struct device *dev) { struct zynq_gpio *gpio = dev_get_drvdata(dev); @@ -814,9 +814,8 @@ static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset) } static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume) - SET_RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, - zynq_gpio_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume) + RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL) }; static const struct zynq_platform_data versal_gpio_def = { @@ -1022,7 +1021,7 @@ static void zynq_gpio_remove(struct platform_device *pdev) static struct platform_driver zynq_gpio_driver = { .driver = { .name = DRIVER_NAME, - .pm = &zynq_gpio_dev_pm_ops, + .pm = pm_ptr(&zynq_gpio_dev_pm_ops), .of_match_table = zynq_gpio_of_match, }, .probe = zynq_gpio_probe, diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 284e762d92c4..83dd227dbbec 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -291,6 +291,19 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity) return GPIOD_ASIS; } +static void acpi_gpio_set_debounce_timeout(struct gpio_desc *desc, + unsigned int acpi_debounce) +{ + int ret; + + /* ACPI uses hundredths of milliseconds units */ + acpi_debounce *= 10; + ret = gpio_set_debounce_timeout(desc, acpi_debounce); + if (ret) + gpiod_warn(desc, "Failed to set debounce-timeout %u: %d\n", + acpi_debounce, ret); +} + static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, struct acpi_resource_gpio *agpio, unsigned int index, @@ -300,18 +313,12 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); unsigned int pin = agpio->pin_table[index]; struct gpio_desc *desc; - int ret; desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags); if (IS_ERR(desc)) return desc; - /* ACPI uses hundredths of milliseconds units */ - ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout * 10); - if (ret) - dev_warn(chip->parent, - "Failed to set debounce-timeout for pin 0x%04X, err %d\n", - pin, ret); + acpi_gpio_set_debounce_timeout(desc, agpio->debounce_timeout); return desc; } @@ -375,8 +382,8 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event"); if (IS_ERR(desc)) { dev_err(chip->parent, - "Failed to request GPIO for pin 0x%04X, err %ld\n", - pin, PTR_ERR(desc)); + "Failed to request GPIO for pin 0x%04X, err %pe\n", + pin, desc); return AE_OK; } @@ -944,7 +951,6 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, bool can_fallback = acpi_can_fallback_to_crs(adev, con_id); struct acpi_gpio_info info = {}; struct gpio_desc *desc; - int ret; desc = __acpi_find_gpio(fwnode, con_id, idx, can_fallback, &info); if (IS_ERR(desc)) @@ -959,10 +965,7 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, acpi_gpio_update_gpiod_flags(dflags, &info); acpi_gpio_update_gpiod_lookup_flags(lookupflags, &info); - /* ACPI uses hundredths of milliseconds units */ - ret = gpio_set_debounce_timeout(desc, info.debounce * 10); - if (ret) - return ERR_PTR(ret); + acpi_gpio_set_debounce_timeout(desc, info.debounce); return desc; } @@ -1096,7 +1099,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, return AE_BAD_PARAMETER; } - length = min_t(u16, agpio->pin_table_length, pin_index + bits); + length = min(agpio->pin_table_length, pin_index + bits); for (i = pin_index; i < length; ++i) { unsigned int pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 175836467f21..3735c9fe1502 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -298,12 +298,13 @@ static const struct file_operations linehandle_fileops = { #endif }; +DEFINE_FREE(linehandle_free, struct linehandle_state *, if (!IS_ERR_OR_NULL(_T)) linehandle_free(_T)) + static int linehandle_create(struct gpio_device *gdev, void __user *ip) { struct gpiohandle_request handlereq; - struct linehandle_state *lh; - struct file *file; - int fd, i, ret; + struct linehandle_state *lh __free(linehandle_free) = NULL; + int i, ret; u32 lflags; if (copy_from_user(&handlereq, ip, sizeof(handlereq))) @@ -327,10 +328,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) lh->label = kstrndup(handlereq.consumer_label, sizeof(handlereq.consumer_label) - 1, GFP_KERNEL); - if (!lh->label) { - ret = -ENOMEM; - goto out_free_lh; - } + if (!lh->label) + return -ENOMEM; } lh->num_descs = handlereq.lines; @@ -340,20 +339,18 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) u32 offset = handlereq.lineoffsets[i]; struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); - if (IS_ERR(desc)) { - ret = PTR_ERR(desc); - goto out_free_lh; - } + if (IS_ERR(desc)) + return PTR_ERR(desc); ret = gpiod_request_user(desc, lh->label); if (ret) - goto out_free_lh; + return ret; lh->descs[i] = desc; linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags); ret = gpiod_set_transitory(desc, false); if (ret < 0) - goto out_free_lh; + return ret; /* * Lines have to be requested explicitly for input @@ -364,11 +361,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_direction_output_nonotify(desc, val); if (ret) - goto out_free_lh; + return ret; } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { ret = gpiod_direction_input_nonotify(desc); if (ret) - goto out_free_lh; + return ret; } gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); @@ -377,44 +374,23 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) offset); } - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_lh; - } - - file = anon_inode_getfile("gpio-linehandle", - &linehandle_fileops, - lh, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } + FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, + anon_inode_getfile("gpio-linehandle", &linehandle_fileops, + lh, O_RDONLY | O_CLOEXEC)); + if (fdf.err) + return fdf.err; + retain_and_null_ptr(lh); - handlereq.fd = fd; - if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); + handlereq.fd = fd_prepare_fd(fdf); + if (copy_to_user(ip, &handlereq, sizeof(handlereq))) return -EFAULT; - } - fd_install(fd, file); + fd_publish(fdf); dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", lh->num_descs); return 0; - -out_put_unused_fd: - put_unused_fd(fd); -out_free_lh: - linehandle_free(lh); - return ret; } #endif /* CONFIG_GPIO_CDEV_V1 */ @@ -676,7 +652,7 @@ static enum hte_return process_hw_ts_thread(void *p) } le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno; - le.offset = gpio_chip_hwgpio(line->desc); + le.offset = gpiod_hwgpio(line->desc); linereq_put_event(lr, &le); @@ -700,7 +676,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) if (READ_ONCE(line->sw_debounced)) { line->total_discard_seq++; line->last_seqno = ts->seq; - mod_delayed_work(system_wq, &line->work, + mod_delayed_work(system_percpu_wq, &line->work, usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); } else { if (unlikely(ts->seq < line->line_seqno)) @@ -793,7 +769,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p) line->line_seqno++; le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno; - le.offset = gpio_chip_hwgpio(line->desc); + le.offset = gpiod_hwgpio(line->desc); linereq_put_event(lr, &le); @@ -841,7 +817,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *p) { struct line *line = p; - mod_delayed_work(system_wq, &line->work, + mod_delayed_work(system_percpu_wq, &line->work, usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); return IRQ_HANDLED; @@ -891,7 +867,7 @@ static void debounce_work_func(struct work_struct *work) lr = line->req; le.timestamp_ns = line_event_timestamp(line); - le.offset = gpio_chip_hwgpio(line->desc); + le.offset = gpiod_hwgpio(line->desc); #ifdef CONFIG_HTE if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) { /* discard events except the last one */ @@ -1591,7 +1567,7 @@ static void linereq_show_fdinfo(struct seq_file *out, struct file *file) for (i = 0; i < lr->num_lines; i++) seq_printf(out, "gpio-line:\t%d\n", - gpio_chip_hwgpio(lr->lines[i].desc)); + gpiod_hwgpio(lr->lines[i].desc)); } #endif @@ -2244,7 +2220,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, return; memset(info, 0, sizeof(*info)); - info->offset = gpio_chip_hwgpio(desc); + info->offset = gpiod_hwgpio(desc); if (desc->name) strscpy(info->name, desc->name, sizeof(info->name)); @@ -2548,8 +2524,15 @@ static int lineinfo_changed_notify(struct notifier_block *nb, container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); struct lineinfo_changed_ctx *ctx; struct gpio_desc *desc = data; + struct file *fp; + + if (!test_bit(gpiod_hwgpio(desc), cdev->watched_lines)) + return NOTIFY_DONE; - if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines)) + /* Keep the file descriptor alive for the duration of the notification. */ + fp = get_file_active(&cdev->fp); + if (!fp) + /* Chardev file descriptor was or is being released. */ return NOTIFY_DONE; /* @@ -2575,8 +2558,6 @@ static int lineinfo_changed_notify(struct notifier_block *nb, /* Keep the GPIO device alive until we emit the event. */ ctx->gdev = gpio_device_get(desc->gdev); ctx->cdev = cdev; - /* Keep the file descriptor alive too. */ - get_file(ctx->cdev->fp); INIT_WORK(&ctx->work, lineinfo_changed_func); queue_work(ctx->gdev->line_state_wq, &ctx->work); @@ -2823,7 +2804,7 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) if (!gc) return -ENODEV; - chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); + gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); return 0; } diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 3bc93ccadb5b..ef3f2ef30cf2 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -34,30 +34,20 @@ EXPORT_SYMBOL_GPL(gpio_free); */ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { - struct gpio_desc *desc; int err; - /* Compatibility: assume unavailable "valid" GPIOs will appear later */ - desc = gpio_to_desc(gpio); - if (!desc) - return -EPROBE_DEFER; - - err = gpiod_request(desc, label); + err = gpio_request(gpio, label); if (err) return err; if (flags & GPIOF_IN) - err = gpiod_direction_input(desc); + err = gpio_direction_input(gpio); else - err = gpiod_direction_output_raw(desc, !!(flags & GPIOF_OUT_INIT_HIGH)); + err = gpio_direction_output(gpio, !!(flags & GPIOF_OUT_INIT_HIGH)); if (err) - goto free_gpio; - - return 0; + gpio_free(gpio); - free_gpio: - gpiod_free(desc); return err; } EXPORT_SYMBOL_GPL(gpio_request_one); @@ -78,11 +68,9 @@ int gpio_request(unsigned gpio, const char *label) } EXPORT_SYMBOL_GPL(gpio_request); -static void devm_gpio_release(struct device *dev, void *res) +static void devm_gpio_release(void *gpio) { - unsigned *gpio = res; - - gpio_free(*gpio); + gpio_free((unsigned)(unsigned long)gpio); } /** @@ -100,22 +88,22 @@ static void devm_gpio_release(struct device *dev, void *res) int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label) { - unsigned *dr; int rc; - dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); - if (!dr) - return -ENOMEM; + rc = gpio_request(gpio, label); + if (rc) + return rc; + + if (flags & GPIOF_IN) + rc = gpio_direction_input(gpio); + else + rc = gpio_direction_output(gpio, !!(flags & GPIOF_OUT_INIT_HIGH)); - rc = gpio_request_one(gpio, flags, label); if (rc) { - devres_free(dr); + gpio_free(gpio); return rc; } - *dr = gpio; - devres_add(dev, dr); - - return 0; + return devm_add_action_or_reset(dev, devm_gpio_release, (void *)(unsigned long)gpio); } EXPORT_SYMBOL_GPL(devm_gpio_request_one); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index fad4edf9cc5c..8657379e9165 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1031,85 +1031,6 @@ static int of_gpio_threecell_xlate(struct gpio_chip *gc, return gpiospec->args[1]; } -#if IS_ENABLED(CONFIG_OF_GPIO_MM_GPIOCHIP) -#include <linux/gpio/legacy-of-mm-gpiochip.h> -/** - * of_mm_gpiochip_add_data - Add memory mapped GPIO chip (bank) - * @np: device node of the GPIO chip - * @mm_gc: pointer to the of_mm_gpio_chip allocated structure - * @data: driver data to store in the struct gpio_chip - * - * To use this function you should allocate and fill mm_gc with: - * - * 1) In the gpio_chip structure: - * - all the callbacks - * - of_gpio_n_cells - * - of_xlate callback (optional) - * - * 3) In the of_mm_gpio_chip structure: - * - save_regs callback (optional) - * - * If succeeded, this function will map bank's memory and will - * do all necessary work for you. Then you'll able to use .regs - * to manage GPIOs from the callbacks. - * - * Returns: - * 0 on success, or negative errno on failure. - */ -int of_mm_gpiochip_add_data(struct device_node *np, - struct of_mm_gpio_chip *mm_gc, - void *data) -{ - int ret = -ENOMEM; - struct gpio_chip *gc = &mm_gc->gc; - - gc->label = kasprintf(GFP_KERNEL, "%pOF", np); - if (!gc->label) - goto err0; - - mm_gc->regs = of_iomap(np, 0); - if (!mm_gc->regs) - goto err1; - - gc->base = -1; - - if (mm_gc->save_regs) - mm_gc->save_regs(mm_gc); - - fwnode_handle_put(mm_gc->gc.fwnode); - mm_gc->gc.fwnode = fwnode_handle_get(of_fwnode_handle(np)); - - ret = gpiochip_add_data(gc, data); - if (ret) - goto err2; - - return 0; -err2: - of_node_put(np); - iounmap(mm_gc->regs); -err1: - kfree(gc->label); -err0: - pr_err("%pOF: GPIO chip registration failed with status %d\n", np, ret); - return ret; -} -EXPORT_SYMBOL_GPL(of_mm_gpiochip_add_data); - -/** - * of_mm_gpiochip_remove - Remove memory mapped GPIO chip (bank) - * @mm_gc: pointer to the of_mm_gpio_chip allocated structure - */ -void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc) -{ - struct gpio_chip *gc = &mm_gc->gc; - - gpiochip_remove(gc); - iounmap(mm_gc->regs); - kfree(gc->label); -} -EXPORT_SYMBOL_GPL(of_mm_gpiochip_remove); -#endif - #ifdef CONFIG_PINCTRL static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c new file mode 100644 index 000000000000..8bdd107b1ad1 --- /dev/null +++ b/drivers/gpio/gpiolib-shared.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Linaro Ltd. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/auxiliary_bus.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/fwnode.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/machine.h> +#include <linux/idr.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/overflow.h> +#include <linux/printk.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include "gpiolib.h" +#include "gpiolib-shared.h" + +/* Represents a single reference to a GPIO pin. */ +struct gpio_shared_ref { + struct list_head list; + /* Firmware node associated with this GPIO's consumer. */ + struct fwnode_handle *fwnode; + /* GPIO flags this consumer uses for the request. */ + enum gpiod_flags flags; + char *con_id; + int dev_id; + struct auxiliary_device adev; + struct gpiod_lookup_table *lookup; +}; + +/* Represents a single GPIO pin. */ +struct gpio_shared_entry { + struct list_head list; + /* Firmware node associated with the GPIO controller. */ + struct fwnode_handle *fwnode; + /* Hardware offset of the GPIO within its chip. */ + unsigned int offset; + /* Index in the property value array. */ + size_t index; + struct mutex lock; + struct gpio_shared_desc *shared_desc; + struct kref ref; + struct list_head refs; +}; + +static LIST_HEAD(gpio_shared_list); +static DEFINE_MUTEX(gpio_shared_lock); +static DEFINE_IDA(gpio_shared_ida); + +#if IS_ENABLED(CONFIG_OF) +static struct gpio_shared_entry * +gpio_shared_find_entry(struct fwnode_handle *controller_node, + unsigned int offset) +{ + struct gpio_shared_entry *entry; + + list_for_each_entry(entry, &gpio_shared_list, list) { + if (entry->fwnode == controller_node && entry->offset == offset) + return entry; + } + + return NULL; +} + +/* Handle all special nodes that we should ignore. */ +static bool gpio_shared_of_node_ignore(struct device_node *node) +{ + /* + * __symbols__ is a special, internal node and should not be considered + * when scanning for shared GPIOs. + */ + if (of_node_name_eq(node, "__symbols__")) + return true; + + /* + * GPIO hogs have a "gpios" property which is not a phandle and can't + * possibly refer to a shared GPIO. + */ + if (of_property_present(node, "gpio-hog")) + return true; + + return false; +} + +static int gpio_shared_of_traverse(struct device_node *curr) +{ + struct gpio_shared_entry *entry; + size_t con_id_len, suffix_len; + struct fwnode_handle *fwnode; + struct of_phandle_args args; + struct property *prop; + unsigned int offset; + const char *suffix; + int ret, count, i; + + if (gpio_shared_of_node_ignore(curr)) + return 0; + + for_each_property_of_node(curr, prop) { + /* + * The standard name for a GPIO property is "foo-gpios" + * or "foo-gpio". Some bindings also use "gpios" or "gpio". + * There are some legacy device-trees which have a different + * naming convention and for which we have rename quirks in + * place in gpiolib-of.c. I don't think any of them require + * support for shared GPIOs so for now let's just ignore + * them. We can always just export the quirk list and + * iterate over it here. + */ + if (!strends(prop->name, "-gpios") && + !strends(prop->name, "-gpio") && + strcmp(prop->name, "gpios") != 0 && + strcmp(prop->name, "gpio") != 0) + continue; + + count = of_count_phandle_with_args(curr, prop->name, + "#gpio-cells"); + if (count <= 0) + continue; + + for (i = 0; i < count; i++) { + struct device_node *np __free(device_node) = NULL; + + ret = of_parse_phandle_with_args(curr, prop->name, + "#gpio-cells", i, + &args); + if (ret) + continue; + + np = args.np; + + if (!of_property_present(np, "gpio-controller")) + continue; + + /* + * We support 1, 2 and 3 cell GPIO bindings in the + * kernel currently. There's only one old MIPS dts that + * has a one-cell binding but there's no associated + * consumer so it may as well be an error. There don't + * seem to be any 3-cell users of non-exclusive GPIOs, + * so we can skip this as well. Let's occupy ourselves + * with the predominant 2-cell binding with the first + * cell indicating the hardware offset of the GPIO and + * the second defining the GPIO flags of the request. + */ + if (args.args_count != 2) + continue; + + fwnode = of_fwnode_handle(args.np); + offset = args.args[0]; + + entry = gpio_shared_find_entry(fwnode, offset); + if (!entry) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->fwnode = fwnode_handle_get(fwnode); + entry->offset = offset; + entry->index = count; + INIT_LIST_HEAD(&entry->refs); + mutex_init(&entry->lock); + + list_add_tail(&entry->list, &gpio_shared_list); + } + + struct gpio_shared_ref *ref __free(kfree) = + kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) + return -ENOMEM; + + ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr)); + ref->flags = args.args[1]; + + if (strends(prop->name, "gpios")) + suffix = "-gpios"; + else if (strends(prop->name, "gpio")) + suffix = "-gpio"; + else + suffix = NULL; + if (!suffix) + continue; + + /* We only set con_id if there's actually one. */ + if (strcmp(prop->name, "gpios") && strcmp(prop->name, "gpio")) { + ref->con_id = kstrdup(prop->name, GFP_KERNEL); + if (!ref->con_id) + return -ENOMEM; + + con_id_len = strlen(ref->con_id); + suffix_len = strlen(suffix); + + ref->con_id[con_id_len - suffix_len] = '\0'; + } + + ref->dev_id = ida_alloc(&gpio_shared_ida, GFP_KERNEL); + if (ref->dev_id < 0) { + kfree(ref->con_id); + return -ENOMEM; + } + + if (!list_empty(&entry->refs)) + pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n", + entry->offset, fwnode_get_name(entry->fwnode)); + + list_add_tail(&no_free_ptr(ref)->list, &entry->refs); + } + } + + for_each_child_of_node_scoped(curr, child) { + ret = gpio_shared_of_traverse(child); + if (ret) + return ret; + } + + return 0; +} + +static int gpio_shared_of_scan(void) +{ + if (of_root) + return gpio_shared_of_traverse(of_root); + + return 0; +} +#else +static int gpio_shared_of_scan(void) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static void gpio_shared_adev_release(struct device *dev) +{ + +} + +static int gpio_shared_make_adev(struct gpio_device *gdev, + struct gpio_shared_entry *entry, + struct gpio_shared_ref *ref) +{ + struct auxiliary_device *adev = &ref->adev; + int ret; + + lockdep_assert_held(&gpio_shared_lock); + + memset(adev, 0, sizeof(*adev)); + + adev->id = ref->dev_id; + adev->name = "proxy"; + adev->dev.parent = gdev->dev.parent; + adev->dev.platform_data = entry; + adev->dev.release = gpio_shared_adev_release; + + ret = auxiliary_device_init(adev); + if (ret) + return ret; + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n", + dev_name(&adev->dev), gpio_device_get_label(gdev)); + + return 0; +} + +#if IS_ENABLED(CONFIG_RESET_GPIO) +/* + * Special case: reset-gpio is an auxiliary device that's created dynamically + * and put in between the GPIO controller and consumers of shared GPIOs + * referred to by the "reset-gpios" property. + * + * If the supposed consumer of a shared GPIO didn't match any of the mappings + * we created when scanning the firmware nodes, it's still possible that it's + * the reset-gpio device which didn't exist at the time of the scan. + * + * This function verifies it an return true if it's the case. + */ +static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, + struct gpio_shared_entry *entry, + struct gpio_shared_ref *ref) +{ + struct fwnode_handle *reset_fwnode = dev_fwnode(consumer); + struct fwnode_reference_args ref_args, aux_args; + struct device *parent = consumer->parent; + bool match; + int ret; + + /* The reset-gpio device must have a parent AND a firmware node. */ + if (!parent || !reset_fwnode) + return false; + + /* + * FIXME: use device_is_compatible() once the reset-gpio drivers gains + * a compatible string which it currently does not have. + */ + if (!strstarts(dev_name(consumer), "reset.gpio.")) + return false; + + /* + * Parent of the reset-gpio auxiliary device is the GPIO chip whose + * fwnode we stored in the entry structure. + */ + if (!device_match_fwnode(parent, entry->fwnode)) + return false; + + /* + * The device associated with the shared reference's firmware node is + * the consumer of the reset control exposed by the reset-gpio device. + * It must have a "reset-gpios" property that's referencing the entry's + * firmware node. + * + * The reference args must agree between the real consumer and the + * auxiliary reset-gpio device. + */ + ret = fwnode_property_get_reference_args(ref->fwnode, "reset-gpios", + NULL, 2, 0, &ref_args); + if (ret) + return false; + + ret = fwnode_property_get_reference_args(reset_fwnode, "reset-gpios", + NULL, 2, 0, &aux_args); + if (ret) { + fwnode_handle_put(ref_args.fwnode); + return false; + } + + match = ((ref_args.fwnode == entry->fwnode) && + (aux_args.fwnode == entry->fwnode) && + (ref_args.args[0] == aux_args.args[0])); + + fwnode_handle_put(ref_args.fwnode); + fwnode_handle_put(aux_args.fwnode); + return match; +} +#else +static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, + struct gpio_shared_entry *entry, + struct gpio_shared_ref *ref) +{ + return false; +} +#endif /* CONFIG_RESET_GPIO */ + +int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) +{ + const char *dev_id = dev_name(consumer); + struct gpio_shared_entry *entry; + struct gpio_shared_ref *ref; + + struct gpiod_lookup_table *lookup __free(kfree) = + kzalloc(struct_size(lookup, table, 2), GFP_KERNEL); + if (!lookup) + return -ENOMEM; + + guard(mutex)(&gpio_shared_lock); + + list_for_each_entry(entry, &gpio_shared_list, list) { + list_for_each_entry(ref, &entry->refs, list) { + if (!device_match_fwnode(consumer, ref->fwnode) && + !gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) + continue; + + /* We've already done that on a previous request. */ + if (ref->lookup) + return 0; + + char *key __free(kfree) = + kasprintf(GFP_KERNEL, + KBUILD_MODNAME ".proxy.%u", + ref->adev.id); + if (!key) + return -ENOMEM; + + pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n", + dev_id, key, ref->con_id ?: "none"); + + lookup->dev_id = dev_id; + lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0, + ref->con_id, lflags); + + gpiod_add_lookup_table(no_free_ptr(lookup)); + + return 0; + } + } + + /* We warn here because this can only happen if the programmer borked. */ + WARN_ON(1); + return -ENOENT; +} + +static void gpio_shared_remove_adev(struct auxiliary_device *adev) +{ + lockdep_assert_held(&gpio_shared_lock); + + auxiliary_device_uninit(adev); + auxiliary_device_delete(adev); +} + +int gpio_device_setup_shared(struct gpio_device *gdev) +{ + struct gpio_shared_entry *entry; + struct gpio_shared_ref *ref; + unsigned long *flags; + int ret; + + guard(mutex)(&gpio_shared_lock); + + list_for_each_entry(entry, &gpio_shared_list, list) { + list_for_each_entry(ref, &entry->refs, list) { + if (gdev->dev.parent == &ref->adev.dev) { + /* + * This is a shared GPIO proxy. Mark its + * descriptor as such and return here. + */ + __set_bit(GPIOD_FLAG_SHARED_PROXY, + &gdev->descs[0].flags); + return 0; + } + } + } + + /* + * This is not a shared GPIO proxy but it still may be the device + * exposing shared pins. Find them and create the proxy devices. + */ + list_for_each_entry(entry, &gpio_shared_list, list) { + if (!device_match_fwnode(&gdev->dev, entry->fwnode)) + continue; + + if (list_count_nodes(&entry->refs) <= 1) + continue; + + flags = &gdev->descs[entry->offset].flags; + + __set_bit(GPIOD_FLAG_SHARED, flags); + /* + * Shared GPIOs are not requested via the normal path. Make + * them inaccessible to anyone even before we register the + * chip. + */ + __set_bit(GPIOD_FLAG_REQUESTED, flags); + + pr_debug("GPIO %u owned by %s is shared by multiple consumers\n", + entry->offset, gpio_device_get_label(gdev)); + + list_for_each_entry(ref, &entry->refs, list) { + pr_debug("Setting up a shared GPIO entry for %s\n", + fwnode_get_name(ref->fwnode)); + + ret = gpio_shared_make_adev(gdev, entry, ref); + if (ret) + return ret; + } + } + + return 0; +} + +void gpio_device_teardown_shared(struct gpio_device *gdev) +{ + struct gpio_shared_entry *entry; + struct gpio_shared_ref *ref; + + guard(mutex)(&gpio_shared_lock); + + list_for_each_entry(entry, &gpio_shared_list, list) { + if (!device_match_fwnode(&gdev->dev, entry->fwnode)) + continue; + + list_for_each_entry(ref, &entry->refs, list) { + gpiod_remove_lookup_table(ref->lookup); + kfree(ref->lookup->table[0].key); + kfree(ref->lookup); + ref->lookup = NULL; + gpio_shared_remove_adev(&ref->adev); + } + } +} + +static void gpio_shared_release(struct kref *kref) +{ + struct gpio_shared_entry *entry = + container_of(kref, struct gpio_shared_entry, ref); + struct gpio_shared_desc *shared_desc; + + guard(mutex)(&entry->lock); + + shared_desc = entry->shared_desc; + gpio_device_put(shared_desc->desc->gdev); + if (shared_desc->can_sleep) + mutex_destroy(&shared_desc->mutex); + kfree(shared_desc); + entry->shared_desc = NULL; +} + +static void gpiod_shared_put(void *data) +{ + struct gpio_shared_entry *entry = data; + + lockdep_assert_not_held(&gpio_shared_lock); + + kref_put(&entry->ref, gpio_shared_release); +} + +static struct gpio_shared_desc * +gpiod_shared_desc_create(struct gpio_shared_entry *entry) +{ + struct gpio_shared_desc *shared_desc; + struct gpio_device *gdev; + + lockdep_assert_held(&entry->lock); + + shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL); + if (!shared_desc) + return ERR_PTR(-ENOMEM); + + gdev = gpio_device_find_by_fwnode(entry->fwnode); + if (!gdev) { + kfree(shared_desc); + return ERR_PTR(-EPROBE_DEFER); + } + + shared_desc->desc = &gdev->descs[entry->offset]; + shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc); + if (shared_desc->can_sleep) + mutex_init(&shared_desc->mutex); + else + spin_lock_init(&shared_desc->spinlock); + + return shared_desc; +} + +struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev) +{ + struct gpio_shared_desc *shared_desc; + struct gpio_shared_entry *entry; + int ret; + + lockdep_assert_not_held(&gpio_shared_lock); + + entry = dev_get_platdata(dev); + if (WARN_ON(!entry)) + /* Programmer bug */ + return ERR_PTR(-ENOENT); + + scoped_guard(mutex, &entry->lock) { + if (entry->shared_desc) { + kref_get(&entry->ref); + shared_desc = entry->shared_desc; + } else { + shared_desc = gpiod_shared_desc_create(entry); + if (IS_ERR(shared_desc)) + return ERR_CAST(shared_desc); + + kref_init(&entry->ref); + entry->shared_desc = shared_desc; + } + + pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n", + dev_name(dev), gpiod_hwgpio(shared_desc->desc), + gpio_device_get_label(shared_desc->desc->gdev)); + } + + ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry); + if (ret) + return ERR_PTR(ret); + + return shared_desc; +} +EXPORT_SYMBOL_GPL(devm_gpiod_shared_get); + +static void gpio_shared_drop_ref(struct gpio_shared_ref *ref) +{ + list_del(&ref->list); + kfree(ref->con_id); + ida_free(&gpio_shared_ida, ref->dev_id); + fwnode_handle_put(ref->fwnode); + kfree(ref); +} + +static void gpio_shared_drop_entry(struct gpio_shared_entry *entry) +{ + list_del(&entry->list); + mutex_destroy(&entry->lock); + fwnode_handle_put(entry->fwnode); + kfree(entry); +} + +/* + * This is only called if gpio_shared_init() fails so it's in fact __init and + * not __exit. + */ +static void __init gpio_shared_teardown(void) +{ + struct gpio_shared_entry *entry, *epos; + struct gpio_shared_ref *ref, *rpos; + + list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) { + list_for_each_entry_safe(ref, rpos, &entry->refs, list) + gpio_shared_drop_ref(ref); + + gpio_shared_drop_entry(entry); + } +} + +static void gpio_shared_free_exclusive(void) +{ + struct gpio_shared_entry *entry, *epos; + + list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) { + if (list_count_nodes(&entry->refs) > 1) + continue; + + gpio_shared_drop_ref(list_first_entry(&entry->refs, + struct gpio_shared_ref, + list)); + gpio_shared_drop_entry(entry); + } +} + +static int __init gpio_shared_init(void) +{ + int ret; + + /* Right now, we only support OF-based systems. */ + ret = gpio_shared_of_scan(); + if (ret) { + gpio_shared_teardown(); + pr_err("Failed to scan OF nodes for shared GPIOs: %d\n", ret); + return ret; + } + + gpio_shared_free_exclusive(); + + pr_debug("Finished scanning firmware nodes for shared GPIOs\n"); + return 0; +} +postcore_initcall(gpio_shared_init); diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h new file mode 100644 index 000000000000..667dbdff3585 --- /dev/null +++ b/drivers/gpio/gpiolib-shared.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LINUX_GPIO_SHARED_H +#define __LINUX_GPIO_SHARED_H + +#include <linux/cleanup.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> + +struct gpio_device; +struct gpio_desc; +struct device; + +#if IS_ENABLED(CONFIG_GPIO_SHARED) + +int gpio_device_setup_shared(struct gpio_device *gdev); +void gpio_device_teardown_shared(struct gpio_device *gdev); +int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags); + +#else + +static inline int gpio_device_setup_shared(struct gpio_device *gdev) +{ + return 0; +} + +static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { } + +static inline int gpio_shared_add_proxy_lookup(struct device *consumer, + unsigned long lflags) +{ + return 0; +} + +#endif /* CONFIG_GPIO_SHARED */ + +struct gpio_shared_desc { + struct gpio_desc *desc; + bool can_sleep; + unsigned long cfg; + unsigned int usecnt; + unsigned int highcnt; + union { + struct mutex mutex; + spinlock_t spinlock; + }; +}; + +struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev); + +DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc, + if (_T->lock->can_sleep) + mutex_lock(&_T->lock->mutex); + else + spin_lock_irqsave(&_T->lock->spinlock, _T->flags), + if (_T->lock->can_sleep) + mutex_unlock(&_T->lock->mutex); + else + spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags), + unsigned long flags) + +static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc) +{ + if (shared_desc->can_sleep) + lockdep_assert_held(&shared_desc->mutex); + else + lockdep_assert_held(&shared_desc->spinlock); +} + +#endif /* __LINUX_GPIO_SHARED_H */ diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index f21dbc28cf2c..b44f35d68459 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -31,7 +31,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) gdev_node = to_software_node(fwnode); if (!gdev_node || !gdev_node->name) - return ERR_PTR(-EINVAL); + goto fwnode_lookup; /* * Check for a special node that identifies undefined GPIOs, this is @@ -41,7 +41,8 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) !strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME)) return ERR_PTR(-ENOENT); - gdev = gpio_device_find_by_label(gdev_node->name); +fwnode_lookup: + gdev = gpio_device_find_by_fwnode(fwnode); return gdev ?: ERR_PTR(-EPROBE_DEFER); } diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 9a849245b358..cd553acf3055 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -244,7 +244,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags) * Remove this redundant call (along with the corresponding unlock) * when those drivers have been fixed. */ - ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); + ret = gpiochip_lock_as_irq(guard.gc, gpiod_hwgpio(desc)); if (ret < 0) goto err_clr_bits; @@ -258,7 +258,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags) return 0; err_unlock: - gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc)); err_clr_bits: clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags); clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags); @@ -280,7 +280,7 @@ static void gpio_sysfs_free_irq(struct gpiod_data *data) data->irq_flags = 0; free_irq(data->irq, data); - gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc)); clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags); clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags); } @@ -478,10 +478,10 @@ static int export_gpio_desc(struct gpio_desc *desc) if (!guard.gc) return -ENODEV; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); if (!gpiochip_line_is_valid(guard.gc, offset)) { pr_debug_ratelimited("%s: GPIO %d masked\n", __func__, - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); return -EINVAL; } @@ -823,7 +823,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) } desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u", - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); if (!desc_data->chip_attr_group.name) { status = -ENOMEM; goto err_put_dirent; @@ -843,7 +843,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) if (status) goto err_free_name; - path = kasprintf(GFP_KERNEL, "gpio%u/value", gpio_chip_hwgpio(desc)); + path = kasprintf(GFP_KERNEL, "gpio%u/value", gpiod_hwgpio(desc)); if (!path) { status = -ENOMEM; goto err_remove_groups; @@ -1091,7 +1091,7 @@ static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data) ret = gpiochip_sysfs_register(gdev); if (ret) - chip_err(gc, "failed to register the sysfs entry: %d\n", ret); + gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret); return 0; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9952e412da50..91e0c384f34a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -37,6 +37,7 @@ #include "gpiolib-acpi.h" #include "gpiolib-cdev.h" #include "gpiolib-of.h" +#include "gpiolib-shared.h" #include "gpiolib-swnode.h" #include "gpiolib-sysfs.h" #include "gpiolib.h" @@ -235,6 +236,19 @@ int desc_to_gpio(const struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(desc_to_gpio); +/** + * gpiod_hwgpio - Return the GPIO number of the passed descriptor relative to + * its chip. + * @desc: GPIO descriptor + * + * Returns: + * Hardware offset of the GPIO represented by the descriptor. + */ +int gpiod_hwgpio(const struct gpio_desc *desc) +{ + return desc - &desc->gdev->descs[0]; +} +EXPORT_SYMBOL_GPL(gpiod_hwgpio); /** * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs @@ -443,7 +457,7 @@ int gpiod_get_direction(struct gpio_desc *desc) if (!guard.gc) return -ENODEV; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); flags = READ_ONCE(desc->flags); /* @@ -921,8 +935,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) desc = gpiochip_get_desc(gc, hog->chip_hwnum); if (IS_ERR(desc)) { - chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__, - PTR_ERR(desc)); + gpiochip_err(gc, "%s: unable to get GPIO desc: %ld\n", + __func__, PTR_ERR(desc)); return; } @@ -1124,7 +1138,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiodev_add_to_list_unlocked(gdev); if (ret) { - chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); + gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n"); goto err_free_label; } } @@ -1200,6 +1214,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_remove_irqchip_mask; + ret = gpio_device_setup_shared(gdev); + if (ret) + goto err_remove_irqchip; + /* * By first adding the chardev, and then adding the device, * we get a device node entry in sysfs under @@ -1211,10 +1229,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (gpiolib_initialized) { ret = gpiochip_setup_dev(gdev); if (ret) - goto err_remove_irqchip; + goto err_teardown_shared; } + return 0; +err_teardown_shared: + gpio_device_teardown_shared(gdev); err_remove_irqchip: gpiochip_irqchip_remove(gc); err_remove_irqchip_mask: @@ -1283,6 +1304,7 @@ void gpiochip_remove(struct gpio_chip *gc) /* Numb the device, cancelling all outstanding operations */ rcu_assign_pointer(gdev->chip, NULL); synchronize_srcu(&gdev->srcu); + gpio_device_teardown_shared(gdev); gpiochip_irqchip_remove(gc); acpi_gpiochip_remove(gc); of_gpiochip_remove(gc); @@ -1528,8 +1550,7 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc, &parent_hwirq, &parent_type); if (ret) { - chip_err(gc, "skip set-up on hwirq %d\n", - i); + gpiochip_err(gc, "skip set-up on hwirq %d\n", i); continue; } @@ -1542,15 +1563,14 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc, ret = irq_domain_alloc_irqs(gc->irq.domain, 1, NUMA_NO_NODE, &fwspec); if (ret < 0) { - chip_err(gc, - "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", - i, parent_hwirq, - ret); + gpiochip_err(gc, + "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", + i, parent_hwirq, ret); } } } - chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); + gpiochip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); return; } @@ -1602,15 +1622,15 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (ret) return ret; - chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); + gpiochip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); ret = girq->child_to_parent_hwirq(gc, hwirq, type, &parent_hwirq, &parent_type); if (ret) { - chip_err(gc, "can't look up hwirq %lu\n", hwirq); + gpiochip_err(gc, "can't look up hwirq %lu\n", hwirq); return ret; } - chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq); + gpiochip_dbg(gc, "found parent hwirq %u\n", parent_hwirq); /* * We set handle_bad_irq because the .set_type() should @@ -1631,8 +1651,8 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (ret) return ret; - chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", - irq, parent_hwirq); + gpiochip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", + irq, parent_hwirq); irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key); ret = irq_domain_alloc_irqs_parent(d, irq, 1, &gpio_parent_fwspec); /* @@ -1642,9 +1662,9 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, 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); + gpiochip_err(gc, + "failed to allocate parent hwirq %d for hwirq %lu\n", + parent_hwirq, hwirq); return ret; } @@ -1720,7 +1740,7 @@ static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc) if (!gc->irq.child_to_parent_hwirq || !gc->irq.fwnode) { - chip_err(gc, "missing irqdomain vital data\n"); + gpiochip_err(gc, "missing irqdomain vital data\n"); return ERR_PTR(-EINVAL); } @@ -1993,7 +2013,7 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc) if (irqchip->flags & IRQCHIP_IMMUTABLE) return; - chip_warn(gc, "not an immutable chip, please consider fixing it!\n"); + gpiochip_warn(gc, "not an immutable chip, please consider fixing it!\n"); if (!irqchip->irq_request_resources && !irqchip->irq_release_resources) { @@ -2009,8 +2029,8 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc) * ...and if so, give a gentle warning that this is bad * practice. */ - chip_info(gc, - "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n"); + gpiochip_info(gc, + "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n"); return; } @@ -2039,7 +2059,8 @@ static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc, return -EINVAL; if (gc->to_irq) - chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__); + gpiochip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", + __func__); gc->to_irq = gpiochip_to_irq; gc->irq.domain = domain; @@ -2080,7 +2101,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, return 0; if (gc->irq.parent_handler && gc->can_sleep) { - chip_err(gc, "you cannot have chained interrupts on a chip that may sleep\n"); + gpiochip_err(gc, "you cannot have chained interrupts on a chip that may sleep\n"); return -EINVAL; } @@ -2316,10 +2337,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, int ret; pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); - if (!pin_range) { - chip_err(gc, "failed to allocate pin ranges\n"); + if (!pin_range) return -ENOMEM; - } /* Use local offset as range ID */ pin_range->range.id = gpio_offset; @@ -2338,7 +2357,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, pinctrl_add_gpio_range(pctldev, &pin_range->range); - chip_dbg(gc, "created GPIO range %d->%d ==> %s PINGRP %s\n", + gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s PINGRP %s\n", gpio_offset, gpio_offset + pin_range->range.npins - 1, pinctrl_dev_get_devname(pctldev), pin_group); @@ -2379,10 +2398,8 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc, int ret; pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); - if (!pin_range) { - chip_err(gc, "failed to allocate pin ranges\n"); + if (!pin_range) return -ENOMEM; - } /* Use local offset as range ID */ pin_range->range.id = gpio_offset; @@ -2396,19 +2413,18 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc, &pin_range->range); if (IS_ERR(pin_range->pctldev)) { ret = PTR_ERR(pin_range->pctldev); - chip_err(gc, "could not create pin range\n"); + gpiochip_err(gc, "could not create pin range\n"); kfree(pin_range); return ret; } if (pin_range->range.pins) - chip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }", - gpio_offset, gpio_offset + npins - 1, - pinctl_name, npins, pins[0]); + gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }", + gpio_offset, gpio_offset + npins - 1, + pinctl_name, npins, pins[0]); else - chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n", - gpio_offset, gpio_offset + npins - 1, - pinctl_name, - pin_offset, pin_offset + npins - 1); + gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n", + gpio_offset, gpio_offset + npins - 1, pinctl_name, + pin_offset, pin_offset + npins - 1); list_add_tail(&pin_range->node, &gdev->pin_ranges); @@ -2452,7 +2468,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (test_and_set_bit(GPIOD_FLAG_REQUESTED, &desc->flags)) return -EBUSY; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); if (!gpiochip_line_is_valid(guard.gc, offset)) return -EINVAL; @@ -2514,7 +2530,7 @@ static void gpiod_free_commit(struct gpio_desc *desc) if (guard.gc && test_bit(GPIOD_FLAG_REQUESTED, &flags)) { if (guard.gc->free) - guard.gc->free(guard.gc, gpio_chip_hwgpio(desc)); + guard.gc->free(guard.gc, gpiod_hwgpio(desc)); clear_bit(GPIOD_FLAG_ACTIVE_LOW, &flags); clear_bit(GPIOD_FLAG_REQUESTED, &flags); @@ -2618,7 +2634,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, int ret; if (IS_ERR(desc)) { - chip_err(gc, "failed to get GPIO %s descriptor\n", name); + gpiochip_err(gc, "failed to get GPIO %s descriptor\n", name); return desc; } @@ -2629,7 +2645,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, ret = gpiod_configure_flags(desc, label, lflags, dflags); if (ret) { gpiod_free_commit(desc); - chip_err(gc, "setup of own GPIO %s failed\n", name); + gpiochip_err(gc, "setup of own GPIO %s failed\n", name); return ERR_PTR(ret); } @@ -2674,7 +2690,7 @@ int gpio_do_set_config(struct gpio_desc *desc, unsigned long config) if (!guard.gc->set_config) return -ENOTSUPP; - ret = guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config); + ret = guard.gc->set_config(guard.gc, gpiod_hwgpio(desc), config); if (ret > 0) ret = -EBADE; @@ -2705,7 +2721,7 @@ static int gpio_set_config_with_argument_optional(struct gpio_desc *desc, u32 argument) { struct device *dev = &desc->gdev->dev; - int gpio = gpio_chip_hwgpio(desc); + int gpio = gpiod_hwgpio(desc); int ret; ret = gpio_set_config_with_argument(desc, mode, argument); @@ -2868,9 +2884,9 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) */ if (guard.gc->direction_input) { ret = gpiochip_direction_input(guard.gc, - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); } else if (guard.gc->get_direction) { - dir = gpiochip_get_direction(guard.gc, gpio_chip_hwgpio(desc)); + dir = gpiochip_get_direction(guard.gc, gpiod_hwgpio(desc)); if (dir < 0) return dir; @@ -2929,12 +2945,12 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) if (guard.gc->direction_output) { ret = gpiochip_direction_output(guard.gc, - gpio_chip_hwgpio(desc), val); + gpiod_hwgpio(desc), val); } else { /* Check that we are in output mode if we can */ if (guard.gc->get_direction) { dir = gpiochip_get_direction(guard.gc, - gpio_chip_hwgpio(desc)); + gpiod_hwgpio(desc)); if (dir < 0) return dir; @@ -2949,7 +2965,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) * If we can't actively set the direction, we are some * output-only chip, so just drive the output as desired. */ - ret = gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), val); + ret = gpiochip_set(guard.gc, gpiod_hwgpio(desc), val); if (ret) return ret; } @@ -3100,7 +3116,7 @@ int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) } ret = guard.gc->en_hw_timestamp(guard.gc, - gpio_chip_hwgpio(desc), flags); + gpiod_hwgpio(desc), flags); if (ret) gpiod_warn(desc, "%s: hw ts request failed\n", __func__); @@ -3132,7 +3148,7 @@ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) return -ENOTSUPP; } - ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc), + ret = guard.gc->dis_hw_timestamp(guard.gc, gpiod_hwgpio(desc), flags); if (ret) gpiod_warn(desc, "%s: hw ts release failed\n", __func__); @@ -3263,7 +3279,7 @@ static int gpiochip_get(struct gpio_chip *gc, unsigned int offset) static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc) { - return gc->get ? gpiochip_get(gc, gpio_chip_hwgpio(desc)) : -EIO; + return gc->get ? gpiochip_get(gc, gpiod_hwgpio(desc)) : -EIO; } /* I/O calls are only valid after configuration completed; the relevant @@ -3423,7 +3439,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, first = i; do { const struct gpio_desc *desc = desc_array[i]; - int hwgpio = gpio_chip_hwgpio(desc); + int hwgpio = gpiod_hwgpio(desc); __set_bit(hwgpio, mask); i++; @@ -3445,7 +3461,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, for (j = first; j < i; ) { const struct gpio_desc *desc = desc_array[j]; - int hwgpio = gpio_chip_hwgpio(desc); + int hwgpio = gpiod_hwgpio(desc); int value = test_bit(hwgpio, bits); if (!raw && test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags)) @@ -3582,7 +3598,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value); */ static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) { - int ret = 0, offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpiod_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) @@ -3611,7 +3627,7 @@ static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) */ static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) { - int ret = 0, offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpiod_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) @@ -3643,7 +3659,7 @@ static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) return -ENODEV; trace_gpio_value(desc_to_gpio(desc), 0, value); - return gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), value); + return gpiochip_set(guard.gc, gpiod_hwgpio(desc), value); } /* @@ -3766,7 +3782,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, do { struct gpio_desc *desc = desc_array[i]; - int hwgpio = gpio_chip_hwgpio(desc); + int hwgpio = gpiod_hwgpio(desc); int value = test_bit(i, value_bitmap); if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags))) @@ -3982,6 +3998,26 @@ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) EXPORT_SYMBOL_GPL(gpiod_set_consumer_name); /** + * gpiod_is_shared() - check if this GPIO can be shared by multiple consumers + * @desc: GPIO to inspect + * + * Returns: + * True if this GPIO can be shared by multiple consumers at once. False if it's + * a regular, exclusive GPIO. + * + * Note: + * This function returning true does not mean that this GPIO is currently being + * shared. It means the GPIO core has registered the fact that the firmware + * configuration indicates that it can be shared by multiple consumers and is + * in charge of arbitrating the access. + */ +bool gpiod_is_shared(const struct gpio_desc *desc) +{ + return test_bit(GPIOD_FLAG_SHARED_PROXY, &desc->flags); +} +EXPORT_SYMBOL_GPL(gpiod_is_shared); + +/** * gpiod_to_irq() - return the IRQ corresponding to a GPIO * @desc: gpio whose IRQ will be returned (already requested) * @@ -4006,7 +4042,7 @@ int gpiod_to_irq(const struct gpio_desc *desc) if (!gc) return -ENODEV; - offset = gpio_chip_hwgpio(desc); + offset = gpiod_hwgpio(desc); if (gc->to_irq) { ret = gc->to_irq(gc, offset); if (ret) @@ -4056,8 +4092,8 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) int dir = gpiod_get_direction(desc); if (dir < 0) { - chip_err(gc, "%s: cannot get GPIO direction\n", - __func__); + gpiochip_err(gc, "%s: cannot get GPIO direction\n", + __func__); return dir; } } @@ -4065,9 +4101,9 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) /* To be valid for IRQ the line needs to be input or open drain */ if (test_bit(GPIOD_FLAG_IS_OUT, &desc->flags) && !test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags)) { - chip_err(gc, - "%s: tried to flag a GPIO set as output for IRQ\n", - __func__); + gpiochip_err(gc, + "%s: tried to flag a GPIO set as output for IRQ\n", + __func__); return -EIO; } @@ -4144,7 +4180,7 @@ int gpiochip_reqres_irq(struct gpio_chip *gc, unsigned int offset) ret = gpiochip_lock_as_irq(gc, offset); if (ret) { - chip_err(gc, "unable to lock HW IRQ %u for IRQ\n", offset); + gpiochip_err(gc, "unable to lock HW IRQ %u for IRQ\n", offset); module_put(gc->gpiodev->owner); return ret; } @@ -4652,11 +4688,29 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, scoped_guard(srcu, &gpio_devices_srcu) { desc = gpiod_fwnode_lookup(fwnode, consumer, con_id, idx, &flags, &lookupflags); + if (!IS_ERR_OR_NULL(desc) && + test_bit(GPIOD_FLAG_SHARED, &desc->flags)) { + /* + * We're dealing with a GPIO shared by multiple + * consumers. This is the moment to add the machine + * lookup table for the proxy device as previously + * we only knew the consumer's fwnode. + */ + ret = gpio_shared_add_proxy_lookup(consumer, lookupflags); + if (ret) + return ERR_PTR(ret); + + /* Trigger platform lookup for shared GPIO proxy. */ + desc = ERR_PTR(-ENOENT); + /* Trigger it even for fwnode-only gpiod_get(). */ + platform_lookup_allowed = true; + } + if (gpiod_not_found(desc) && platform_lookup_allowed) { /* * Either we are not using DT or ACPI, or their lookup - * did not return a result. In that case, use platform - * lookup as a fallback. + * did not return a result or this is a shared GPIO. In + * that case, use platform lookup as a fallback. */ dev_dbg(consumer, "using lookup tables for GPIO lookup\n"); @@ -4679,14 +4733,19 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, return ERR_PTR(ret); /* - * This happens when there are several consumers for - * the same GPIO line: we just return here without - * further initialization. It is a bit of a hack. - * This is necessary to support fixed regulators. + * This happens when there are several consumers for the same + * GPIO line: we just return here without further + * initialization. It's a hack introduced long ago to support + * fixed regulators. We now have a better solution with + * automated scanning where affected platforms just need to + * select the provided Kconfig option. * - * FIXME: Make this more sane and safe. + * FIXME: Remove the GPIOD_FLAGS_BIT_NONEXCLUSIVE flag after + * making sure all platforms use the new mechanism. */ - dev_info(consumer, "nonexclusive access to GPIO for %s\n", name); + dev_info(consumer, + "nonexclusive access to GPIO for %s, consider updating your code to using gpio-shared-proxy\n", + name); return desc; } @@ -4963,7 +5022,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, if (test_and_set_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags)) return 0; - hwnum = gpio_chip_hwgpio(desc); + hwnum = gpiod_hwgpio(desc); local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name, lflags, dflags); @@ -5044,7 +5103,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, * If pin hardware number of array member 0 is also 0, select * its chip as a candidate for fast bitmap processing path. */ - if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) { + if (descs->ndescs == 0 && gpiod_hwgpio(desc) == 0) { struct gpio_descs *array; bitmap_size = BITS_TO_LONGS(gdev->ngpio > count ? @@ -5089,7 +5148,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, * Detect array members which belong to the 'fast' chip * but their pins are not in hardware order. */ - else if (gpio_chip_hwgpio(desc) != descs->ndescs) { + else if (gpiod_hwgpio(desc) != descs->ndescs) { /* * Don't use fast path if all array members processed so * far belong to the same chip as this one but its pin @@ -5296,6 +5355,8 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) struct gpio_device *gdev; loff_t index = *pos; + s->private = NULL; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return NULL; @@ -5329,7 +5390,11 @@ static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) static void gpiolib_seq_stop(struct seq_file *s, void *v) { - struct gpiolib_seq_priv *priv = s->private; + struct gpiolib_seq_priv *priv; + + priv = s->private; + if (!priv) + return; srcu_read_unlock(&gpio_devices_srcu, priv->idx); kfree(priv); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 2a003a7311e7..77f6f2936dc2 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -204,6 +204,8 @@ struct gpio_desc { #define GPIOD_FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */ #define GPIOD_FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */ #define GPIOD_FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ +#define GPIOD_FLAG_SHARED 20 /* GPIO is shared by multiple consumers */ +#define GPIOD_FLAG_SHARED_PROXY 21 /* GPIO is a virtual proxy to a physically shared pin. */ /* Connection label */ struct gpio_desc_label __rcu *label; @@ -273,49 +275,30 @@ int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev); struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum); const char *gpiod_get_label(struct gpio_desc *desc); -/* - * Return the GPIO number of the passed descriptor relative to its chip - */ -static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) -{ - return desc - &desc->gdev->descs[0]; -} - /* With descriptor prefix */ -#define gpiod_err(desc, fmt, ...) \ +#define __gpiod_pr(level, desc, fmt, ...) \ do { \ scoped_guard(srcu, &desc->gdev->desc_srcu) { \ - pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ - gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + pr_##level("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ?: "?", ##__VA_ARGS__); \ } \ } while (0) -#define gpiod_warn(desc, fmt, ...) \ -do { \ - scoped_guard(srcu, &desc->gdev->desc_srcu) { \ - pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ - gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ - } \ -} while (0) +#define gpiod_err(desc, fmt, ...) __gpiod_pr(err, desc, fmt, ##__VA_ARGS__) +#define gpiod_warn(desc, fmt, ...) __gpiod_pr(warn, desc, fmt, ##__VA_ARGS__) +#define gpiod_dbg(desc, fmt, ...) __gpiod_pr(debug, desc, fmt, ##__VA_ARGS__) + +/* With chip prefix */ -#define gpiod_dbg(desc, fmt, ...) \ +#define __gpiochip_pr(level, gc, fmt, ...) \ do { \ - scoped_guard(srcu, &desc->gdev->desc_srcu) { \ - pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ - gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ - } \ + dev_##level(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__); \ } while (0) -/* With chip prefix */ - -#define chip_err(gc, fmt, ...) \ - dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_warn(gc, fmt, ...) \ - dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_info(gc, fmt, ...) \ - dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_dbg(gc, fmt, ...) \ - dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) +#define gpiochip_err(gc, fmt, ...) __gpiochip_pr(err, gc, fmt, ##__VA_ARGS__) +#define gpiochip_warn(gc, fmt, ...) __gpiochip_pr(warn, gc, fmt, ##__VA_ARGS__) +#define gpiochip_info(gc, fmt, ...) __gpiochip_pr(info, gc, fmt, ##__VA_ARGS__) +#define gpiochip_dbg(gc, fmt, ...) __gpiochip_pr(dbg, gc, fmt, ##__VA_ARGS__) #endif /* GPIOLIB_H */ |
