summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig35
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/TODO11
-rw-r--r--drivers/gpio/gpio-104-idio-16.c1
-rw-r--r--drivers/gpio/gpio-aggregator.c1
-rw-r--r--drivers/gpio/gpio-aspeed.c7
-rw-r--r--drivers/gpio/gpio-brcmstb.c12
-rw-r--r--drivers/gpio/gpio-bt8xx.c30
-rw-r--r--drivers/gpio/gpio-dwapb.c18
-rw-r--r--drivers/gpio/gpio-elkhartlake.c36
-rw-r--r--drivers/gpio/gpio-fxl6408.c13
-rw-r--r--drivers/gpio/gpio-grgpio.c24
-rw-r--r--drivers/gpio/gpio-htc-egpio.c21
-rw-r--r--drivers/gpio/gpio-idio-16.c5
-rw-r--r--drivers/gpio/gpio-latch.c2
-rw-r--r--drivers/gpio/gpio-ljca.c14
-rw-r--r--drivers/gpio/gpio-loongson-64bit.c11
-rw-r--r--drivers/gpio/gpio-menz127.c26
-rw-r--r--drivers/gpio/gpio-ml-ioh.c12
-rw-r--r--drivers/gpio/gpio-mlxbf2.c8
-rw-r--r--drivers/gpio/gpio-mm-lantiq.c57
-rw-r--r--drivers/gpio/gpio-mmio.c323
-rw-r--r--drivers/gpio/gpio-mpsse.c229
-rw-r--r--drivers/gpio/gpio-msc313.c8
-rw-r--r--drivers/gpio/gpio-mvebu.c3
-rw-r--r--drivers/gpio/gpio-omap.c15
-rw-r--r--drivers/gpio/gpio-pca953x.c13
-rw-r--r--drivers/gpio/gpio-pch.c12
-rw-r--r--drivers/gpio/gpio-pci-idio-16.c1
-rw-r--r--drivers/gpio/gpio-pl061.c17
-rw-r--r--drivers/gpio/gpio-qixis-fpga.c111
-rw-r--r--drivers/gpio/gpio-regmap.c44
-rw-r--r--drivers/gpio/gpio-shared-proxy.c334
-rw-r--r--drivers/gpio/gpio-tb10x.c19
-rw-r--r--drivers/gpio/gpio-tegra186.c174
-rw-r--r--drivers/gpio/gpio-tqmx86.c9
-rw-r--r--drivers/gpio/gpio-uniphier.c9
-rw-r--r--drivers/gpio/gpio-virtuser.c8
-rw-r--r--drivers/gpio/gpio-xgene.c8
-rw-r--r--drivers/gpio/gpio-xilinx.c15
-rw-r--r--drivers/gpio/gpio-zynq.c15
-rw-r--r--drivers/gpio/gpiolib-acpi-core.c33
-rw-r--r--drivers/gpio/gpiolib-cdev.c93
-rw-r--r--drivers/gpio/gpiolib-legacy.c44
-rw-r--r--drivers/gpio/gpiolib-of.c79
-rw-r--r--drivers/gpio/gpiolib-shared.c656
-rw-r--r--drivers/gpio/gpiolib-shared.h71
-rw-r--r--drivers/gpio/gpiolib-swnode.c5
-rw-r--r--drivers/gpio/gpiolib-sysfs.c16
-rw-r--r--drivers/gpio/gpiolib.c225
-rw-r--r--drivers/gpio/gpiolib.h49
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, &regmap_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, &reg, &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 */