summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig40
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/gpio-adp5585.c229
-rw-r--r--drivers/gpio/gpio-amd8111.c4
-rw-r--r--drivers/gpio/gpio-ath79.c33
-rw-r--r--drivers/gpio/gpio-brcmstb.c5
-rw-r--r--drivers/gpio/gpio-cadence.c23
-rw-r--r--drivers/gpio/gpio-davinci.c107
-rw-r--r--drivers/gpio/gpio-fxl6408.c2
-rw-r--r--drivers/gpio/gpio-graniterapids.c2
-rw-r--r--drivers/gpio/gpio-ixp4xx.c10
-rw-r--r--drivers/gpio/gpio-lpc18xx.c23
-rw-r--r--drivers/gpio/gpio-max7300.c2
-rw-r--r--drivers/gpio/gpio-mb86s7x.c21
-rw-r--r--drivers/gpio/gpio-mc33880.c2
-rw-r--r--drivers/gpio/gpio-mlxbf3.c14
-rw-r--r--drivers/gpio/gpio-mmio.c2
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c44
-rw-r--r--drivers/gpio/gpio-msc313.c5
-rw-r--r--drivers/gpio/gpio-pca953x.c7
-rw-r--r--drivers/gpio/gpio-pch.c1
-rw-r--r--drivers/gpio/gpio-rdc321x.c6
-rw-r--r--drivers/gpio/gpio-rockchip.c1
-rw-r--r--drivers/gpio/gpio-sama5d2-piobu.c18
-rw-r--r--drivers/gpio/gpio-sim.c60
-rw-r--r--drivers/gpio/gpio-sloppy-logic-analyzer.c344
-rw-r--r--drivers/gpio/gpio-stmpe.c47
-rw-r--r--drivers/gpio/gpio-stp-xway.c10
-rw-r--r--drivers/gpio/gpio-syscon.c28
-rw-r--r--drivers/gpio/gpio-tegra.c5
-rw-r--r--drivers/gpio/gpio-tegra186.c3
-rw-r--r--drivers/gpio/gpio-thunderx.c5
-rw-r--r--drivers/gpio/gpio-uniphier.c5
-rw-r--r--drivers/gpio/gpio-vf610.c31
-rw-r--r--drivers/gpio/gpio-virtio.c10
-rw-r--r--drivers/gpio/gpio-virtuser.c1802
-rw-r--r--drivers/gpio/gpio-visconti.c5
-rw-r--r--drivers/gpio/gpio-xilinx.c11
-rw-r--r--drivers/gpio/gpio-zynq.c10
-rw-r--r--drivers/gpio/gpio-zynqmp-modepin.c1
-rw-r--r--drivers/gpio/gpiolib-acpi.c43
-rw-r--r--drivers/gpio/gpiolib-cdev.c116
-rw-r--r--drivers/gpio/gpiolib-devres.c120
-rw-r--r--drivers/gpio/gpiolib-legacy.c94
-rw-r--r--drivers/gpio/gpiolib-of.c114
-rw-r--r--drivers/gpio/gpiolib-swnode.c62
-rw-r--r--drivers/gpio/gpiolib-sysfs.c6
-rw-r--r--drivers/gpio/gpiolib.c198
-rw-r--r--drivers/gpio/gpiolib.h18
49 files changed, 3160 insertions, 592 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 1c28a48915bb..d93cd4f722b4 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -219,7 +219,7 @@ config GPIO_BCM_XGS_IPROC
config GPIO_BRCMSTB
tristate "BRCMSTB GPIO support"
default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
- depends on OF_GPIO && (ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST)
+ depends on OF_GPIO && (ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST)
select GPIO_GENERIC
select IRQ_DOMAIN
help
@@ -1233,6 +1233,13 @@ config GPIO_ADP5520
This option enables support for on-chip GPIO found
on Analog Devices ADP5520 PMICs.
+config GPIO_ADP5585
+ tristate "GPIO Support for ADP5585"
+ depends on MFD_ADP5585
+ help
+ This option enables support for the GPIO function found in the Analog
+ Devices ADP5585.
+
config GPIO_ALTERA_A10SR
tristate "Altera Arria10 System Resource GPIO"
depends on MFD_ALTERA_A10SR
@@ -1891,4 +1898,35 @@ config GPIO_SIM
endmenu
+menu "GPIO Debugging utilities"
+
+config GPIO_SLOPPY_LOGIC_ANALYZER
+ tristate "Sloppy GPIO logic analyzer"
+ depends on (GPIOLIB || COMPILE_TEST) && CPUSETS && DEBUG_FS && EXPERT
+ help
+ This option enables support for a sloppy logic analyzer using polled
+ GPIOs. Use the 'tools/gpio/gpio-sloppy-logic-analyzer' script with
+ this driver. The script will make it easier to use and will also
+ isolate a CPU for the polling task. Note that this is a last resort
+ analyzer which can be affected by latencies, non-deterministic code
+ paths, or NMIs. However, for e.g. remote development, it may be useful
+ to get a first view and aid further debugging.
+
+ If this driver is built as a module it will be called
+ 'gpio-sloppy-logic-analyzer'.
+
+config GPIO_VIRTUSER
+ tristate "GPIO Virtual User Testing Module"
+ select DEBUG_FS
+ select CONFIGFS_FS
+ select IRQ_WORK
+ help
+ Say Y here to enable the configurable, configfs-based virtual GPIO
+ consumer testing driver.
+
+ This driver is aimed as a helper in spotting any regressions in
+ hot-unplug handling in GPIOLIB.
+
+endmenu
+
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index e2a53013780e..1429e8c0229b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
+obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o
obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
@@ -150,6 +151,7 @@ obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
+obj-$(CONFIG_GPIO_SLOPPY_LOGIC_ANALYZER) += gpio-sloppy-logic-analyzer.o
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
@@ -181,6 +183,7 @@ obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
+obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o
obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o
obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c
new file mode 100644
index 000000000000..000d31f09671
--- /dev/null
+++ b/drivers/gpio/gpio-adp5585.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADP5585 GPIO driver
+ *
+ * Copyright 2022 NXP
+ * Copyright 2024 Ideas on Board Oy
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/mfd/adp5585.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define ADP5585_GPIO_MAX 11
+
+struct adp5585_gpio_dev {
+ struct gpio_chip gpio_chip;
+ struct regmap *regmap;
+};
+
+static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ unsigned int bank = ADP5585_BANK(off);
+ unsigned int bit = ADP5585_BIT(off);
+ unsigned int val;
+
+ regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
+
+ return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int adp5585_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ unsigned int bank = ADP5585_BANK(off);
+ unsigned int bit = ADP5585_BIT(off);
+
+ return regmap_clear_bits(adp5585_gpio->regmap,
+ ADP5585_GPIO_DIRECTION_A + bank, bit);
+}
+
+static int adp5585_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ unsigned int bank = ADP5585_BANK(off);
+ unsigned int bit = ADP5585_BIT(off);
+ int ret;
+
+ ret = regmap_update_bits(adp5585_gpio->regmap,
+ ADP5585_GPO_DATA_OUT_A + bank, bit,
+ val ? bit : 0);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(adp5585_gpio->regmap,
+ ADP5585_GPIO_DIRECTION_A + bank, bit);
+}
+
+static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ unsigned int bank = ADP5585_BANK(off);
+ unsigned int bit = ADP5585_BIT(off);
+ unsigned int reg;
+ unsigned int val;
+
+ /*
+ * The input status register doesn't reflect the pin state when the
+ * GPIO is configured as an output. Check the direction, and read the
+ * input status from GPI_STATUS or output value from GPO_DATA_OUT
+ * accordingly.
+ *
+ * We don't need any locking, as concurrent access to the same GPIO
+ * isn't allowed by the GPIO API, so there's no risk of the
+ * .direction_input(), .direction_output() or .set() operations racing
+ * with this.
+ */
+ regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
+ reg = val & bit ? ADP5585_GPO_DATA_OUT_A : ADP5585_GPI_STATUS_A;
+ regmap_read(adp5585_gpio->regmap, reg + bank, &val);
+
+ return !!(val & bit);
+}
+
+static void adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off, int val)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ unsigned int bank = ADP5585_BANK(off);
+ unsigned int bit = ADP5585_BIT(off);
+
+ regmap_update_bits(adp5585_gpio->regmap, ADP5585_GPO_DATA_OUT_A + bank,
+ bit, val ? bit : 0);
+}
+
+static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
+ unsigned int off, unsigned int bias)
+{
+ unsigned int bit, reg, mask, val;
+
+ /*
+ * The bias configuration fields are 2 bits wide and laid down in
+ * consecutive registers ADP5585_RPULL_CONFIG_*, with a hole of 4 bits
+ * after R5.
+ */
+ bit = off * 2 + (off > 5 ? 4 : 0);
+ reg = ADP5585_RPULL_CONFIG_A + bit / 8;
+ mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);
+ val = bias << (bit % 8);
+
+ return regmap_update_bits(adp5585_gpio->regmap, reg, mask, val);
+}
+
+static int adp5585_gpio_set_drive(struct adp5585_gpio_dev *adp5585_gpio,
+ unsigned int off, enum pin_config_param drive)
+{
+ unsigned int bank = ADP5585_BANK(off);
+ unsigned int bit = ADP5585_BIT(off);
+
+ return regmap_update_bits(adp5585_gpio->regmap,
+ ADP5585_GPO_OUT_MODE_A + bank, bit,
+ drive == PIN_CONFIG_DRIVE_OPEN_DRAIN ? bit : 0);
+}
+
+static int adp5585_gpio_set_debounce(struct adp5585_gpio_dev *adp5585_gpio,
+ unsigned int off, unsigned int debounce)
+{
+ unsigned int bank = ADP5585_BANK(off);
+ unsigned int bit = ADP5585_BIT(off);
+
+ return regmap_update_bits(adp5585_gpio->regmap,
+ ADP5585_DEBOUNCE_DIS_A + bank, bit,
+ debounce ? 0 : bit);
+}
+
+static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off,
+ unsigned long config)
+{
+ struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
+ enum pin_config_param param = pinconf_to_config_param(config);
+ u32 arg = pinconf_to_config_argument(config);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ return adp5585_gpio_set_bias(adp5585_gpio, off,
+ ADP5585_Rx_PULL_CFG_DISABLE);
+
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ return adp5585_gpio_set_bias(adp5585_gpio, off, arg ?
+ ADP5585_Rx_PULL_CFG_PD_300K :
+ ADP5585_Rx_PULL_CFG_DISABLE);
+
+ case PIN_CONFIG_BIAS_PULL_UP:
+ return adp5585_gpio_set_bias(adp5585_gpio, off, arg ?
+ ADP5585_Rx_PULL_CFG_PU_300K :
+ ADP5585_Rx_PULL_CFG_DISABLE);
+
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ return adp5585_gpio_set_drive(adp5585_gpio, off, param);
+
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ return adp5585_gpio_set_debounce(adp5585_gpio, off, arg);
+
+ default:
+ return -ENOTSUPP;
+ };
+}
+
+static int adp5585_gpio_probe(struct platform_device *pdev)
+{
+ struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
+ struct adp5585_gpio_dev *adp5585_gpio;
+ struct device *dev = &pdev->dev;
+ struct gpio_chip *gc;
+ int ret;
+
+ adp5585_gpio = devm_kzalloc(dev, sizeof(*adp5585_gpio), GFP_KERNEL);
+ if (!adp5585_gpio)
+ return -ENOMEM;
+
+ adp5585_gpio->regmap = adp5585->regmap;
+
+ device_set_of_node_from_dev(dev, dev->parent);
+
+ gc = &adp5585_gpio->gpio_chip;
+ gc->parent = dev;
+ gc->get_direction = adp5585_gpio_get_direction;
+ gc->direction_input = adp5585_gpio_direction_input;
+ gc->direction_output = adp5585_gpio_direction_output;
+ gc->get = adp5585_gpio_get_value;
+ gc->set = adp5585_gpio_set_value;
+ gc->set_config = adp5585_gpio_set_config;
+ gc->can_sleep = true;
+
+ gc->base = -1;
+ gc->ngpio = ADP5585_GPIO_MAX;
+ gc->label = pdev->name;
+ gc->owner = THIS_MODULE;
+
+ ret = devm_gpiochip_add_data(dev, &adp5585_gpio->gpio_chip,
+ adp5585_gpio);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add GPIO chip\n");
+
+ return 0;
+}
+
+static const struct platform_device_id adp5585_gpio_id_table[] = {
+ { "adp5585-gpio" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table);
+
+static struct platform_driver adp5585_gpio_driver = {
+ .driver = {
+ .name = "adp5585-gpio",
+ },
+ .probe = adp5585_gpio_probe,
+ .id_table = adp5585_gpio_id_table,
+};
+module_platform_driver(adp5585_gpio_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@nxp.com>");
+MODULE_DESCRIPTION("GPIO ADP5585 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c
index 6f3ded619c8b..3377667a28de 100644
--- a/drivers/gpio/gpio-amd8111.c
+++ b/drivers/gpio/gpio-amd8111.c
@@ -195,8 +195,10 @@ static int __init amd_gpio_init(void)
found:
err = pci_read_config_dword(pdev, 0x58, &gp.pmbase);
- if (err)
+ if (err) {
+ err = pcibios_err_to_errno(err);
goto out;
+ }
err = -EIO;
gp.pmbase &= 0x0000FF00;
if (gp.pmbase == 0)
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index f0c0c0f77eb0..de4cc12e5e03 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -8,13 +8,13 @@
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*/
+#include <linux/device.h>
#include <linux/gpio/driver.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/gpio-ath79.h>
-#include <linux/of.h>
#include <linux/interrupt.h>
-#include <linux/module.h>
#include <linux/irq.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#define AR71XX_GPIO_REG_OE 0x00
#define AR71XX_GPIO_REG_IN 0x04
@@ -224,9 +224,7 @@ MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
static int ath79_gpio_probe(struct platform_device *pdev)
{
- struct ath79_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
struct ath79_gpio_ctrl *ctrl;
struct gpio_irq_chip *girq;
u32 ath79_gpio_count;
@@ -237,21 +235,14 @@ static int ath79_gpio_probe(struct platform_device *pdev)
if (!ctrl)
return -ENOMEM;
- if (np) {
- err = of_property_read_u32(np, "ngpios", &ath79_gpio_count);
- if (err) {
- dev_err(dev, "ngpios property is not valid\n");
- return err;
- }
- oe_inverted = of_device_is_compatible(np, "qca,ar9340-gpio");
- } else if (pdata) {
- ath79_gpio_count = pdata->ngpios;
- oe_inverted = pdata->oe_inverted;
- } else {
- dev_err(dev, "No DT node or platform data found\n");
- return -EINVAL;
+ err = device_property_read_u32(dev, "ngpios", &ath79_gpio_count);
+ if (err) {
+ dev_err(dev, "ngpios property is not valid\n");
+ return err;
}
+ oe_inverted = device_is_compatible(dev, "qca,ar9340-gpio");
+
if (ath79_gpio_count >= 32) {
dev_err(dev, "ngpios must be less than 32\n");
return -EINVAL;
@@ -273,11 +264,9 @@ static int ath79_gpio_probe(struct platform_device *pdev)
dev_err(dev, "bgpio_init failed\n");
return err;
}
- /* Use base 0 to stay compatible with legacy platforms */
- ctrl->gc.base = 0;
/* Optional interrupt setup */
- if (!np || of_property_read_bool(np, "interrupt-controller")) {
+ if (device_property_read_bool(dev, "interrupt-controller")) {
girq = &ctrl->gc.irq;
gpio_irq_chip_set_chip(girq, &ath79_gpio_irqchip);
girq->parent_handler = ath79_gpio_irq_handler;
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index 8dce78ea7139..5762e517338e 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -591,8 +591,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
void __iomem *reg_base;
struct brcmstb_gpio_priv *priv;
struct resource *res;
- struct property *prop;
- const __be32 *p;
u32 bank_width;
int num_banks = 0;
int num_gpios = 0;
@@ -636,8 +634,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
#endif
- of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p,
- bank_width) {
+ of_property_for_each_u32(np, "brcm,gpio-bank-widths", bank_width) {
struct brcmstb_gpio_bank *bank;
struct gpio_chip *gc;
diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c
index 6a439cf78459..1b8ffd0ddab6 100644
--- a/drivers/gpio/gpio-cadence.c
+++ b/drivers/gpio/gpio-cadence.c
@@ -31,7 +31,6 @@
struct cdns_gpio_chip {
struct gpio_chip gc;
- struct clk *pclk;
void __iomem *regs;
u32 bypass_orig;
};
@@ -155,6 +154,7 @@ static int cdns_gpio_probe(struct platform_device *pdev)
int ret, irq;
u32 dir_prev;
u32 num_gpios = 32;
+ struct clk *clk;
cgpio = devm_kzalloc(&pdev->dev, sizeof(*cgpio), GFP_KERNEL);
if (!cgpio)
@@ -203,21 +203,14 @@ static int cdns_gpio_probe(struct platform_device *pdev)
cgpio->gc.request = cdns_gpio_request;
cgpio->gc.free = cdns_gpio_free;
- cgpio->pclk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(cgpio->pclk)) {
- ret = PTR_ERR(cgpio->pclk);
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
dev_err(&pdev->dev,
"Failed to retrieve peripheral clock, %d\n", ret);
goto err_revert_dir;
}
- ret = clk_prepare_enable(cgpio->pclk);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to enable the peripheral clock, %d\n", ret);
- goto err_revert_dir;
- }
-
/*
* Optional irq_chip support
*/
@@ -234,7 +227,7 @@ static int cdns_gpio_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!girq->parents) {
ret = -ENOMEM;
- goto err_disable_clk;
+ goto err_revert_dir;
}
girq->parents[0] = irq;
girq->default_type = IRQ_TYPE_NONE;
@@ -244,7 +237,7 @@ static int cdns_gpio_probe(struct platform_device *pdev)
ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- goto err_disable_clk;
+ goto err_revert_dir;
}
cgpio->bypass_orig = ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE);
@@ -259,9 +252,6 @@ static int cdns_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, cgpio);
return 0;
-err_disable_clk:
- clk_disable_unprepare(cgpio->pclk);
-
err_revert_dir:
iowrite32(dir_prev, cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
@@ -273,7 +263,6 @@ static void cdns_gpio_remove(struct platform_device *pdev)
struct cdns_gpio_chip *cgpio = platform_get_drvdata(pdev);
iowrite32(cgpio->bypass_orig, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
- clk_disable_unprepare(cgpio->pclk);
}
static const struct of_device_id cdns_of_ids[] = {
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index bb499e362912..b54fef6b1e12 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -18,7 +18,6 @@
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
-#include <linux/platform_data/gpio-davinci.h>
#include <linux/property.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/spinlock.h>
@@ -154,77 +153,45 @@ davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
value ? &g->set_data : &g->clr_data);
}
-static struct davinci_gpio_platform_data *
-davinci_gpio_get_pdata(struct platform_device *pdev)
-{
- struct device_node *dn = pdev->dev.of_node;
- struct davinci_gpio_platform_data *pdata;
- int ret;
- u32 val;
-
- if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
- return dev_get_platdata(&pdev->dev);
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return NULL;
-
- ret = of_property_read_u32(dn, "ti,ngpio", &val);
- if (ret)
- goto of_err;
-
- pdata->ngpio = val;
-
- ret = of_property_read_u32(dn, "ti,davinci-gpio-unbanked", &val);
- if (ret)
- goto of_err;
-
- pdata->gpio_unbanked = val;
-
- return pdata;
-
-of_err:
- dev_err(&pdev->dev, "Populating pdata from DT failed: err %d\n", ret);
- return NULL;
-}
-
static int davinci_gpio_probe(struct platform_device *pdev)
{
int bank, i, ret = 0;
- unsigned int ngpio, nbank, nirq;
+ unsigned int ngpio, nbank, nirq, gpio_unbanked;
struct davinci_gpio_controller *chips;
- struct davinci_gpio_platform_data *pdata;
struct device *dev = &pdev->dev;
-
- pdata = davinci_gpio_get_pdata(pdev);
- if (!pdata) {
- dev_err(dev, "No platform data found\n");
- return -EINVAL;
- }
-
- dev->platform_data = pdata;
+ struct device_node *dn = dev_of_node(dev);
/*
* The gpio banks conceptually expose a segmented bitmap,
* and "ngpio" is one more than the largest zero-based
* bit index that's valid.
*/
- ngpio = pdata->ngpio;
- if (ngpio == 0) {
- dev_err(dev, "How many GPIOs?\n");
- return -EINVAL;
- }
+ ret = of_property_read_u32(dn, "ti,ngpio", &ngpio);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get the number of GPIOs\n");
+ if (ngpio == 0)
+ return dev_err_probe(dev, -EINVAL, "How many GPIOs?\n");
/*
* If there are unbanked interrupts then the number of
* interrupts is equal to number of gpios else all are banked so
* number of interrupts is equal to number of banks(each with 16 gpios)
*/
- if (pdata->gpio_unbanked)
- nirq = pdata->gpio_unbanked;
+ ret = of_property_read_u32(dn, "ti,davinci-gpio-unbanked",
+ &gpio_unbanked);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get the unbanked GPIOs property\n");
+
+ if (gpio_unbanked)
+ nirq = gpio_unbanked;
else
nirq = DIV_ROUND_UP(ngpio, 16);
+ if (nirq > MAX_INT_PER_BANK) {
+ dev_err(dev, "Too many IRQs!\n");
+ return -EINVAL;
+ }
+
chips = devm_kzalloc(dev, sizeof(*chips), GFP_KERNEL);
if (!chips)
return -ENOMEM;
@@ -247,7 +214,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
chips->chip.set = davinci_gpio_set;
chips->chip.ngpio = ngpio;
- chips->chip.base = pdata->no_auto_base ? pdata->base : -1;
+ chips->chip.base = -1;
#ifdef CONFIG_OF_GPIO
chips->chip.parent = dev;
@@ -256,6 +223,8 @@ static int davinci_gpio_probe(struct platform_device *pdev)
#endif
spin_lock_init(&chips->lock);
+ chips->gpio_unbanked = gpio_unbanked;
+
nbank = DIV_ROUND_UP(ngpio, 32);
for (bank = 0; bank < nbank; bank++)
chips->regs[bank] = gpio_base + offset_array[bank];
@@ -477,13 +446,11 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
{
unsigned gpio, bank;
int irq;
- int ret;
struct clk *clk;
u32 binten = 0;
unsigned ngpio;
struct device *dev = &pdev->dev;
struct davinci_gpio_controller *chips = platform_get_drvdata(pdev);
- struct davinci_gpio_platform_data *pdata = dev->platform_data;
struct davinci_gpio_regs __iomem *g;
struct irq_domain *irq_domain = NULL;
struct irq_chip *irq_chip;
@@ -497,23 +464,18 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
if (dev->of_node)
gpio_get_irq_chip = (gpio_get_irq_chip_cb_t)device_get_match_data(dev);
- ngpio = pdata->ngpio;
+ ngpio = chips->chip.ngpio;
- clk = devm_clk_get(dev, "gpio");
+ clk = devm_clk_get_enabled(dev, "gpio");
if (IS_ERR(clk)) {
dev_err(dev, "Error %ld getting gpio clock\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
- ret = clk_prepare_enable(clk);
- if (ret)
- return ret;
-
- if (!pdata->gpio_unbanked) {
+ if (chips->gpio_unbanked) {
irq = devm_irq_alloc_descs(dev, -1, 0, ngpio, 0);
if (irq < 0) {
dev_err(dev, "Couldn't allocate IRQ numbers\n");
- clk_disable_unprepare(clk);
return irq;
}
@@ -522,7 +484,6 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
chips);
if (!irq_domain) {
dev_err(dev, "Couldn't register an IRQ domain\n");
- clk_disable_unprepare(clk);
return -ENODEV;
}
}
@@ -541,11 +502,11 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
* controller only handling trigger modes. We currently assume no
* IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs.
*/
- if (pdata->gpio_unbanked) {
+ if (chips->gpio_unbanked) {
/* pass "bank 0" GPIO IRQs to AINTC */
chips->chip.to_irq = gpio_to_irq_unbanked;
- chips->gpio_unbanked = pdata->gpio_unbanked;
- binten = GENMASK(pdata->gpio_unbanked / 16, 0);
+
+ binten = GENMASK(chips->gpio_unbanked / 16, 0);
/* AINTC handles mask/unmask; GPIO handles triggering */
irq = chips->irqs[0];
@@ -559,7 +520,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
writel_relaxed(~0, &g->set_rising);
/* set the direct IRQs up to use that irqchip */
- for (gpio = 0; gpio < pdata->gpio_unbanked; gpio++) {
+ for (gpio = 0; gpio < chips->gpio_unbanked; gpio++) {
irq_set_chip(chips->irqs[gpio], irq_chip);
irq_set_handler_data(chips->irqs[gpio], chips);
irq_set_status_flags(chips->irqs[gpio],
@@ -591,10 +552,8 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
sizeof(struct
davinci_gpio_irq_data),
GFP_KERNEL);
- if (!irqdata) {
- clk_disable_unprepare(clk);
+ if (!irqdata)
return -ENOMEM;
- }
irqdata->regs = g;
irqdata->bank_num = bank;
@@ -670,8 +629,7 @@ static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips,
static int davinci_gpio_suspend(struct device *dev)
{
struct davinci_gpio_controller *chips = dev_get_drvdata(dev);
- struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev);
- u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32);
+ u32 nbank = DIV_ROUND_UP(chips->chip.ngpio, 32);
davinci_gpio_save_context(chips, nbank);
@@ -681,8 +639,7 @@ static int davinci_gpio_suspend(struct device *dev)
static int davinci_gpio_resume(struct device *dev)
{
struct davinci_gpio_controller *chips = dev_get_drvdata(dev);
- struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev);
- u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32);
+ u32 nbank = DIV_ROUND_UP(chips->chip.ngpio, 32);
davinci_gpio_restore_context(chips, nbank);
diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c
index 991549888904..86ebc66b1104 100644
--- a/drivers/gpio/gpio-fxl6408.c
+++ b/drivers/gpio/gpio-fxl6408.c
@@ -138,7 +138,7 @@ static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = {
MODULE_DEVICE_TABLE(of, fxl6408_dt_ids);
static const struct i2c_device_id fxl6408_id[] = {
- { "fxl6408", 0 },
+ { "fxl6408" },
{ }
};
MODULE_DEVICE_TABLE(i2c, fxl6408_id);
diff --git a/drivers/gpio/gpio-graniterapids.c b/drivers/gpio/gpio-graniterapids.c
index c693fe05d50f..f2e911a3d2ca 100644
--- a/drivers/gpio/gpio-graniterapids.c
+++ b/drivers/gpio/gpio-graniterapids.c
@@ -296,6 +296,8 @@ static int gnr_gpio_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ raw_spin_lock_init(&priv->lock);
+
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c
index c5a9fa640566..28a8a6a8f05f 100644
--- a/drivers/gpio/gpio-ixp4xx.c
+++ b/drivers/gpio/gpio-ixp4xx.c
@@ -6,6 +6,7 @@
// based on previous work and know-how from:
// Deepak Saxena <dsaxena@plexity.net>
+#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/irq.h>
@@ -13,7 +14,7 @@
#include <linux/irqchip.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
-#include <linux/bitops.h>
+#include <linux/property.h>
#define IXP4XX_REG_GPOUT 0x00
#define IXP4XX_REG_GPOE 0x04
@@ -53,16 +54,14 @@
/**
* struct ixp4xx_gpio - IXP4 GPIO state container
* @dev: containing device for this instance
- * @fwnode: the fwnode for this GPIO chip
* @gc: gpiochip for this instance
* @base: remapped I/O-memory base
* @irq_edge: Each bit represents an IRQ: 1: edge-triggered,
* 0: level triggered
*/
struct ixp4xx_gpio {
- struct device *dev;
- struct fwnode_handle *fwnode;
struct gpio_chip gc;
+ struct device *dev;
void __iomem *base;
unsigned long long irq_edge;
};
@@ -237,7 +236,6 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
dev_err(dev, "no IRQ parent domain\n");
return -ENODEV;
}
- g->fwnode = of_node_to_fwnode(np);
/*
* If either clock output is enabled explicitly in the device tree
@@ -322,7 +320,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &ixp4xx_gpio_irqchip);
- girq->fwnode = g->fwnode;
+ girq->fwnode = dev_fwnode(dev);
girq->parent_domain = parent;
girq->child_to_parent_hwirq = ixp4xx_gpio_child_to_parent_hwirq;
girq->handler = handle_bad_irq;
diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c
index 5c6bb57a8c99..e7c0ef6e54fa 100644
--- a/drivers/gpio/gpio-lpc18xx.c
+++ b/drivers/gpio/gpio-lpc18xx.c
@@ -47,7 +47,6 @@ struct lpc18xx_gpio_pin_ic {
struct lpc18xx_gpio_chip {
struct gpio_chip gpio;
void __iomem *base;
- struct clk *clk;
struct lpc18xx_gpio_pin_ic *pin_ic;
spinlock_t lock;
};
@@ -328,6 +327,7 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct lpc18xx_gpio_chip *gc;
int index, ret;
+ struct clk *clk;
gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
@@ -352,16 +352,10 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gc->base))
return PTR_ERR(gc->base);
- gc->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(gc->clk)) {
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk)) {
dev_err(dev, "input clock not found\n");
- return PTR_ERR(gc->clk);
- }
-
- ret = clk_prepare_enable(gc->clk);
- if (ret) {
- dev_err(dev, "unable to enable clock\n");
- return ret;
+ return PTR_ERR(clk);
}
spin_lock_init(&gc->lock);
@@ -369,11 +363,8 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
gc->gpio.parent = dev;
ret = devm_gpiochip_add_data(dev, &gc->gpio, gc);
- if (ret) {
- dev_err(dev, "failed to add gpio chip\n");
- clk_disable_unprepare(gc->clk);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add gpio chip\n");
/* On error GPIO pin interrupt controller just won't be registered */
lpc18xx_gpio_pin_ic_probe(gc);
@@ -387,8 +378,6 @@ static void lpc18xx_gpio_remove(struct platform_device *pdev)
if (gc->pin_ic)
irq_domain_remove(gc->pin_ic->domain);
-
- clk_disable_unprepare(gc->clk);
}
static const struct of_device_id lpc18xx_gpio_match[] = {
diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c
index 31c2b95321cc..621d609ece90 100644
--- a/drivers/gpio/gpio-max7300.c
+++ b/drivers/gpio/gpio-max7300.c
@@ -53,7 +53,7 @@ static void max7300_remove(struct i2c_client *client)
}
static const struct i2c_device_id max7300_id[] = {
- { "max7300", 0 },
+ { "max7300" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max7300_id);
diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c
index 7fb298b4571b..ccbb63c21d6f 100644
--- a/drivers/gpio/gpio-mb86s7x.c
+++ b/drivers/gpio/gpio-mb86s7x.c
@@ -35,7 +35,6 @@
struct mb86s70_gpio_chip {
struct gpio_chip gc;
void __iomem *base;
- struct clk *clk;
spinlock_t lock;
};
@@ -157,6 +156,7 @@ static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
static int mb86s70_gpio_probe(struct platform_device *pdev)
{
struct mb86s70_gpio_chip *gchip;
+ struct clk *clk;
int ret;
gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
@@ -169,13 +169,9 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gchip->base))
return PTR_ERR(gchip->base);
- gchip->clk = devm_clk_get_optional(&pdev->dev, NULL);
- if (IS_ERR(gchip->clk))
- return PTR_ERR(gchip->clk);
-
- ret = clk_prepare_enable(gchip->clk);
- if (ret)
- return ret;
+ clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
spin_lock_init(&gchip->lock);
@@ -193,11 +189,9 @@ static int mb86s70_gpio_probe(struct platform_device *pdev)
gchip->gc.base = -1;
ret = gpiochip_add_data(&gchip->gc, gchip);
- if (ret) {
- dev_err(&pdev->dev, "couldn't register gpio driver\n");
- clk_disable_unprepare(gchip->clk);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "couldn't register gpio driver\n");
acpi_gpiochip_request_interrupts(&gchip->gc);
@@ -210,7 +204,6 @@ static void mb86s70_gpio_remove(struct platform_device *pdev)
acpi_gpiochip_free_interrupts(&gchip->gc);
gpiochip_remove(&gchip->gc);
- clk_disable_unprepare(gchip->clk);
}
static const struct of_device_id mb86s70_gpio_dt_ids[] = {
diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c
index 94f6fefc011b..5fb357d7b78a 100644
--- a/drivers/gpio/gpio-mc33880.c
+++ b/drivers/gpio/gpio-mc33880.c
@@ -99,7 +99,7 @@ static int mc33880_probe(struct spi_device *spi)
mc->spi = spi;
- mc->chip.label = DRIVER_NAME,
+ mc->chip.label = DRIVER_NAME;
mc->chip.set = mc33880_set;
mc->chip.base = pdata->base;
mc->chip.ngpio = PIN_NUMBER;
diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c
index d5906d419b0a..10ea71273c89 100644
--- a/drivers/gpio/gpio-mlxbf3.c
+++ b/drivers/gpio/gpio-mlxbf3.c
@@ -39,6 +39,8 @@
#define MLXBF_GPIO_CAUSE_OR_EVTEN0 0x14
#define MLXBF_GPIO_CAUSE_OR_CLRCAUSE 0x18
+#define MLXBF_GPIO_CLR_ALL_INTS GENMASK(31, 0)
+
struct mlxbf3_gpio_context {
struct gpio_chip gc;
@@ -82,6 +84,8 @@ static void mlxbf3_gpio_irq_disable(struct irq_data *irqd)
val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
val &= ~BIT(offset);
writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
+
+ writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE);
raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
gpiochip_disable_irq(gc, offset);
@@ -253,6 +257,15 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
return 0;
}
+static void mlxbf3_gpio_shutdown(struct platform_device *pdev)
+{
+ struct mlxbf3_gpio_context *gs = platform_get_drvdata(pdev);
+
+ /* Disable and clear all interrupts */
+ writel(0, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0);
+ writel(MLXBF_GPIO_CLR_ALL_INTS, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE);
+}
+
static const struct acpi_device_id mlxbf3_gpio_acpi_match[] = {
{ "MLNXBF33", 0 },
{}
@@ -265,6 +278,7 @@ static struct platform_driver mlxbf3_gpio_driver = {
.acpi_match_table = mlxbf3_gpio_acpi_match,
},
.probe = mlxbf3_gpio_probe,
+ .shutdown = mlxbf3_gpio_shutdown,
};
module_platform_driver(mlxbf3_gpio_driver);
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index 71e1af7c2184..d89e78f0ead3 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -619,8 +619,6 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
ret = gpiochip_get_ngpios(gc, dev);
if (ret)
gc->ngpio = gc->bgpio_bits;
- else
- gc->bgpio_bits = roundup_pow_of_two(round_up(gc->ngpio, 8));
ret = bgpio_setup_io(gc, dat, set, clr, flags);
if (ret)
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index c0125ac73906..685ec31db409 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -7,19 +7,21 @@
*/
#include <linux/acpi.h>
-#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/property.h>
-#include <linux/mod_devicetable.h>
#include <linux/slab.h>
-#include <linux/irq.h>
-#include <linux/gpio/driver.h>
-#include <linux/bitops.h>
-#include <linux/interrupt.h>
+#include <linux/spinlock.h>
#define MPC8XXX_GPIO_PINS 32
@@ -413,6 +415,8 @@ static int mpc8xxx_probe(struct platform_device *pdev)
goto err;
}
+ device_init_wakeup(&pdev->dev, true);
+
return 0;
err:
irq_domain_remove(mpc8xxx_gc->irq);
@@ -429,6 +433,29 @@ static void mpc8xxx_remove(struct platform_device *pdev)
}
}
+static int mpc8xxx_suspend(struct device *dev)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = dev_get_drvdata(dev);
+
+ if (mpc8xxx_gc->irqn && device_may_wakeup(dev))
+ enable_irq_wake(mpc8xxx_gc->irqn);
+
+ return 0;
+}
+
+static int mpc8xxx_resume(struct device *dev)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = dev_get_drvdata(dev);
+
+ if (mpc8xxx_gc->irqn && device_may_wakeup(dev))
+ disable_irq_wake(mpc8xxx_gc->irqn);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(mpc8xx_pm_ops,
+ mpc8xxx_suspend, mpc8xxx_resume, NULL);
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id gpio_acpi_ids[] = {
{"NXP0031",},
@@ -444,6 +471,7 @@ static struct platform_driver mpc8xxx_plat_driver = {
.name = "gpio-mpc8xxx",
.of_match_table = mpc8xxx_gpio_ids,
.acpi_match_table = ACPI_PTR(gpio_acpi_ids),
+ .pm = pm_ptr(&mpc8xx_pm_ops),
},
};
diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c
index 2f448eb23abb..6db9e469e0dc 100644
--- a/drivers/gpio/gpio-msc313.c
+++ b/drivers/gpio/gpio-msc313.c
@@ -3,13 +3,14 @@
#include <linux/bitops.h>
#include <linux/kernel.h>
-#include <linux/types.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/types.h>
#include <dt-bindings/gpio/msc313-gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -662,7 +663,7 @@ static int msc313_gpio_probe(struct platform_device *pdev)
gpioirqchip = &gpiochip->irq;
gpio_irq_chip_set_chip(gpioirqchip, &msc313_gpio_irqchip);
- gpioirqchip->fwnode = of_node_to_fwnode(dev->of_node);
+ gpioirqchip->fwnode = dev_fwnode(dev);
gpioirqchip->parent_domain = parent_domain;
gpioirqchip->child_to_parent_hwirq = msc313e_gpio_child_to_parent_hwirq;
gpioirqchip->populate_parent_alloc_arg = msc313_gpio_populate_parent_fwspec;
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 77a2812f2974..3f2d33ee20cc 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -498,7 +498,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long
ret = regmap_bulk_write(chip->regmap, regaddr, value, NBANK(chip));
if (ret < 0) {
- dev_err(&chip->client->dev, "failed writing register\n");
+ dev_err(&chip->client->dev, "failed writing register: %d\n", ret);
return ret;
}
@@ -513,7 +513,7 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *
ret = regmap_bulk_read(chip->regmap, regaddr, value, NBANK(chip));
if (ret < 0) {
- dev_err(&chip->client->dev, "failed reading register\n");
+ dev_err(&chip->client->dev, "failed reading register: %d\n", ret);
return ret;
}
@@ -758,6 +758,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
int level;
if (chip->driver_data & PCA_PCAL) {
+ guard(mutex)(&chip->i2c_lock);
+
/* Enable latch on interrupt-enabled inputs */
pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask);
@@ -1313,6 +1315,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
+ { .compatible = "ti,tca9535", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), },
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index ee37ecb615cb..63f25c72eac2 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -84,7 +84,6 @@ struct pch_gpio_reg_data {
* @gpio: Data for GPIO infrastructure.
* @pch_gpio_reg: Memory mapped Register data is saved here
* when suspend.
- * @lock: Used for register access protection
* @irq_base: Save base of IRQ number for interrupt
* @ioh: IOH ID
* @spinlock: Used for register access protection
diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c
index 01ed2517e9fd..ec7fb9220a47 100644
--- a/drivers/gpio/gpio-rdc321x.c
+++ b/drivers/gpio/gpio-rdc321x.c
@@ -102,7 +102,7 @@ static int rdc_gpio_config(struct gpio_chip *chip,
unlock:
spin_unlock(&gpch->lock);
- return err;
+ return pcibios_err_to_errno(err);
}
/* configure GPIO pin as input */
@@ -170,13 +170,13 @@ static int rdc321x_gpio_probe(struct platform_device *pdev)
rdc321x_gpio_dev->reg1_data_base,
&rdc321x_gpio_dev->data_reg[0]);
if (err)
- return err;
+ return pcibios_err_to_errno(err);
err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
rdc321x_gpio_dev->reg2_data_base,
&rdc321x_gpio_dev->data_reg[1]);
if (err)
- return err;
+ return pcibios_err_to_errno(err);
dev_info(&pdev->dev, "registering %d GPIOs\n",
rdc321x_gpio_dev->chip.ngpio);
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 0bd339813110..365ab947983c 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -713,6 +713,7 @@ static int rockchip_gpio_probe(struct platform_device *pdev)
return -ENODEV;
pctldev = of_pinctrl_get(pctlnp);
+ of_node_put(pctlnp);
if (!pctldev)
return -EPROBE_DEFER;
diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c
index d89da7300ddd..d770a6f3d846 100644
--- a/drivers/gpio/gpio-sama5d2-piobu.c
+++ b/drivers/gpio/gpio-sama5d2-piobu.c
@@ -191,15 +191,15 @@ static int sama5d2_piobu_probe(struct platform_device *pdev)
piobu->chip.label = pdev->name;
piobu->chip.parent = &pdev->dev;
- piobu->chip.owner = THIS_MODULE,
- piobu->chip.get_direction = sama5d2_piobu_get_direction,
- piobu->chip.direction_input = sama5d2_piobu_direction_input,
- piobu->chip.direction_output = sama5d2_piobu_direction_output,
- piobu->chip.get = sama5d2_piobu_get,
- piobu->chip.set = sama5d2_piobu_set,
- piobu->chip.base = -1,
- piobu->chip.ngpio = PIOBU_NUM,
- piobu->chip.can_sleep = 0,
+ piobu->chip.owner = THIS_MODULE;
+ piobu->chip.get_direction = sama5d2_piobu_get_direction;
+ piobu->chip.direction_input = sama5d2_piobu_direction_input;
+ piobu->chip.direction_output = sama5d2_piobu_direction_output;
+ piobu->chip.get = sama5d2_piobu_get;
+ piobu->chip.set = sama5d2_piobu_set;
+ piobu->chip.base = -1;
+ piobu->chip.ngpio = PIOBU_NUM;
+ piobu->chip.can_sleep = 0;
piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node);
if (IS_ERR(piobu->regmap)) {
diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c
index 2ed5cbe7c8a8..dcca1d7f173e 100644
--- a/drivers/gpio/gpio-sim.c
+++ b/drivers/gpio/gpio-sim.c
@@ -7,6 +7,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/array_size.h>
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/completion.h>
@@ -20,7 +21,6 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irq_sim.h>
-#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/minmax.h>
@@ -227,6 +227,27 @@ static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset)
}
}
+static int gpio_sim_irq_requested(struct irq_domain *domain,
+ irq_hw_number_t hwirq, void *data)
+{
+ struct gpio_sim_chip *chip = data;
+
+ return gpiochip_lock_as_irq(&chip->gc, hwirq);
+}
+
+static void gpio_sim_irq_released(struct irq_domain *domain,
+ irq_hw_number_t hwirq, void *data)
+{
+ struct gpio_sim_chip *chip = data;
+
+ gpiochip_unlock_as_irq(&chip->gc, hwirq);
+}
+
+static const struct irq_sim_ops gpio_sim_irq_sim_ops = {
+ .irq_sim_irq_requested = gpio_sim_irq_requested,
+ .irq_sim_irq_released = gpio_sim_irq_released,
+};
+
static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc)
{
struct gpio_sim_chip *chip = gpiochip_get_data(gc);
@@ -308,13 +329,6 @@ static ssize_t gpio_sim_sysfs_pull_store(struct device *dev,
return len;
}
-static void gpio_sim_mutex_destroy(void *data)
-{
- struct mutex *lock = data;
-
- mutex_destroy(lock);
-}
-
static void gpio_sim_put_device(void *data)
{
struct device *dev = data;
@@ -450,7 +464,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
if (!chip->pull_map)
return -ENOMEM;
- chip->irq_sim = devm_irq_domain_create_sim(dev, swnode, num_lines);
+ chip->irq_sim = devm_irq_domain_create_sim_full(dev, swnode, num_lines,
+ &gpio_sim_irq_sim_ops,
+ chip);
if (IS_ERR(chip->irq_sim))
return PTR_ERR(chip->irq_sim);
@@ -458,9 +474,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
if (ret)
return ret;
- mutex_init(&chip->lock);
- ret = devm_add_action_or_reset(dev, gpio_sim_mutex_destroy,
- &chip->lock);
+ ret = devm_mutex_init(dev, &chip->lock);
if (ret)
return ret;
@@ -581,19 +595,19 @@ static int gpio_sim_bus_notifier_call(struct notifier_block *nb,
snprintf(devname, sizeof(devname), "gpio-sim.%u", simdev->id);
- if (strcmp(dev_name(dev), devname) == 0) {
- if (action == BUS_NOTIFY_BOUND_DRIVER)
- simdev->driver_bound = true;
- else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND)
- simdev->driver_bound = false;
- else
- return NOTIFY_DONE;
+ if (!device_match_name(dev, devname))
+ return NOTIFY_DONE;
- complete(&simdev->probe_completion);
- return NOTIFY_OK;
- }
+ if (action == BUS_NOTIFY_BOUND_DRIVER)
+ simdev->driver_bound = true;
+ else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND)
+ simdev->driver_bound = false;
+ else
+ return NOTIFY_DONE;
+
+ complete(&simdev->probe_completion);
- return NOTIFY_DONE;
+ return NOTIFY_OK;
}
static struct gpio_sim_device *to_gpio_sim_device(struct config_item *item)
diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c
new file mode 100644
index 000000000000..aed6d1f6cfc3
--- /dev/null
+++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Sloppy logic analyzer using GPIOs (to be run on an isolated CPU)
+ *
+ * Use the 'gpio-sloppy-logic-analyzer' script in the 'tools/gpio' folder for
+ * easier usage and further documentation. Note that this is a last resort
+ * analyzer which can be affected by latencies and non-deterministic code
+ * paths. However, for e.g. remote development, it may be useful to get a first
+ * view and aid further debugging.
+ *
+ * Copyright (C) Wolfram Sang <wsa@sang-engineering.com>
+ * Copyright (C) Renesas Electronics Corporation
+ */
+
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/ktime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/sizes.h>
+#include <linux/timekeeping.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#define GPIO_LA_NAME "gpio-sloppy-logic-analyzer"
+#define GPIO_LA_DEFAULT_BUF_SIZE SZ_256K
+/* can be increased but then we need to extend the u8 buffers */
+#define GPIO_LA_MAX_PROBES 8
+#define GPIO_LA_NUM_TESTS 1024
+
+struct gpio_la_poll_priv {
+ struct mutex blob_lock; /* serialize access to the blob (data) */
+ u32 buf_idx;
+ struct gpio_descs *descs;
+ unsigned long delay_ns;
+ unsigned long acq_delay;
+ struct debugfs_blob_wrapper blob;
+ struct dentry *debug_dir;
+ struct dentry *blob_dent;
+ struct debugfs_blob_wrapper meta;
+ struct device *dev;
+ unsigned int trig_len;
+ u8 *trig_data;
+};
+
+static struct dentry *gpio_la_poll_debug_dir;
+
+static __always_inline int gpio_la_get_array(struct gpio_descs *d, unsigned long *sptr)
+{
+ int ret;
+
+ ret = gpiod_get_array_value(d->ndescs, d->desc, d->info, sptr);
+ if (ret == 0 && fatal_signal_pending(current))
+ ret = -EINTR;
+
+ return ret;
+}
+
+static int fops_capture_set(void *data, u64 val)
+{
+ struct gpio_la_poll_priv *priv = data;
+ u8 *la_buf = priv->blob.data;
+ unsigned long state = 0; /* zeroed because GPIO arrays are bitfields */
+ unsigned long delay;
+ ktime_t start_time;
+ unsigned int i;
+ int ret;
+
+ if (!val)
+ return 0;
+
+ if (!la_buf)
+ return -ENOMEM;
+
+ if (!priv->delay_ns)
+ return -EINVAL;
+
+ mutex_lock(&priv->blob_lock);
+ if (priv->blob_dent) {
+ debugfs_remove(priv->blob_dent);
+ priv->blob_dent = NULL;
+ }
+
+ priv->buf_idx = 0;
+
+ local_irq_disable();
+ preempt_disable_notrace();
+
+ /* Measure delay of reading GPIOs */
+ start_time = ktime_get();
+ for (i = 0; i < GPIO_LA_NUM_TESTS; i++) {
+ ret = gpio_la_get_array(priv->descs, &state);
+ if (ret)
+ goto out;
+ }
+
+ priv->acq_delay = ktime_sub(ktime_get(), start_time) / GPIO_LA_NUM_TESTS;
+ if (priv->delay_ns < priv->acq_delay) {
+ ret = -ERANGE;
+ goto out;
+ }
+
+ delay = priv->delay_ns - priv->acq_delay;
+
+ /* Wait for triggers */
+ for (i = 0; i < priv->trig_len; i += 2) {
+ do {
+ ret = gpio_la_get_array(priv->descs, &state);
+ if (ret)
+ goto out;
+
+ ndelay(delay);
+ } while ((state & priv->trig_data[i]) != priv->trig_data[i + 1]);
+ }
+
+ /* With triggers, final state is also the first sample */
+ if (priv->trig_len)
+ la_buf[priv->buf_idx++] = state;
+
+ /* Sample */
+ while (priv->buf_idx < priv->blob.size) {
+ ret = gpio_la_get_array(priv->descs, &state);
+ if (ret)
+ goto out;
+
+ la_buf[priv->buf_idx++] = state;
+ ndelay(delay);
+ }
+out:
+ preempt_enable_notrace();
+ local_irq_enable();
+ if (ret)
+ dev_err(priv->dev, "couldn't read GPIOs: %d\n", ret);
+
+ kfree(priv->trig_data);
+ priv->trig_data = NULL;
+ priv->trig_len = 0;
+
+ priv->blob_dent = debugfs_create_blob("sample_data", 0400, priv->debug_dir, &priv->blob);
+ mutex_unlock(&priv->blob_lock);
+
+ return ret;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_capture, NULL, fops_capture_set, "%llu\n");
+
+static int fops_buf_size_get(void *data, u64 *val)
+{
+ struct gpio_la_poll_priv *priv = data;
+
+ *val = priv->blob.size;
+
+ return 0;
+}
+
+static int fops_buf_size_set(void *data, u64 val)
+{
+ struct gpio_la_poll_priv *priv = data;
+ int ret = 0;
+ void *p;
+
+ if (!val)
+ return -EINVAL;
+
+ mutex_lock(&priv->blob_lock);
+
+ vfree(priv->blob.data);
+ p = vzalloc(val);
+ if (!p) {
+ val = 0;
+ ret = -ENOMEM;
+ }
+
+ priv->blob.data = p;
+ priv->blob.size = val;
+
+ mutex_unlock(&priv->blob_lock);
+ return ret;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_buf_size, fops_buf_size_get, fops_buf_size_set, "%llu\n");
+
+static int trigger_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, inode->i_private);
+}
+
+static ssize_t trigger_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ struct seq_file *m = file->private_data;
+ struct gpio_la_poll_priv *priv = m->private;
+ char *buf;
+
+ /* upper limit is arbitrary but should be less than PAGE_SIZE */
+ if (count > 2048 || count & 1)
+ return -EINVAL;
+
+ buf = memdup_user(ubuf, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ priv->trig_data = buf;
+ priv->trig_len = count;
+
+ return count;
+}
+
+static const struct file_operations fops_trigger = {
+ .owner = THIS_MODULE,
+ .open = trigger_open,
+ .write = trigger_write,
+ .llseek = no_llseek,
+ .release = single_release,
+};
+
+static int gpio_la_poll_probe(struct platform_device *pdev)
+{
+ struct gpio_la_poll_priv *priv;
+ struct device *dev = &pdev->dev;
+ const char *devname = dev_name(dev);
+ const char *gpio_names[GPIO_LA_MAX_PROBES];
+ char *meta = NULL;
+ unsigned int i, meta_len = 0;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ devm_mutex_init(dev, &priv->blob_lock);
+
+ fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE);
+
+ priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN);
+ if (IS_ERR(priv->descs))
+ return PTR_ERR(priv->descs);
+
+ /* artificial limit to keep 1 byte per sample for now */
+ if (priv->descs->ndescs > GPIO_LA_MAX_PROBES)
+ return -EFBIG;
+
+ ret = device_property_read_string_array(dev, "probe-names", gpio_names,
+ priv->descs->ndescs);
+ if (ret >= 0 && ret != priv->descs->ndescs)
+ ret = -EBADR;
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "error naming the GPIOs");
+
+ for (i = 0; i < priv->descs->ndescs; i++) {
+ unsigned int add_len;
+ char *new_meta, *consumer_name;
+
+ if (gpiod_cansleep(priv->descs->desc[i]))
+ return -EREMOTE;
+
+ consumer_name = kasprintf(GFP_KERNEL, "%s: %s", devname, gpio_names[i]);
+ if (!consumer_name)
+ return -ENOMEM;
+ gpiod_set_consumer_name(priv->descs->desc[i], consumer_name);
+ kfree(consumer_name);
+
+ /* '10' is length of 'probe00=\n\0' */
+ add_len = strlen(gpio_names[i]) + 10;
+
+ new_meta = devm_krealloc(dev, meta, meta_len + add_len, GFP_KERNEL);
+ if (!new_meta)
+ return -ENOMEM;
+
+ meta = new_meta;
+ meta_len += snprintf(meta + meta_len, add_len, "probe%02u=%s\n",
+ i + 1, gpio_names[i]);
+ }
+
+ platform_set_drvdata(pdev, priv);
+ priv->dev = dev;
+
+ priv->meta.data = meta;
+ priv->meta.size = meta_len;
+ priv->debug_dir = debugfs_create_dir(devname, gpio_la_poll_debug_dir);
+ debugfs_create_blob("meta_data", 0400, priv->debug_dir, &priv->meta);
+ debugfs_create_ulong("delay_ns", 0600, priv->debug_dir, &priv->delay_ns);
+ debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay);
+ debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size);
+ debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture);
+ debugfs_create_file_unsafe("trigger", 0200, priv->debug_dir, priv, &fops_trigger);
+
+ return 0;
+}
+
+static void gpio_la_poll_remove(struct platform_device *pdev)
+{
+ struct gpio_la_poll_priv *priv = platform_get_drvdata(pdev);
+
+ mutex_lock(&priv->blob_lock);
+ debugfs_remove_recursive(priv->debug_dir);
+ mutex_unlock(&priv->blob_lock);
+}
+
+static const struct of_device_id gpio_la_poll_of_match[] = {
+ { .compatible = GPIO_LA_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpio_la_poll_of_match);
+
+static struct platform_driver gpio_la_poll_device_driver = {
+ .probe = gpio_la_poll_probe,
+ .remove_new = gpio_la_poll_remove,
+ .driver = {
+ .name = GPIO_LA_NAME,
+ .of_match_table = gpio_la_poll_of_match,
+ }
+};
+
+static int __init gpio_la_poll_init(void)
+{
+ gpio_la_poll_debug_dir = debugfs_create_dir(GPIO_LA_NAME, NULL);
+
+ return platform_driver_register(&gpio_la_poll_device_driver);
+}
+/*
+ * Non-strict pin controllers can read GPIOs while being muxed to something else.
+ * To support that, we need to claim GPIOs before further pinmuxing happens. So,
+ * we probe early using 'late_initcall'
+ */
+late_initcall(gpio_la_poll_init);
+
+static void __exit gpio_la_poll_exit(void)
+{
+ platform_driver_unregister(&gpio_la_poll_device_driver);
+ debugfs_remove_recursive(gpio_la_poll_debug_dir);
+}
+module_exit(gpio_la_poll_exit);
+
+MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
+MODULE_DESCRIPTION("Sloppy logic analyzer using GPIOs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index 6c5ee81d71b3..75a3633ceddb 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -5,16 +5,16 @@
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
*/
+#include <linux/bitops.h>
#include <linux/cleanup.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/gpio/driver.h>
+#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/of.h>
#include <linux/mfd/stmpe.h>
+#include <linux/property.h>
+#include <linux/platform_device.h>
#include <linux/seq_file.h>
-#include <linux/bitops.h>
+#include <linux/slab.h>
/*
* These registers are modified under the irq bus lock and cached to avoid
@@ -31,7 +31,6 @@ enum { LSB, CSB, MSB };
struct stmpe_gpio {
struct gpio_chip chip;
struct stmpe *stmpe;
- struct device *dev;
struct mutex irq_lock;
u32 norequest_mask;
/* Caches of interrupt control registers for bus_lock */
@@ -464,59 +463,49 @@ static void stmpe_gpio_disable(void *stmpe)
static int stmpe_gpio_probe(struct platform_device *pdev)
{
- struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
- struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct stmpe *stmpe = dev_get_drvdata(dev->parent);
struct stmpe_gpio *stmpe_gpio;
int ret, irq;
if (stmpe->num_gpios > MAX_GPIOS) {
- dev_err(&pdev->dev, "Need to increase maximum GPIO number\n");
+ dev_err(dev, "Need to increase maximum GPIO number\n");
return -EINVAL;
}
- stmpe_gpio = devm_kzalloc(&pdev->dev, sizeof(*stmpe_gpio), GFP_KERNEL);
+ stmpe_gpio = devm_kzalloc(dev, sizeof(*stmpe_gpio), GFP_KERNEL);
if (!stmpe_gpio)
return -ENOMEM;
mutex_init(&stmpe_gpio->irq_lock);
- stmpe_gpio->dev = &pdev->dev;
stmpe_gpio->stmpe = stmpe;
stmpe_gpio->chip = template_chip;
stmpe_gpio->chip.ngpio = stmpe->num_gpios;
- stmpe_gpio->chip.parent = &pdev->dev;
+ stmpe_gpio->chip.parent = dev;
stmpe_gpio->chip.base = -1;
if (IS_ENABLED(CONFIG_DEBUG_FS))
stmpe_gpio->chip.dbg_show = stmpe_dbg_show;
- of_property_read_u32(np, "st,norequest-mask",
- &stmpe_gpio->norequest_mask);
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- dev_info(&pdev->dev,
- "device configured in no-irq mode: "
- "irqs are not available\n");
+ device_property_read_u32(dev, "st,norequest-mask", &stmpe_gpio->norequest_mask);
ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
if (ret)
return ret;
- ret = devm_add_action_or_reset(&pdev->dev, stmpe_gpio_disable, stmpe);
+ ret = devm_add_action_or_reset(dev, stmpe_gpio_disable, stmpe);
if (ret)
return ret;
+ irq = platform_get_irq(pdev, 0);
if (irq > 0) {
struct gpio_irq_chip *girq;
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
- stmpe_gpio_irq, IRQF_ONESHOT,
- "stmpe-gpio", stmpe_gpio);
- if (ret) {
- dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
- return ret;
- }
+ ret = devm_request_threaded_irq(dev, irq, NULL, stmpe_gpio_irq,
+ IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio);
+ if (ret)
+ return dev_err_probe(dev, ret, "unable to register IRQ handler\n");
girq = &stmpe_gpio->chip.irq;
gpio_irq_chip_set_chip(girq, &stmpe_gpio_irq_chip);
@@ -530,7 +519,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
girq->init_valid_mask = stmpe_init_irq_valid_mask;
}
- return devm_gpiochip_add_data(&pdev->dev, &stmpe_gpio->chip, stmpe_gpio);
+ return devm_gpiochip_add_data(dev, &stmpe_gpio->chip, stmpe_gpio);
}
static struct platform_driver stmpe_gpio_driver = {
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index 053d616f2e02..5a6406d1f03a 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -296,23 +296,17 @@ static int xway_stp_probe(struct platform_device *pdev)
if (!of_property_read_bool(pdev->dev.of_node, "lantiq,rising"))
chip->edge = XWAY_STP_FALLING;
- clk = devm_clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Failed to get clock\n");
return PTR_ERR(clk);
}
- ret = clk_prepare_enable(clk);
- if (ret)
- return ret;
-
xway_stp_hw_init(chip);
ret = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip);
- if (ret) {
- clk_disable_unprepare(clk);
+ if (ret)
return ret;
- }
dev_info(&pdev->dev, "Init done\n");
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
index 6e1a2581e6ae..5ab394ec81e6 100644
--- a/drivers/gpio/gpio-syscon.c
+++ b/drivers/gpio/gpio-syscon.c
@@ -23,7 +23,6 @@
/**
* struct syscon_gpio_data - Configuration for the device.
- * @compatible: SYSCON driver compatible string.
* @flags: Set of GPIO_SYSCON_FEAT_ flags:
* GPIO_SYSCON_FEAT_IN: GPIOs supports input,
* GPIO_SYSCON_FEAT_OUT: GPIOs supports output,
@@ -208,6 +207,7 @@ static int syscon_gpio_probe(struct platform_device *pdev)
struct syscon_gpio_priv *priv;
struct device_node *np = dev->of_node;
int ret;
+ bool use_parent_regmap = false;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -216,24 +216,28 @@ static int syscon_gpio_probe(struct platform_device *pdev)
priv->data = of_device_get_match_data(dev);
priv->syscon = syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
- if (IS_ERR(priv->syscon) && np->parent)
+ if (IS_ERR(priv->syscon) && np->parent) {
priv->syscon = syscon_node_to_regmap(np->parent);
+ use_parent_regmap = true;
+ }
if (IS_ERR(priv->syscon))
return PTR_ERR(priv->syscon);
- ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
- &priv->dreg_offset);
- if (ret)
- dev_err(dev, "can't read the data register offset!\n");
+ if (!use_parent_regmap) {
+ ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
+ &priv->dreg_offset);
+ if (ret)
+ dev_err(dev, "can't read the data register offset!\n");
- priv->dreg_offset <<= 3;
+ priv->dreg_offset <<= 3;
- ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
- &priv->dir_reg_offset);
- if (ret)
- dev_dbg(dev, "can't read the dir register offset!\n");
+ ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
+ &priv->dir_reg_offset);
+ if (ret)
+ dev_dbg(dev, "can't read the dir register offset!\n");
- priv->dir_reg_offset <<= 3;
+ priv->dir_reg_offset <<= 3;
+ }
priv->chip.parent = dev;
priv->chip.owner = THIS_MODULE;
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index ea5f9cc14bc4..6d3a39a03f58 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -18,11 +18,12 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/module.h>
-#include <linux/seq_file.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/seq_file.h>
#define GPIO_BANK(x) ((x) >> 5)
#define GPIO_PORT(x) (((x) >> 3) & 0x3)
@@ -755,7 +756,7 @@ static int tegra_gpio_probe(struct platform_device *pdev)
}
irq = &tgi->gc.irq;
- irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
+ irq->fwnode = dev_fwnode(&pdev->dev);
irq->child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq;
irq->populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec;
irq->handler = handle_simple_irq;
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 9130c691a2dd..1ecb733a5e88 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/seq_file.h>
#include <dt-bindings/gpio/tegra186-gpio.h>
@@ -928,7 +929,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
irq = &gpio->gpio.irq;
gpio_irq_chip_set_chip(irq, &tegra186_gpio_irq_chip);
- irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
+ irq->fwnode = dev_fwnode(&pdev->dev);
irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq;
irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec;
irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq;
diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c
index 8521c6aacace..5b851e904c11 100644
--- a/drivers/gpio/gpio-thunderx.c
+++ b/drivers/gpio/gpio-thunderx.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/property.h>
#include <linux/spinlock.h>
#define GPIO_RX_DAT 0x0
@@ -533,7 +534,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
chip->set_config = thunderx_gpio_set_config;
girq = &chip->irq;
gpio_irq_chip_set_chip(girq, &thunderx_gpio_irq_chip);
- girq->fwnode = of_node_to_fwnode(dev->of_node);
+ girq->fwnode = dev_fwnode(dev);
girq->parent_domain =
irq_get_irq_data(txgpio->msix_entries[0].vector)->domain;
girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq;
@@ -549,7 +550,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
for (i = 0; i < ngpio; i++) {
struct irq_fwspec fwspec;
- fwspec.fwnode = of_node_to_fwnode(dev->of_node);
+ fwspec.fwnode = dev_fwnode(dev);
fwspec.param_count = 2;
fwspec.param[0] = i;
fwspec.param[1] = IRQ_TYPE_NONE;
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
index 1f440707f8f4..da99ba13e82d 100644
--- a/drivers/gpio/gpio-uniphier.c
+++ b/drivers/gpio/gpio-uniphier.c
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/spinlock.h>
#include <dt-bindings/gpio/uniphier-gpio.h>
@@ -164,7 +165,7 @@ static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
if (offset < UNIPHIER_GPIO_IRQ_OFFSET)
return -ENXIO;
- fwspec.fwnode = of_node_to_fwnode(chip->parent->of_node);
+ fwspec.fwnode = dev_fwnode(chip->parent);
fwspec.param_count = 2;
fwspec.param[0] = offset - UNIPHIER_GPIO_IRQ_OFFSET;
/*
@@ -404,7 +405,7 @@ static int uniphier_gpio_probe(struct platform_device *pdev)
priv->domain = irq_domain_create_hierarchy(
parent_domain, 0,
UNIPHIER_GPIO_IRQ_MAX_NUM,
- of_node_to_fwnode(dev->of_node),
+ dev_fwnode(dev),
&uniphier_gpio_irq_domain_ops, priv);
if (!priv->domain)
return -ENOMEM;
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index 07e5e6323e86..27eff741fe9a 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -97,7 +97,7 @@ static inline u32 vf610_gpio_readl(void __iomem *reg)
static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct vf610_gpio_port *port = gpiochip_get_data(gc);
- unsigned long mask = BIT(gpio);
+ u32 mask = BIT(gpio);
unsigned long offset = GPIO_PDIR;
if (port->sdata->have_paddr) {
@@ -112,16 +112,16 @@ static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct vf610_gpio_port *port = gpiochip_get_data(gc);
- unsigned long mask = BIT(gpio);
+ u32 mask = BIT(gpio);
unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR;
vf610_gpio_writel(mask, port->gpio_base + offset);
}
-static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio)
{
struct vf610_gpio_port *port = gpiochip_get_data(chip);
- unsigned long mask = BIT(gpio);
+ u32 mask = BIT(gpio);
u32 val;
if (port->sdata->have_paddr) {
@@ -133,11 +133,11 @@ static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
return pinctrl_gpio_direction_input(chip, gpio);
}
-static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio,
int value)
{
struct vf610_gpio_port *port = gpiochip_get_data(chip);
- unsigned long mask = BIT(gpio);
+ u32 mask = BIT(gpio);
u32 val;
vf610_gpio_set(chip, gpio, value);
@@ -151,6 +151,19 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
return pinctrl_gpio_direction_output(chip, gpio);
}
+static int vf610_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct vf610_gpio_port *port = gpiochip_get_data(gc);
+ u32 mask = BIT(gpio);
+
+ mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
+
+ if (mask)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
+}
+
static void vf610_gpio_irq_handler(struct irq_desc *desc)
{
struct vf610_gpio_port *port =
@@ -362,6 +375,12 @@ static int vf610_gpio_probe(struct platform_device *pdev)
gc->get = vf610_gpio_get;
gc->direction_output = vf610_gpio_direction_output;
gc->set = vf610_gpio_set;
+ /*
+ * only IP has Port Data Direction Register(PDDR) can
+ * support get direction
+ */
+ if (port->sdata->have_paddr)
+ gc->get_direction = vf610_gpio_get_direction;
/* Mask all GPIO interrupts */
for (i = 0; i < gc->ngpio; i++)
diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c
index 9fae8e396c58..93544ff62513 100644
--- a/drivers/gpio/gpio-virtio.c
+++ b/drivers/gpio/gpio-virtio.c
@@ -457,15 +457,15 @@ static void virtio_gpio_free_vqs(struct virtio_device *vdev)
static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio,
struct virtio_device *vdev)
{
- const char * const names[] = { "requestq", "eventq" };
- vq_callback_t *cbs[] = {
- virtio_gpio_request_vq,
- virtio_gpio_event_vq,
+ struct virtqueue_info vqs_info[] = {
+ { "requestq", virtio_gpio_request_vq },
+ { "eventq", virtio_gpio_event_vq },
};
struct virtqueue *vqs[2] = { NULL, NULL };
int ret;
- ret = virtio_find_vqs(vdev, vgpio->irq_lines ? 2 : 1, vqs, cbs, names, NULL);
+ ret = virtio_find_vqs(vdev, vgpio->irq_lines ? 2 : 1, vqs,
+ vqs_info, NULL);
if (ret) {
dev_err(&vdev->dev, "failed to find vqs: %d\n", ret);
return ret;
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
new file mode 100644
index 000000000000..91b6352c957c
--- /dev/null
+++ b/drivers/gpio/gpio-virtuser.c
@@ -0,0 +1,1802 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Configurable virtual GPIO consumer module.
+ *
+ * Copyright (C) 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/array_size.h>
+#include <linux/atomic.h>
+#include <linux/bitmap.h>
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/configfs.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/irq_work.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+#include <linux/types.h>
+
+#define GPIO_VIRTUSER_NAME_BUF_LEN 32
+
+static DEFINE_IDA(gpio_virtuser_ida);
+static struct dentry *gpio_virtuser_dbg_root;
+
+struct gpio_virtuser_attr_data {
+ union {
+ struct gpio_desc *desc;
+ struct gpio_descs *descs;
+ };
+ struct dentry *dbgfs_dir;
+};
+
+struct gpio_virtuser_line_array_data {
+ struct gpio_virtuser_attr_data ad;
+};
+
+struct gpio_virtuser_line_data {
+ struct gpio_virtuser_attr_data ad;
+ char consumer[GPIO_VIRTUSER_NAME_BUF_LEN];
+ struct mutex consumer_lock;
+ unsigned int debounce;
+ atomic_t irq;
+ atomic_t irq_count;
+};
+
+struct gpio_virtuser_dbgfs_attr_descr {
+ const char *name;
+ const struct file_operations *fops;
+};
+
+struct gpio_virtuser_irq_work_context {
+ struct irq_work work;
+ struct completion work_completion;
+ union {
+ struct {
+ struct gpio_desc *desc;
+ int dir;
+ int val;
+ int ret;
+ };
+ struct {
+ struct gpio_descs *descs;
+ unsigned long *values;
+ };
+ };
+};
+
+static struct gpio_virtuser_irq_work_context *
+to_gpio_virtuser_irq_work_context(struct irq_work *work)
+{
+ return container_of(work, struct gpio_virtuser_irq_work_context, work);
+}
+
+static void
+gpio_virtuser_init_irq_work_context(struct gpio_virtuser_irq_work_context *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ init_completion(&ctx->work_completion);
+}
+
+static void
+gpio_virtuser_irq_work_queue_sync(struct gpio_virtuser_irq_work_context *ctx)
+{
+ irq_work_queue(&ctx->work);
+ wait_for_completion(&ctx->work_completion);
+}
+
+static void gpio_virtuser_dbgfs_emit_value_array(char *buf,
+ unsigned long *values,
+ size_t num_values)
+{
+ size_t i;
+
+ for (i = 0; i < num_values; i++)
+ buf[i] = test_bit(i, values) ? '1' : '0';
+
+ buf[i++] = '\n';
+}
+
+static void gpio_virtuser_get_value_array_atomic(struct irq_work *work)
+{
+ struct gpio_virtuser_irq_work_context *ctx =
+ to_gpio_virtuser_irq_work_context(work);
+ struct gpio_descs *descs = ctx->descs;
+
+ ctx->ret = gpiod_get_array_value(descs->ndescs, descs->desc,
+ descs->info, ctx->values);
+ complete(&ctx->work_completion);
+}
+
+static int gpio_virtuser_get_array_value(struct gpio_descs *descs,
+ unsigned long *values, bool atomic)
+{
+ struct gpio_virtuser_irq_work_context ctx;
+
+ if (!atomic)
+ return gpiod_get_array_value_cansleep(descs->ndescs,
+ descs->desc,
+ descs->info, values);
+
+ gpio_virtuser_init_irq_work_context(&ctx);
+ ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_get_value_array_atomic);
+ ctx.descs = descs;
+ ctx.values = values;
+
+ gpio_virtuser_irq_work_queue_sync(&ctx);
+
+ return ctx.ret;
+}
+
+static ssize_t gpio_virtuser_value_array_do_read(struct file *file,
+ char __user *user_buf,
+ size_t size, loff_t *ppos,
+ bool atomic)
+{
+ struct gpio_virtuser_line_data *data = file->private_data;
+ struct gpio_descs *descs = data->ad.descs;
+ size_t bufsize;
+ int ret;
+
+ unsigned long *values __free(bitmap) = bitmap_zalloc(descs->ndescs,
+ GFP_KERNEL);
+ if (!values)
+ return -ENOMEM;
+
+ ret = gpio_virtuser_get_array_value(descs, values, atomic);
+ if (ret)
+ return ret;
+
+ bufsize = descs->ndescs + 2;
+
+ char *buf __free(kfree) = kzalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ gpio_virtuser_dbgfs_emit_value_array(buf, values, descs->ndescs);
+
+ return simple_read_from_buffer(user_buf, size, ppos, buf,
+ descs->ndescs + 1);
+}
+
+static int gpio_virtuser_dbgfs_parse_value_array(const char *buf,
+ size_t len,
+ unsigned long *values)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] == '0')
+ clear_bit(i, values);
+ else if (buf[i] == '1')
+ set_bit(i, values);
+ else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void gpio_virtuser_set_value_array_atomic(struct irq_work *work)
+{
+ struct gpio_virtuser_irq_work_context *ctx =
+ to_gpio_virtuser_irq_work_context(work);
+ struct gpio_descs *descs = ctx->descs;
+
+ ctx->ret = gpiod_set_array_value(descs->ndescs, descs->desc,
+ descs->info, ctx->values);
+ complete(&ctx->work_completion);
+}
+
+static int gpio_virtuser_set_array_value(struct gpio_descs *descs,
+ unsigned long *values, bool atomic)
+{
+ struct gpio_virtuser_irq_work_context ctx;
+
+ if (!atomic)
+ return gpiod_set_array_value_cansleep(descs->ndescs,
+ descs->desc,
+ descs->info, values);
+
+ gpio_virtuser_init_irq_work_context(&ctx);
+ ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_set_value_array_atomic);
+ ctx.descs = descs;
+ ctx.values = values;
+
+ gpio_virtuser_irq_work_queue_sync(&ctx);
+
+ return ctx.ret;
+}
+
+static ssize_t gpio_virtuser_value_array_do_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos,
+ bool atomic)
+{
+ struct gpio_virtuser_line_data *data = file->private_data;
+ struct gpio_descs *descs = data->ad.descs;
+ int ret;
+
+ if (count - 1 != descs->ndescs)
+ return -EINVAL;
+
+ char *buf __free(kfree) = kzalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = simple_write_to_buffer(buf, count, ppos, user_buf, count);
+ if (ret < 0)
+ return ret;
+
+ unsigned long *values __free(bitmap) = bitmap_zalloc(descs->ndescs,
+ GFP_KERNEL);
+ if (!values)
+ return -ENOMEM;
+
+ ret = gpio_virtuser_dbgfs_parse_value_array(buf, count - 1, values);
+ if (ret)
+ return ret;
+
+ ret = gpio_virtuser_set_array_value(descs, values, atomic);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t gpio_virtuser_value_array_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return gpio_virtuser_value_array_do_read(file, user_buf, count, ppos,
+ false);
+}
+
+static ssize_t gpio_virtuser_value_array_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return gpio_virtuser_value_array_do_write(file, user_buf, count, ppos,
+ false);
+}
+
+static const struct file_operations gpio_virtuser_value_array_fops = {
+ .read = gpio_virtuser_value_array_read,
+ .write = gpio_virtuser_value_array_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t
+gpio_virtuser_value_array_atomic_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return gpio_virtuser_value_array_do_read(file, user_buf, count, ppos,
+ true);
+}
+
+static ssize_t
+gpio_virtuser_value_array_atomic_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return gpio_virtuser_value_array_do_write(file, user_buf, count, ppos,
+ true);
+}
+
+static const struct file_operations gpio_virtuser_value_array_atomic_fops = {
+ .read = gpio_virtuser_value_array_atomic_read,
+ .write = gpio_virtuser_value_array_atomic_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static void gpio_virtuser_do_get_direction_atomic(struct irq_work *work)
+{
+ struct gpio_virtuser_irq_work_context *ctx =
+ to_gpio_virtuser_irq_work_context(work);
+
+ ctx->ret = gpiod_get_direction(ctx->desc);
+ complete(&ctx->work_completion);
+}
+
+static int gpio_virtuser_get_direction_atomic(struct gpio_desc *desc)
+{
+ struct gpio_virtuser_irq_work_context ctx;
+
+ gpio_virtuser_init_irq_work_context(&ctx);
+ ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_do_get_direction_atomic);
+ ctx.desc = desc;
+
+ gpio_virtuser_irq_work_queue_sync(&ctx);
+
+ return ctx.ret;
+}
+
+static ssize_t gpio_virtuser_direction_do_read(struct file *file,
+ char __user *user_buf,
+ size_t size, loff_t *ppos,
+ bool atomic)
+{
+ struct gpio_virtuser_line_data *data = file->private_data;
+ struct gpio_desc *desc = data->ad.desc;
+ char buf[32];
+ int dir;
+
+ if (!atomic)
+ dir = gpiod_get_direction(desc);
+ else
+ dir = gpio_virtuser_get_direction_atomic(desc);
+ if (dir < 0)
+ return dir;
+
+ snprintf(buf, sizeof(buf), "%s\n", dir ? "input" : "output");
+
+ return simple_read_from_buffer(user_buf, size, ppos, buf, strlen(buf));
+}
+
+static int gpio_virtuser_set_direction(struct gpio_desc *desc, int dir, int val)
+{
+ if (dir)
+ return gpiod_direction_input(desc);
+
+ return gpiod_direction_output(desc, val);
+}
+
+static void gpio_virtuser_do_set_direction_atomic(struct irq_work *work)
+{
+ struct gpio_virtuser_irq_work_context *ctx =
+ to_gpio_virtuser_irq_work_context(work);
+
+ ctx->ret = gpio_virtuser_set_direction(ctx->desc, ctx->dir, ctx->val);
+ complete(&ctx->work_completion);
+}
+
+static int gpio_virtuser_set_direction_atomic(struct gpio_desc *desc,
+ int dir, int val)
+{
+ struct gpio_virtuser_irq_work_context ctx;
+
+ gpio_virtuser_init_irq_work_context(&ctx);
+ ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_do_set_direction_atomic);
+ ctx.desc = desc;
+ ctx.dir = dir;
+ ctx.val = val;
+
+ gpio_virtuser_irq_work_queue_sync(&ctx);
+
+ return ctx.ret;
+}
+
+static ssize_t gpio_virtuser_direction_do_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos,
+ bool atomic)
+{
+ struct gpio_virtuser_line_data *data = file->private_data;
+ struct gpio_desc *desc = data->ad.desc;
+ char buf[32], *trimmed;
+ int ret, dir, val = 0;
+
+ ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
+ if (ret < 0)
+ return ret;
+
+ trimmed = strim(buf);
+
+ if (strcmp(trimmed, "input") == 0) {
+ dir = 1;
+ } else if (strcmp(trimmed, "output-high") == 0) {
+ dir = 0;
+ val = 1;
+ } else if (strcmp(trimmed, "output-low") == 0) {
+ dir = val = 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (!atomic)
+ ret = gpio_virtuser_set_direction(desc, dir, val);
+ else
+ ret = gpio_virtuser_set_direction_atomic(desc, dir, val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t gpio_virtuser_direction_read(struct file *file,
+ char __user *user_buf,
+ size_t size, loff_t *ppos)
+{
+ return gpio_virtuser_direction_do_read(file, user_buf, size, ppos,
+ false);
+}
+
+static ssize_t gpio_virtuser_direction_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return gpio_virtuser_direction_do_write(file, user_buf, count, ppos,
+ false);
+}
+
+static const struct file_operations gpio_virtuser_direction_fops = {
+ .read = gpio_virtuser_direction_read,
+ .write = gpio_virtuser_direction_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t gpio_virtuser_direction_atomic_read(struct file *file,
+ char __user *user_buf,
+ size_t size, loff_t *ppos)
+{
+ return gpio_virtuser_direction_do_read(file, user_buf, size, ppos,
+ true);
+}
+
+static ssize_t gpio_virtuser_direction_atomic_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return gpio_virtuser_direction_do_write(file, user_buf, count, ppos,
+ true);
+}
+
+static const struct file_operations gpio_virtuser_direction_atomic_fops = {
+ .read = gpio_virtuser_direction_atomic_read,
+ .write = gpio_virtuser_direction_atomic_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int gpio_virtuser_value_get(void *data, u64 *val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+ int ret;
+
+ ret = gpiod_get_value_cansleep(ld->ad.desc);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int gpio_virtuser_value_set(void *data, u64 val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+
+ if (val > 1)
+ return -EINVAL;
+
+ gpiod_set_value_cansleep(ld->ad.desc, (int)val);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_fops,
+ gpio_virtuser_value_get,
+ gpio_virtuser_value_set,
+ "%llu\n");
+
+static void gpio_virtuser_get_value_atomic(struct irq_work *work)
+{
+ struct gpio_virtuser_irq_work_context *ctx =
+ to_gpio_virtuser_irq_work_context(work);
+
+ ctx->val = gpiod_get_value(ctx->desc);
+ complete(&ctx->work_completion);
+}
+
+static int gpio_virtuser_value_atomic_get(void *data, u64 *val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+ struct gpio_virtuser_irq_work_context ctx;
+
+ gpio_virtuser_init_irq_work_context(&ctx);
+ ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_get_value_atomic);
+ ctx.desc = ld->ad.desc;
+
+ gpio_virtuser_irq_work_queue_sync(&ctx);
+
+ if (ctx.val < 0)
+ return ctx.val;
+
+ *val = ctx.val;
+
+ return 0;
+}
+
+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);
+ complete(&ctx->work_completion);
+}
+
+static int gpio_virtuser_value_atomic_set(void *data, u64 val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+ struct gpio_virtuser_irq_work_context ctx;
+
+ if (val > 1)
+ return -EINVAL;
+
+ gpio_virtuser_init_irq_work_context(&ctx);
+ ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_set_value_atomic);
+ ctx.desc = ld->ad.desc;
+ ctx.val = (int)val;
+
+ gpio_virtuser_irq_work_queue_sync(&ctx);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_atomic_fops,
+ gpio_virtuser_value_atomic_get,
+ gpio_virtuser_value_atomic_set,
+ "%llu\n");
+
+static int gpio_virtuser_debounce_get(void *data, u64 *val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+
+ *val = READ_ONCE(ld->debounce);
+
+ return 0;
+}
+
+static int gpio_virtuser_debounce_set(void *data, u64 val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+ int ret;
+
+ if (val > UINT_MAX)
+ return -E2BIG;
+
+ ret = gpiod_set_debounce(ld->ad.desc, (unsigned int)val);
+ if (ret)
+ /* Don't propagate errno unknown to user-space. */
+ return ret == -ENOTSUPP ? -EOPNOTSUPP : ret;
+
+ WRITE_ONCE(ld->debounce, (unsigned int)val);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_debounce_fops,
+ gpio_virtuser_debounce_get,
+ gpio_virtuser_debounce_set,
+ "%llu\n");
+
+static ssize_t gpio_virtuser_consumer_read(struct file *file,
+ char __user *user_buf,
+ size_t size, loff_t *ppos)
+{
+ struct gpio_virtuser_line_data *data = file->private_data;
+ char buf[GPIO_VIRTUSER_NAME_BUF_LEN + 1];
+ ssize_t ret;
+
+ memset(buf, 0x0, sizeof(buf));
+
+ scoped_guard(mutex, &data->consumer_lock)
+ ret = snprintf(buf, sizeof(buf), "%s\n", data->consumer);
+
+ return simple_read_from_buffer(user_buf, size, ppos, buf, ret);
+}
+
+static ssize_t gpio_virtuser_consumer_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct gpio_virtuser_line_data *data = file->private_data;
+ char buf[GPIO_VIRTUSER_NAME_BUF_LEN + 2];
+ int ret;
+
+ ret = simple_write_to_buffer(buf, GPIO_VIRTUSER_NAME_BUF_LEN, ppos,
+ user_buf, count);
+ if (ret < 0)
+ return ret;
+
+ buf[strlen(buf) - 1] = '\0';
+
+ ret = gpiod_set_consumer_name(data->ad.desc, buf);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &data->consumer_lock)
+ strscpy(data->consumer, buf, sizeof(data->consumer));
+
+ return count;
+}
+
+static const struct file_operations gpio_virtuser_consumer_fops = {
+ .read = gpio_virtuser_consumer_read,
+ .write = gpio_virtuser_consumer_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int gpio_virtuser_interrupts_get(void *data, u64 *val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+
+ *val = atomic_read(&ld->irq_count);
+
+ return 0;
+}
+
+static irqreturn_t gpio_virtuser_irq_handler(int irq, void *data)
+{
+ struct gpio_virtuser_line_data *ld = data;
+
+ atomic_inc(&ld->irq_count);
+
+ return IRQ_HANDLED;
+}
+
+static int gpio_virtuser_interrupts_set(void *data, u64 val)
+{
+ struct gpio_virtuser_line_data *ld = data;
+ int irq, ret;
+
+ if (val > 1)
+ return -EINVAL;
+
+ if (val) {
+ irq = gpiod_to_irq(ld->ad.desc);
+ if (irq < 0)
+ return irq;
+
+ ret = request_threaded_irq(irq, NULL,
+ gpio_virtuser_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ ld->consumer, data);
+ if (ret)
+ return ret;
+
+ atomic_set(&ld->irq, irq);
+ } else {
+ irq = atomic_xchg(&ld->irq, 0);
+ free_irq(irq, ld);
+ }
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_interrupts_fops,
+ gpio_virtuser_interrupts_get,
+ gpio_virtuser_interrupts_set,
+ "%llu\n");
+
+static const struct gpio_virtuser_dbgfs_attr_descr
+gpio_virtuser_line_array_dbgfs_attrs[] = {
+ {
+ .name = "values",
+ .fops = &gpio_virtuser_value_array_fops,
+ },
+ {
+ .name = "values_atomic",
+ .fops = &gpio_virtuser_value_array_atomic_fops,
+ },
+};
+
+static const struct gpio_virtuser_dbgfs_attr_descr
+gpio_virtuser_line_dbgfs_attrs[] = {
+ {
+ .name = "direction",
+ .fops = &gpio_virtuser_direction_fops,
+ },
+ {
+ .name = "direction_atomic",
+ .fops = &gpio_virtuser_direction_atomic_fops,
+ },
+ {
+ .name = "value",
+ .fops = &gpio_virtuser_value_fops,
+ },
+ {
+ .name = "value_atomic",
+ .fops = &gpio_virtuser_value_atomic_fops,
+ },
+ {
+ .name = "debounce",
+ .fops = &gpio_virtuser_debounce_fops,
+ },
+ {
+ .name = "consumer",
+ .fops = &gpio_virtuser_consumer_fops,
+ },
+ {
+ .name = "interrupts",
+ .fops = &gpio_virtuser_interrupts_fops,
+ },
+};
+
+static int gpio_virtuser_create_debugfs_attrs(
+ const struct gpio_virtuser_dbgfs_attr_descr *attr,
+ size_t num_attrs, struct dentry *parent, void *data)
+{
+ struct dentry *ret;
+ size_t i;
+
+ for (i = 0; i < num_attrs; i++, attr++) {
+ ret = debugfs_create_file(attr->name, 0644, parent, data,
+ attr->fops);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ }
+
+ return 0;
+}
+
+static int gpio_virtuser_dbgfs_init_line_array_attrs(struct device *dev,
+ struct gpio_descs *descs,
+ const char *id,
+ struct dentry *dbgfs_entry)
+{
+ struct gpio_virtuser_line_array_data *data;
+ char *name;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ad.descs = descs;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "gpiod:%s", id);
+ if (!name)
+ return -ENOMEM;
+
+ data->ad.dbgfs_dir = debugfs_create_dir(name, dbgfs_entry);
+ if (IS_ERR(data->ad.dbgfs_dir))
+ return PTR_ERR(data->ad.dbgfs_dir);
+
+ return gpio_virtuser_create_debugfs_attrs(
+ gpio_virtuser_line_array_dbgfs_attrs,
+ ARRAY_SIZE(gpio_virtuser_line_array_dbgfs_attrs),
+ data->ad.dbgfs_dir, data);
+}
+
+static int gpio_virtuser_dbgfs_init_line_attrs(struct device *dev,
+ struct gpio_desc *desc,
+ const char *id,
+ unsigned int index,
+ struct dentry *dbgfs_entry)
+{
+ struct gpio_virtuser_line_data *data;
+ char *name;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ad.desc = desc;
+ strscpy(data->consumer, id);
+ atomic_set(&data->irq, 0);
+ atomic_set(&data->irq_count, 0);
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "gpiod:%s:%u", id, index);
+ if (!name)
+ return -ENOMEM;
+
+ ret = devm_mutex_init(dev, &data->consumer_lock);
+ if (ret)
+ return ret;
+
+ data->ad.dbgfs_dir = debugfs_create_dir(name, dbgfs_entry);
+ if (IS_ERR(data->ad.dbgfs_dir))
+ return PTR_ERR(data->ad.dbgfs_dir);
+
+ return gpio_virtuser_create_debugfs_attrs(
+ gpio_virtuser_line_dbgfs_attrs,
+ ARRAY_SIZE(gpio_virtuser_line_dbgfs_attrs),
+ data->ad.dbgfs_dir, data);
+}
+
+static void gpio_virtuser_debugfs_remove(void *data)
+{
+ struct dentry *dbgfs_entry = data;
+
+ debugfs_remove_recursive(dbgfs_entry);
+}
+
+static int gpio_virtuser_prop_is_gpio(struct property *prop)
+{
+ char *dash = strrchr(prop->name, '-');
+
+ return dash && strcmp(dash, "-gpios") == 0;
+}
+
+/*
+ * If this is an OF-based system, then we iterate over properties and consider
+ * all whose names end in "-gpios". For configfs we expect an additional string
+ * array property - "gpio-virtuser,ids" - containing the list of all GPIO IDs
+ * to request.
+ */
+static int gpio_virtuser_count_ids(struct device *dev)
+{
+ struct device_node *of_node = dev_of_node(dev);
+ struct property *prop;
+ int ret = 0;
+
+ if (!of_node)
+ return device_property_string_array_count(dev,
+ "gpio-virtuser,ids");
+
+ for_each_property_of_node(of_node, prop) {
+ if (gpio_virtuser_prop_is_gpio(prop))
+ ++ret;
+ }
+
+ return ret;
+}
+
+static int gpio_virtuser_get_ids(struct device *dev, const char **ids,
+ int num_ids)
+{
+ struct device_node *of_node = dev_of_node(dev);
+ struct property *prop;
+ size_t pos = 0, diff;
+ char *dash, *tmp;
+
+ if (!of_node)
+ return device_property_read_string_array(dev,
+ "gpio-virtuser,ids",
+ ids, num_ids);
+
+ for_each_property_of_node(of_node, prop) {
+ if (!gpio_virtuser_prop_is_gpio(prop))
+ continue;
+
+ dash = strrchr(prop->name, '-');
+ diff = dash - prop->name;
+
+ tmp = devm_kmemdup(dev, prop->name, diff + 1,
+ GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp[diff] = '\0';
+ ids[pos++] = tmp;
+ }
+
+ return 0;
+}
+
+static int gpio_virtuser_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dentry *dbgfs_entry;
+ struct gpio_descs *descs;
+ int ret, num_ids = 0, i;
+ const char **ids;
+ unsigned int j;
+
+ num_ids = gpio_virtuser_count_ids(dev);
+ if (num_ids < 0)
+ return dev_err_probe(dev, num_ids,
+ "Failed to get the number of GPIOs to request\n");
+
+ if (num_ids == 0)
+ return dev_err_probe(dev, -EINVAL, "No GPIO IDs specified\n");
+
+ ids = devm_kcalloc(dev, num_ids, sizeof(*ids), GFP_KERNEL);
+ if (!ids)
+ return -ENOMEM;
+
+ ret = gpio_virtuser_get_ids(dev, ids, num_ids);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to get the IDs of GPIOs to request\n");
+
+ dbgfs_entry = debugfs_create_dir(dev_name(dev), gpio_virtuser_dbg_root);
+ ret = devm_add_action_or_reset(dev, gpio_virtuser_debugfs_remove,
+ dbgfs_entry);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_ids; i++) {
+ descs = devm_gpiod_get_array(dev, ids[i], GPIOD_ASIS);
+ if (IS_ERR(descs))
+ return dev_err_probe(dev, PTR_ERR(descs),
+ "Failed to request the '%s' GPIOs\n",
+ ids[i]);
+
+ ret = gpio_virtuser_dbgfs_init_line_array_attrs(dev, descs,
+ ids[i],
+ dbgfs_entry);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to setup the debugfs array interface for the '%s' GPIOs\n",
+ ids[i]);
+
+ for (j = 0; j < descs->ndescs; j++) {
+ ret = gpio_virtuser_dbgfs_init_line_attrs(dev,
+ descs->desc[j], ids[i],
+ j, dbgfs_entry);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to setup the debugfs line interface for the '%s' GPIOs\n",
+ ids[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id gpio_virtuser_of_match[] = {
+ { .compatible = "gpio-virtuser" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpio_virtuser_of_match);
+
+static struct platform_driver gpio_virtuser_driver = {
+ .driver = {
+ .name = "gpio-virtuser",
+ .of_match_table = gpio_virtuser_of_match,
+ },
+ .probe = gpio_virtuser_probe,
+};
+
+struct gpio_virtuser_device {
+ struct config_group group;
+
+ struct platform_device *pdev;
+ int id;
+ struct mutex lock;
+
+ struct notifier_block bus_notifier;
+ struct completion probe_completion;
+ bool driver_bound;
+
+ struct gpiod_lookup_table *lookup_table;
+
+ struct list_head lookup_list;
+};
+
+static int gpio_virtuser_bus_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct gpio_virtuser_device *vdev;
+ struct device *dev = data;
+ char devname[32];
+
+ vdev = container_of(nb, struct gpio_virtuser_device, bus_notifier);
+ snprintf(devname, sizeof(devname), "gpio-virtuser.%d", vdev->id);
+
+ if (!device_match_name(dev, devname))
+ return NOTIFY_DONE;
+
+ switch (action) {
+ case BUS_NOTIFY_BOUND_DRIVER:
+ vdev->driver_bound = true;
+ break;
+ case BUS_NOTIFY_DRIVER_NOT_BOUND:
+ vdev->driver_bound = false;
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ complete(&vdev->probe_completion);
+ return NOTIFY_OK;
+}
+
+static struct gpio_virtuser_device *
+to_gpio_virtuser_device(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ return container_of(group, struct gpio_virtuser_device, group);
+}
+
+static bool
+gpio_virtuser_device_is_live(struct gpio_virtuser_device *dev)
+{
+ lockdep_assert_held(&dev->lock);
+
+ return !!dev->pdev;
+}
+
+struct gpio_virtuser_lookup {
+ struct config_group group;
+
+ struct gpio_virtuser_device *parent;
+ struct list_head siblings;
+
+ char *con_id;
+
+ struct list_head entry_list;
+};
+
+static struct gpio_virtuser_lookup *
+to_gpio_virtuser_lookup(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ return container_of(group, struct gpio_virtuser_lookup, group);
+}
+
+struct gpio_virtuser_lookup_entry {
+ struct config_group group;
+
+ struct gpio_virtuser_lookup *parent;
+ struct list_head siblings;
+
+ char *key;
+ /* Can be negative to indicate lookup by name. */
+ int offset;
+ enum gpio_lookup_flags flags;
+};
+
+static struct gpio_virtuser_lookup_entry *
+to_gpio_virtuser_lookup_entry(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ return container_of(group, struct gpio_virtuser_lookup_entry, group);
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_key_show(struct config_item *item, char *page)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+
+ guard(mutex)(&dev->lock);
+
+ return sprintf(page, "%s\n", entry->key ?: "");
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_key_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+
+ char *key __free(kfree) = kstrndup(skip_spaces(page), count,
+ GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ strim(key);
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return -EBUSY;
+
+ kfree(entry->key);
+ entry->key = no_free_ptr(key);
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_virtuser_lookup_entry_config_, key);
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_offset_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+ unsigned int offset;
+
+ scoped_guard(mutex, &dev->lock)
+ offset = entry->offset;
+
+ return sprintf(page, "%d\n", offset);
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_offset_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+ int offset, ret;
+
+ ret = kstrtoint(page, 0, &offset);
+ if (ret)
+ return ret;
+
+ /*
+ * Negative number here means: 'key' represents a line name to lookup.
+ * Non-negative means: 'key' represents the label of the chip with
+ * the 'offset' value representing the line within that chip.
+ *
+ * GPIOLIB uses the U16_MAX value to indicate lookup by line name so
+ * the greatest offset we can accept is (U16_MAX - 1).
+ */
+ if (offset > (U16_MAX - 1))
+ return -EINVAL;
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return -EBUSY;
+
+ entry->offset = offset;
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_virtuser_lookup_entry_config_, offset);
+
+static enum gpio_lookup_flags
+gpio_virtuser_lookup_get_flags(struct config_item *item)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+
+ guard(mutex)(&dev->lock);
+
+ return entry->flags;
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_drive_show(struct config_item *item, char *page)
+{
+ enum gpio_lookup_flags flags = gpio_virtuser_lookup_get_flags(item);
+ const char *repr;
+
+ if (flags & GPIO_OPEN_DRAIN)
+ repr = "open-drain";
+ else if (flags & GPIO_OPEN_SOURCE)
+ repr = "open-source";
+ else
+ repr = "push-pull";
+
+ return sprintf(page, "%s\n", repr);
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_drive_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return -EBUSY;
+
+ if (sysfs_streq(page, "push-pull")) {
+ entry->flags &= ~(GPIO_OPEN_DRAIN | GPIO_OPEN_SOURCE);
+ } else if (sysfs_streq(page, "open-drain")) {
+ entry->flags &= ~GPIO_OPEN_SOURCE;
+ entry->flags |= GPIO_OPEN_DRAIN;
+ } else if (sysfs_streq(page, "open-source")) {
+ entry->flags &= ~GPIO_OPEN_DRAIN;
+ entry->flags |= GPIO_OPEN_SOURCE;
+ } else {
+ count = -EINVAL;
+ }
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_virtuser_lookup_entry_config_, drive);
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_pull_show(struct config_item *item, char *page)
+{
+ enum gpio_lookup_flags flags = gpio_virtuser_lookup_get_flags(item);
+ const char *repr;
+
+ if (flags & GPIO_PULL_UP)
+ repr = "pull-up";
+ else if (flags & GPIO_PULL_DOWN)
+ repr = "pull-down";
+ else if (flags & GPIO_PULL_DISABLE)
+ repr = "pull-disabled";
+ else
+ repr = "as-is";
+
+ return sprintf(page, "%s\n", repr);
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_pull_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return -EBUSY;
+
+ if (sysfs_streq(page, "pull-up")) {
+ entry->flags &= ~(GPIO_PULL_DOWN | GPIO_PULL_DISABLE);
+ entry->flags |= GPIO_PULL_UP;
+ } else if (sysfs_streq(page, "pull-down")) {
+ entry->flags &= ~(GPIO_PULL_UP | GPIO_PULL_DISABLE);
+ entry->flags |= GPIO_PULL_DOWN;
+ } else if (sysfs_streq(page, "pull-disabled")) {
+ entry->flags &= ~(GPIO_PULL_UP | GPIO_PULL_DOWN);
+ entry->flags |= GPIO_PULL_DISABLE;
+ } else if (sysfs_streq(page, "as-is")) {
+ entry->flags &= ~(GPIO_PULL_UP | GPIO_PULL_DOWN |
+ GPIO_PULL_DISABLE);
+ } else {
+ count = -EINVAL;
+ }
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_virtuser_lookup_entry_config_, pull);
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_active_low_show(struct config_item *item,
+ char *page)
+{
+ enum gpio_lookup_flags flags = gpio_virtuser_lookup_get_flags(item);
+
+ return sprintf(page, "%c\n", flags & GPIO_ACTIVE_LOW ? '1' : '0');
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_active_low_store(struct config_item *item,
+ const char *page,
+ size_t count)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+ bool active_low;
+ int ret;
+
+ ret = kstrtobool(page, &active_low);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return -EBUSY;
+
+ if (active_low)
+ entry->flags |= GPIO_ACTIVE_LOW;
+ else
+ entry->flags &= ~GPIO_ACTIVE_LOW;
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_virtuser_lookup_entry_config_, active_low);
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_transitory_show(struct config_item *item,
+ char *page)
+{
+ enum gpio_lookup_flags flags = gpio_virtuser_lookup_get_flags(item);
+
+ return sprintf(page, "%c\n", flags & GPIO_TRANSITORY ? '1' : '0');
+}
+
+static ssize_t
+gpio_virtuser_lookup_entry_config_transitory_store(struct config_item *item,
+ const char *page,
+ size_t count)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+ bool transitory;
+ int ret;
+
+ ret = kstrtobool(page, &transitory);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return -EBUSY;
+
+ if (transitory)
+ entry->flags |= GPIO_TRANSITORY;
+ else
+ entry->flags &= ~GPIO_TRANSITORY;
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_virtuser_lookup_entry_config_, transitory);
+
+static struct configfs_attribute *gpio_virtuser_lookup_entry_config_attrs[] = {
+ &gpio_virtuser_lookup_entry_config_attr_key,
+ &gpio_virtuser_lookup_entry_config_attr_offset,
+ &gpio_virtuser_lookup_entry_config_attr_drive,
+ &gpio_virtuser_lookup_entry_config_attr_pull,
+ &gpio_virtuser_lookup_entry_config_attr_active_low,
+ &gpio_virtuser_lookup_entry_config_attr_transitory,
+ NULL
+};
+
+static ssize_t
+gpio_virtuser_device_config_dev_name_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_virtuser_device *dev = to_gpio_virtuser_device(item);
+ struct platform_device *pdev;
+
+ guard(mutex)(&dev->lock);
+
+ pdev = dev->pdev;
+ if (pdev)
+ return sprintf(page, "%s\n", dev_name(&pdev->dev));
+
+ return sprintf(page, "gpio-sim.%d\n", dev->id);
+}
+
+CONFIGFS_ATTR_RO(gpio_virtuser_device_config_, dev_name);
+
+static ssize_t gpio_virtuser_device_config_live_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_virtuser_device *dev = to_gpio_virtuser_device(item);
+ bool live;
+
+ scoped_guard(mutex, &dev->lock)
+ live = gpio_virtuser_device_is_live(dev);
+
+ return sprintf(page, "%c\n", live ? '1' : '0');
+}
+
+static size_t
+gpio_virtuser_get_lookup_count(struct gpio_virtuser_device *dev)
+{
+ struct gpio_virtuser_lookup *lookup;
+ size_t count = 0;
+
+ lockdep_assert_held(&dev->lock);
+
+ list_for_each_entry(lookup, &dev->lookup_list, siblings)
+ count += list_count_nodes(&lookup->entry_list);
+
+ return count;
+}
+
+static int
+gpio_virtuser_make_lookup_table(struct gpio_virtuser_device *dev)
+{
+ size_t num_entries = gpio_virtuser_get_lookup_count(dev);
+ struct gpio_virtuser_lookup_entry *entry;
+ struct gpio_virtuser_lookup *lookup;
+ unsigned int i = 0;
+
+ lockdep_assert_held(&dev->lock);
+
+ struct gpiod_lookup_table *table __free(kfree) =
+ kzalloc(struct_size(table, table, num_entries + 1), GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+
+ table->dev_id = kasprintf(GFP_KERNEL, "gpio-virtuser.%d", dev->id);
+ if (!table->dev_id)
+ return -ENOMEM;
+
+ list_for_each_entry(lookup, &dev->lookup_list, siblings) {
+ list_for_each_entry(entry, &lookup->entry_list, siblings) {
+ table->table[i] =
+ GPIO_LOOKUP_IDX(entry->key,
+ entry->offset < 0 ? U16_MAX : entry->offset,
+ lookup->con_id, i, entry->flags);
+ i++;
+ }
+ }
+
+ gpiod_add_lookup_table(table);
+ dev->lookup_table = no_free_ptr(table);
+
+ return 0;
+}
+
+static struct fwnode_handle *
+gpio_virtuser_make_device_swnode(struct gpio_virtuser_device *dev)
+{
+ struct property_entry properties[2];
+ struct gpio_virtuser_lookup *lookup;
+ unsigned int i = 0;
+ size_t num_ids;
+
+ memset(properties, 0, sizeof(properties));
+
+ num_ids = list_count_nodes(&dev->lookup_list);
+ char **ids __free(kfree) = kcalloc(num_ids + 1, sizeof(*ids),
+ GFP_KERNEL);
+ if (!ids)
+ return ERR_PTR(-ENOMEM);
+
+ list_for_each_entry(lookup, &dev->lookup_list, siblings)
+ ids[i++] = lookup->con_id;
+
+ properties[0] = PROPERTY_ENTRY_STRING_ARRAY_LEN("gpio-virtuser,ids",
+ ids, num_ids);
+
+ return fwnode_create_software_node(properties, NULL);
+}
+
+static int
+gpio_virtuser_device_activate(struct gpio_virtuser_device *dev)
+{
+ struct platform_device_info pdevinfo;
+ struct fwnode_handle *swnode;
+ struct platform_device *pdev;
+ int ret;
+
+ lockdep_assert_held(&dev->lock);
+
+ if (list_empty(&dev->lookup_list))
+ return -ENODATA;
+
+ swnode = gpio_virtuser_make_device_swnode(dev);
+ if (IS_ERR(swnode))
+ return PTR_ERR(swnode);
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo.name = "gpio-virtuser";
+ pdevinfo.id = dev->id;
+ pdevinfo.fwnode = swnode;
+
+ ret = gpio_virtuser_make_lookup_table(dev);
+ if (ret) {
+ fwnode_remove_software_node(swnode);
+ return ret;
+ }
+
+ reinit_completion(&dev->probe_completion);
+ dev->driver_bound = false;
+ bus_register_notifier(&platform_bus_type, &dev->bus_notifier);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier);
+ fwnode_remove_software_node(swnode);
+ return PTR_ERR(pdev);
+ }
+
+ wait_for_completion(&dev->probe_completion);
+ bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier);
+
+ if (!dev->driver_bound) {
+ platform_device_unregister(pdev);
+ fwnode_remove_software_node(swnode);
+ return -ENXIO;
+ }
+
+ dev->pdev = pdev;
+
+ return 0;
+}
+
+static void
+gpio_virtuser_device_deactivate(struct gpio_virtuser_device *dev)
+{
+ struct fwnode_handle *swnode;
+
+ lockdep_assert_held(&dev->lock);
+
+ swnode = dev_fwnode(&dev->pdev->dev);
+ platform_device_unregister(dev->pdev);
+ fwnode_remove_software_node(swnode);
+ dev->pdev = NULL;
+ gpiod_remove_lookup_table(dev->lookup_table);
+ kfree(dev->lookup_table);
+}
+
+static ssize_t
+gpio_virtuser_device_config_live_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_virtuser_device *dev = to_gpio_virtuser_device(item);
+ int ret = 0;
+ bool live;
+
+ ret = kstrtobool(page, &live);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&dev->lock);
+
+ if (live == gpio_virtuser_device_is_live(dev))
+ return -EPERM;
+
+ if (live)
+ ret = gpio_virtuser_device_activate(dev);
+ else
+ gpio_virtuser_device_deactivate(dev);
+
+ return ret ?: count;
+}
+
+CONFIGFS_ATTR(gpio_virtuser_device_config_, live);
+
+static struct configfs_attribute *gpio_virtuser_device_config_attrs[] = {
+ &gpio_virtuser_device_config_attr_dev_name,
+ &gpio_virtuser_device_config_attr_live,
+ NULL
+};
+
+static void
+gpio_virtuser_lookup_entry_config_group_release(struct config_item *item)
+{
+ struct gpio_virtuser_lookup_entry *entry =
+ to_gpio_virtuser_lookup_entry(item);
+ struct gpio_virtuser_device *dev = entry->parent->parent;
+
+ guard(mutex)(&dev->lock);
+
+ list_del(&entry->siblings);
+
+ kfree(entry->key);
+ kfree(entry);
+}
+
+static struct
+configfs_item_operations gpio_virtuser_lookup_entry_config_item_ops = {
+ .release = gpio_virtuser_lookup_entry_config_group_release,
+};
+
+static const struct
+config_item_type gpio_virtuser_lookup_entry_config_group_type = {
+ .ct_item_ops = &gpio_virtuser_lookup_entry_config_item_ops,
+ .ct_attrs = gpio_virtuser_lookup_entry_config_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *
+gpio_virtuser_make_lookup_entry_group(struct config_group *group,
+ const char *name)
+{
+ struct gpio_virtuser_lookup *lookup =
+ to_gpio_virtuser_lookup(&group->cg_item);
+ struct gpio_virtuser_device *dev = lookup->parent;
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return ERR_PTR(-EBUSY);
+
+ struct gpio_virtuser_lookup_entry *entry __free(kfree) =
+ kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&entry->group, name,
+ &gpio_virtuser_lookup_entry_config_group_type);
+ entry->flags = GPIO_LOOKUP_FLAGS_DEFAULT;
+ entry->parent = lookup;
+ list_add_tail(&entry->siblings, &lookup->entry_list);
+
+ return &no_free_ptr(entry)->group;
+}
+
+static void gpio_virtuser_lookup_config_group_release(struct config_item *item)
+{
+ struct gpio_virtuser_lookup *lookup = to_gpio_virtuser_lookup(item);
+ struct gpio_virtuser_device *dev = lookup->parent;
+
+ guard(mutex)(&dev->lock);
+
+ list_del(&lookup->siblings);
+
+ kfree(lookup->con_id);
+ kfree(lookup);
+}
+
+static struct configfs_item_operations gpio_virtuser_lookup_config_item_ops = {
+ .release = gpio_virtuser_lookup_config_group_release,
+};
+
+static struct
+configfs_group_operations gpio_virtuser_lookup_config_group_ops = {
+ .make_group = gpio_virtuser_make_lookup_entry_group,
+};
+
+static const struct config_item_type gpio_virtuser_lookup_config_group_type = {
+ .ct_group_ops = &gpio_virtuser_lookup_config_group_ops,
+ .ct_item_ops = &gpio_virtuser_lookup_config_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *
+gpio_virtuser_make_lookup_group(struct config_group *group, const char *name)
+{
+ struct gpio_virtuser_device *dev =
+ to_gpio_virtuser_device(&group->cg_item);
+
+ if (strlen(name) > (GPIO_VIRTUSER_NAME_BUF_LEN - 1))
+ return ERR_PTR(-E2BIG);
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ return ERR_PTR(-EBUSY);
+
+ struct gpio_virtuser_lookup *lookup __free(kfree) =
+ kzalloc(sizeof(*lookup), GFP_KERNEL);
+ if (!lookup)
+ return ERR_PTR(-ENOMEM);
+
+ lookup->con_id = kstrdup(name, GFP_KERNEL);
+ if (!lookup->con_id)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&lookup->group, name,
+ &gpio_virtuser_lookup_config_group_type);
+ INIT_LIST_HEAD(&lookup->entry_list);
+ lookup->parent = dev;
+ list_add_tail(&lookup->siblings, &dev->lookup_list);
+
+ return &no_free_ptr(lookup)->group;
+}
+
+static void gpio_virtuser_device_config_group_release(struct config_item *item)
+{
+ struct gpio_virtuser_device *dev = to_gpio_virtuser_device(item);
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_virtuser_device_is_live(dev))
+ gpio_virtuser_device_deactivate(dev);
+
+ mutex_destroy(&dev->lock);
+ ida_free(&gpio_virtuser_ida, dev->id);
+ kfree(dev);
+}
+
+static struct configfs_item_operations gpio_virtuser_device_config_item_ops = {
+ .release = gpio_virtuser_device_config_group_release,
+};
+
+static struct configfs_group_operations gpio_virtuser_device_config_group_ops = {
+ .make_group = gpio_virtuser_make_lookup_group,
+};
+
+static const struct config_item_type gpio_virtuser_device_config_group_type = {
+ .ct_group_ops = &gpio_virtuser_device_config_group_ops,
+ .ct_item_ops = &gpio_virtuser_device_config_item_ops,
+ .ct_attrs = gpio_virtuser_device_config_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *
+gpio_virtuser_config_make_device_group(struct config_group *group,
+ const char *name)
+{
+ struct gpio_virtuser_device *dev __free(kfree) = kzalloc(sizeof(*dev),
+ GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ dev->id = ida_alloc(&gpio_virtuser_ida, GFP_KERNEL);
+ if (dev->id < 0)
+ return ERR_PTR(dev->id);
+
+ config_group_init_type_name(&dev->group, name,
+ &gpio_virtuser_device_config_group_type);
+ mutex_init(&dev->lock);
+ INIT_LIST_HEAD(&dev->lookup_list);
+ dev->bus_notifier.notifier_call = gpio_virtuser_bus_notifier_call;
+ init_completion(&dev->probe_completion);
+
+ return &no_free_ptr(dev)->group;
+}
+
+static struct configfs_group_operations gpio_virtuser_config_group_ops = {
+ .make_group = gpio_virtuser_config_make_device_group,
+};
+
+static const struct config_item_type gpio_virtuser_config_type = {
+ .ct_group_ops = &gpio_virtuser_config_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem gpio_virtuser_config_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "gpio-virtuser",
+ .ci_type = &gpio_virtuser_config_type,
+ },
+ },
+};
+
+static int __init gpio_virtuser_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&gpio_virtuser_driver);
+ if (ret) {
+ pr_err("Failed to register the platform driver: %d\n", ret);
+ return ret;
+ }
+
+ config_group_init(&gpio_virtuser_config_subsys.su_group);
+ mutex_init(&gpio_virtuser_config_subsys.su_mutex);
+ ret = configfs_register_subsystem(&gpio_virtuser_config_subsys);
+ if (ret) {
+ pr_err("Failed to register the '%s' configfs subsystem: %d\n",
+ gpio_virtuser_config_subsys.su_group.cg_item.ci_namebuf,
+ ret);
+ goto err_plat_drv_unreg;
+ }
+
+ gpio_virtuser_dbg_root = debugfs_create_dir("gpio-virtuser", NULL);
+ if (IS_ERR(gpio_virtuser_dbg_root)) {
+ ret = PTR_ERR(gpio_virtuser_dbg_root);
+ pr_err("Failed to create the debugfs tree: %d\n", ret);
+ goto err_configfs_unreg;
+ }
+
+ return 0;
+
+err_configfs_unreg:
+ configfs_unregister_subsystem(&gpio_virtuser_config_subsys);
+err_plat_drv_unreg:
+ mutex_destroy(&gpio_virtuser_config_subsys.su_mutex);
+ platform_driver_unregister(&gpio_virtuser_driver);
+
+ return ret;
+}
+module_init(gpio_virtuser_init);
+
+static void __exit gpio_virtuser_exit(void)
+{
+ configfs_unregister_subsystem(&gpio_virtuser_config_subsys);
+ mutex_destroy(&gpio_virtuser_config_subsys.su_mutex);
+ platform_driver_unregister(&gpio_virtuser_driver);
+ debugfs_remove_recursive(gpio_virtuser_dbg_root);
+}
+module_exit(gpio_virtuser_exit);
+
+MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
+MODULE_DESCRIPTION("Virtual GPIO consumer module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-visconti.c b/drivers/gpio/gpio-visconti.c
index 6734e7e1e2a4..ebc71ecdb6cf 100644
--- a/drivers/gpio/gpio-visconti.c
+++ b/drivers/gpio/gpio-visconti.c
@@ -8,6 +8,7 @@
* Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
*/
+#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -15,8 +16,8 @@
#include <linux/io.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/seq_file.h>
-#include <linux/bitops.h>
/* register offset */
#define GPIO_DIR 0x00
@@ -202,7 +203,7 @@ static int visconti_gpio_probe(struct platform_device *pdev)
girq = &priv->gpio_chip.irq;
gpio_irq_chip_set_chip(girq, &visconti_gpio_irq_chip);
- girq->fwnode = of_node_to_fwnode(dev->of_node);
+ girq->fwnode = dev_fwnode(dev);
girq->parent_domain = parent;
girq->child_to_parent_hwirq = visconti_gpio_child_to_parent_hwirq;
girq->populate_parent_alloc_arg = visconti_gpio_populate_parent_fwspec;
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index 7348df385198..afcf432a1573 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -333,12 +333,9 @@ static int __maybe_unused xgpio_suspend(struct device *dev)
*/
static void xgpio_remove(struct platform_device *pdev)
{
- struct xgpio_instance *gpio = platform_get_drvdata(pdev);
-
pm_runtime_get_sync(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(gpio->clk);
}
/**
@@ -644,15 +641,10 @@ static int xgpio_probe(struct platform_device *pdev)
return PTR_ERR(chip->regs);
}
- chip->clk = devm_clk_get_optional(&pdev->dev, NULL);
+ chip->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
if (IS_ERR(chip->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk), "input clock not found.\n");
- status = clk_prepare_enable(chip->clk);
- if (status < 0) {
- dev_err(&pdev->dev, "Failed to prepare clk\n");
- return status;
- }
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
@@ -699,7 +691,6 @@ skip_irq:
err_pm_put:
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
- clk_disable_unprepare(chip->clk);
return status;
}
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 466e23031afc..1a42336dfc1d 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -940,16 +940,10 @@ static int zynq_gpio_probe(struct platform_device *pdev)
chip->ngpio = gpio->p_data->ngpio;
/* Retrieve GPIO clock */
- gpio->clk = devm_clk_get(&pdev->dev, NULL);
+ gpio->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(gpio->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(gpio->clk), "input clock not found.\n");
- ret = clk_prepare_enable(gpio->clk);
- if (ret) {
- dev_err(&pdev->dev, "Unable to enable clock.\n");
- return ret;
- }
-
spin_lock_init(&gpio->dirlock);
pm_runtime_set_active(&pdev->dev);
@@ -999,7 +993,6 @@ err_pm_put:
pm_runtime_put(&pdev->dev);
err_pm_dis:
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(gpio->clk);
return ret;
}
@@ -1019,7 +1012,6 @@ static void zynq_gpio_remove(struct platform_device *pdev)
if (ret < 0)
dev_warn(&pdev->dev, "pm_runtime_get_sync() Failed\n");
gpiochip_remove(&gpio->chip);
- clk_disable_unprepare(gpio->clk);
device_set_wakeup_capable(&pdev->dev, 0);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c
index a0d69387c153..2f3c9ebfa78d 100644
--- a/drivers/gpio/gpio-zynqmp-modepin.c
+++ b/drivers/gpio/gpio-zynqmp-modepin.c
@@ -146,6 +146,7 @@ static const struct of_device_id modepin_platform_id[] = {
{ .compatible = "xlnx,zynqmp-gpio-modepin", },
{ }
};
+MODULE_DEVICE_TABLE(of, modepin_platform_id);
static struct platform_driver modepin_platform_driver = {
.driver = {
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index bb063b81cee6..78ecd56123a3 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -153,8 +153,12 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data)
* @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
* @pin: ACPI GPIO pin number (0-based, controller-relative)
*
- * Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR
- * error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO
+ * Returns:
+ * GPIO descriptor to use with Linux generic GPIO API.
+ * If the GPIO cannot be translated or there is an error an ERR_PTR is
+ * returned.
+ *
+ * Specifically returns %-EPROBE_DEFER if the referenced GPIO
* controller does not have GPIO chip registered at the moment. This is to
* support probe deferral.
*/
@@ -224,6 +228,9 @@ EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource);
* I/O resource or return False if not.
* @ares: Pointer to the ACPI resource to fetch
* @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer
+ *
+ * Returns:
+ * %true if GpioIo resource is found, %false otherwise.
*/
bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
struct acpi_resource_gpio **agpio)
@@ -876,7 +883,9 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,
* that case @index is used to select the GPIO entry in the property value
* (in case of multiple).
*
- * If the GPIO cannot be translated or there is an error, an ERR_PTR is
+ * Returns:
+ * GPIO descriptor to use with Linux generic GPIO API.
+ * If the GPIO cannot be translated or there is an error an ERR_PTR is
* returned.
*
* Note: if the GPIO resource has multiple entries in the pin list, this
@@ -924,6 +933,8 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
* resource with the relevant information from a data-only ACPI firmware node
* and uses that to obtain the GPIO descriptor to return.
*
+ * Returns:
+ * GPIO descriptor to use with Linux generic GPIO API.
* If the GPIO cannot be translated or there is an error an ERR_PTR is
* returned.
*/
@@ -973,18 +984,9 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int
struct acpi_device *adev = to_acpi_device_node(fwnode);
struct gpio_desc *desc;
char propname[32];
- int i;
/* Try first from _DSD */
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
- if (con_id) {
- snprintf(propname, sizeof(propname), "%s-%s",
- con_id, gpio_suffixes[i]);
- } else {
- snprintf(propname, sizeof(propname), "%s",
- gpio_suffixes[i]);
- }
-
+ for_each_gpio_property_name(propname, con_id) {
if (adev)
desc = acpi_get_gpiod_by_index(adev,
propname, idx, info);
@@ -1051,7 +1053,8 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
* The GPIO is considered wake capable if the GpioInt resource specifies
* SharedAndWake or ExclusiveAndWake.
*
- * Return: Linux IRQ number (> %0) on success, negative errno on failure.
+ * Returns:
+ * Linux IRQ number (> 0) on success, negative errno on failure.
*/
int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *con_id, int index,
bool *wake_capable)
@@ -1438,7 +1441,7 @@ static int acpi_find_gpio_count(struct acpi_resource *ares, void *data)
* @fwnode: firmware node of the GPIO consumer
* @con_id: function within the GPIO consumer
*
- * Return:
+ * Returns:
* The number of GPIOs associated with a firmware node / function or %-ENOENT,
* if no GPIO has been assigned to the requested function.
*/
@@ -1450,17 +1453,9 @@ int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
int count = -ENOENT;
int ret;
char propname[32];
- unsigned int i;
/* Try first from _DSD */
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
- if (con_id)
- snprintf(propname, sizeof(propname), "%s-%s",
- con_id, gpio_suffixes[i]);
- else
- snprintf(propname, sizeof(propname), "%s",
- gpio_suffixes[i]);
-
+ for_each_gpio_property_name(propname, con_id) {
ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, &obj);
if (ret == 0) {
if (obj->type == ACPI_TYPE_LOCAL_REFERENCE)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 9dad67ea2597..5aac59de0d76 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -89,6 +89,10 @@ struct linehandle_state {
GPIOHANDLE_REQUEST_OPEN_DRAIN | \
GPIOHANDLE_REQUEST_OPEN_SOURCE)
+#define GPIOHANDLE_REQUEST_DIRECTION_FLAGS \
+ (GPIOHANDLE_REQUEST_INPUT | \
+ GPIOHANDLE_REQUEST_OUTPUT)
+
static int linehandle_validate_flags(u32 flags)
{
/* Return an error if an unknown flag is set */
@@ -169,21 +173,21 @@ static long linehandle_set_config(struct linehandle_state *lh,
if (ret)
return ret;
+ /* Lines must be reconfigured explicitly as input or output. */
+ if (!(lflags & GPIOHANDLE_REQUEST_DIRECTION_FLAGS))
+ return -EINVAL;
+
for (i = 0; i < lh->num_descs; i++) {
desc = lh->descs[i];
- linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags);
+ linehandle_flags_to_desc_flags(lflags, &desc->flags);
- /*
- * Lines have to be requested explicitly for input
- * or output, else the line will be treated "as is".
- */
if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
int val = !!gcnf.default_values[i];
ret = gpiod_direction_output(desc, val);
if (ret)
return ret;
- } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
+ } else {
ret = gpiod_direction_input(desc);
if (ret)
return ret;
@@ -1128,6 +1132,14 @@ static void edge_detector_stop(struct line *line)
/* do not change line->level - see comment in debounced_value() */
}
+static int edge_detector_fifo_init(struct linereq *req)
+{
+ if (kfifo_initialized(&req->events))
+ return 0;
+
+ return kfifo_alloc(&req->events, req->event_buffer_size, GFP_KERNEL);
+}
+
static int edge_detector_setup(struct line *line,
struct gpio_v2_line_config *lc,
unsigned int line_idx, u64 edflags)
@@ -1139,9 +1151,8 @@ static int edge_detector_setup(struct line *line,
char *label;
eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
- if (eflags && !kfifo_initialized(&line->req->events)) {
- ret = kfifo_alloc(&line->req->events,
- line->req->event_buffer_size, GFP_KERNEL);
+ if (eflags) {
+ ret = edge_detector_fifo_init(line->req);
if (ret)
return ret;
}
@@ -1193,8 +1204,6 @@ static int edge_detector_update(struct line *line,
struct gpio_v2_line_config *lc,
unsigned int line_idx, u64 edflags)
{
- u64 eflags;
- int ret;
u64 active_edflags = READ_ONCE(line->edflags);
unsigned int debounce_period_us =
gpio_v2_line_config_debounce_period(lc, line_idx);
@@ -1210,14 +1219,9 @@ static int edge_detector_update(struct line *line,
* ensure event fifo is initialised if edge detection
* is now enabled.
*/
- eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
- if (eflags && !kfifo_initialized(&line->req->events)) {
- ret = kfifo_alloc(&line->req->events,
- line->req->event_buffer_size,
- GFP_KERNEL);
- if (ret)
- return ret;
- }
+ if (edflags & GPIO_V2_LINE_EDGE_FLAGS)
+ return edge_detector_fifo_init(line->req);
+
return 0;
}
@@ -1530,12 +1534,14 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
line = &lr->lines[i];
desc = lr->lines[i].desc;
flags = gpio_v2_line_config_flags(&lc, i);
- gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
- edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
/*
- * Lines have to be requested explicitly for input
- * or output, else the line will be treated "as is".
+ * Lines not explicitly reconfigured as input or output
+ * are left unchanged.
*/
+ if (!(flags & GPIO_V2_LINE_DIRECTION_FLAGS))
+ continue;
+ gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
+ edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
int val = gpio_v2_line_config_output_value(&lc, i);
@@ -1543,7 +1549,7 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
ret = gpiod_direction_output(desc, val);
if (ret)
return ret;
- } else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
+ } else {
ret = gpiod_direction_input(desc);
if (ret)
return ret;
@@ -1642,16 +1648,15 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
return ret;
}
- ret = kfifo_out(&lr->events, &le, 1);
- }
- if (ret != 1) {
- /*
- * This should never happen - we were holding the
- * lock from the moment we learned the fifo is no
- * longer empty until now.
- */
- ret = -EIO;
- break;
+ if (kfifo_out(&lr->events, &le, 1) != 1) {
+ /*
+ * This should never happen - we hold the
+ * lock from the moment we learned the fifo
+ * is no longer empty until now.
+ */
+ WARN(1, "failed to read from non-empty kfifo");
+ return -EIO;
+ }
}
if (copy_to_user(buf + bytes_read, &le, sizeof(le)))
@@ -1774,6 +1779,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
mutex_init(&lr->config_mutex);
init_waitqueue_head(&lr->wait);
+ INIT_KFIFO(lr->events);
lr->event_buffer_size = ulr.event_buffer_size;
if (lr->event_buffer_size == 0)
lr->event_buffer_size = ulr.num_lines * 16;
@@ -1994,16 +2000,15 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
return ret;
}
- ret = kfifo_out(&le->events, &ge, 1);
- }
- if (ret != 1) {
- /*
- * This should never happen - we were holding the lock
- * from the moment we learned the fifo is no longer
- * empty until now.
- */
- ret = -EIO;
- break;
+ if (kfifo_out(&le->events, &ge, 1) != 1) {
+ /*
+ * This should never happen - we hold the
+ * lock from the moment we learned the fifo
+ * is no longer empty until now.
+ */
+ WARN(1, "failed to read from non-empty kfifo");
+ return -EIO;
+ }
}
if (copy_to_user(buf + bytes_read, &ge, ge_size))
@@ -2706,12 +2711,15 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
if (count < event_size)
return -EINVAL;
#endif
- ret = kfifo_out(&cdev->events, &event, 1);
- }
- if (ret != 1) {
- ret = -EIO;
- break;
- /* We should never get here. See lineevent_read(). */
+ if (kfifo_out(&cdev->events, &event, 1) != 1) {
+ /*
+ * This should never happen - we hold the
+ * lock from the moment we learned the fifo
+ * is no longer empty until now.
+ */
+ WARN(1, "failed to read from non-empty kfifo");
+ return -EIO;
+ }
}
#ifdef CONFIG_GPIO_CDEV_V1
@@ -2740,7 +2748,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
* gpio_chrdev_open() - open the chardev for ioctl operations
* @inode: inode for this chardev
* @file: file struct for storing private data
- * Returns 0 on success
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
static int gpio_chrdev_open(struct inode *inode, struct file *file)
{
@@ -2806,7 +2816,9 @@ out_free_cdev:
* gpio_chrdev_release() - close chardev after ioctl operations
* @inode: inode for this chardev
* @file: file struct for storing private data
- * Returns 0 on success
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
static int gpio_chrdev_release(struct inode *inode, struct file *file)
{
diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c
index 4987e62dcb3d..08205f355ceb 100644
--- a/drivers/gpio/gpiolib-devres.c
+++ b/drivers/gpio/gpiolib-devres.c
@@ -6,15 +6,19 @@
* Copyright (c) 2011 John Crispin <john@phrozen.org>
*/
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
#include <linux/gfp.h>
+#include <linux/types.h>
+
+#include <linux/gpio/consumer.h>
#include "gpiolib.h"
+struct fwnode_handle;
+struct lock_class_key;
+
static void devm_gpiod_release(struct device *dev, void *res)
{
struct gpio_desc **desc = res;
@@ -52,6 +56,11 @@ static int devm_gpiod_match_array(struct device *dev, void *res, void *data)
* Managed gpiod_get(). GPIO descriptors returned from this function are
* automatically disposed on driver detach. See gpiod_get() for detailed
* information about behavior and return values.
+ *
+ * Returns:
+ * The GPIO descriptor corresponding to the function @con_id of device
+ * dev, %-ENOENT if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check devm_gpiod_get(struct device *dev,
const char *con_id,
@@ -70,6 +79,11 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get);
* Managed gpiod_get_optional(). GPIO descriptors returned from this function
* are automatically disposed on driver detach. See gpiod_get_optional() for
* detailed information about behavior and return values.
+ *
+ * Returns:
+ * The GPIO descriptor corresponding to the function @con_id of device
+ * dev, NULL if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev,
const char *con_id,
@@ -89,6 +103,11 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get_optional);
* Managed gpiod_get_index(). GPIO descriptors returned from this function are
* automatically disposed on driver detach. See gpiod_get_index() for detailed
* information about behavior and return values.
+ *
+ * Returns:
+ * The GPIO descriptor corresponding to the function @con_id of device
+ * dev, %-ENOENT if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev,
const char *con_id,
@@ -141,8 +160,10 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get_index);
* GPIO descriptors returned from this function are automatically disposed on
* driver detach.
*
- * On successful request the GPIO pin is configured in accordance with
- * provided @flags.
+ * Returns:
+ * The GPIO descriptor corresponding to the function @con_id of device
+ * dev, %-ENOENT if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev,
struct fwnode_handle *fwnode,
@@ -182,6 +203,11 @@ EXPORT_SYMBOL_GPL(devm_fwnode_gpiod_get_index);
* function are automatically disposed on driver detach. See
* gpiod_get_index_optional() for detailed information about behavior and
* return values.
+ *
+ * Returns:
+ * The GPIO descriptor corresponding to the function @con_id of device
+ * dev, %NULL if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev,
const char *con_id,
@@ -207,6 +233,12 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get_index_optional);
* Managed gpiod_get_array(). GPIO descriptors returned from this function are
* automatically disposed on driver detach. See gpiod_get_array() for detailed
* information about behavior and return values.
+ *
+ * Returns:
+ * The GPIO descriptors corresponding to the function @con_id of device
+ * dev, %-ENOENT if no GPIO has been assigned to the requested function,
+ * or another IS_ERR() code if an error occurred while trying to acquire
+ * the GPIOs.
*/
struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
const char *con_id,
@@ -243,6 +275,12 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get_array);
* function are automatically disposed on driver detach.
* See gpiod_get_array_optional() for detailed information about behavior and
* return values.
+ *
+ * Returns:
+ * The GPIO descriptors corresponding to the function @con_id of device
+ * dev, %NULL if no GPIO has been assigned to the requested function,
+ * or another IS_ERR() code if an error occurred while trying to acquire
+ * the GPIOs.
*/
struct gpio_descs *__must_check
devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
@@ -320,76 +358,6 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)
}
EXPORT_SYMBOL_GPL(devm_gpiod_put_array);
-static void devm_gpio_release(struct device *dev, void *res)
-{
- unsigned *gpio = res;
-
- gpio_free(*gpio);
-}
-
-/**
- * devm_gpio_request - request a GPIO for a managed device
- * @dev: device to request the GPIO for
- * @gpio: GPIO to allocate
- * @label: the name of the requested GPIO
- *
- * Except for the extra @dev argument, this function takes the
- * same arguments and performs the same function as
- * gpio_request(). GPIOs requested with this function will be
- * automatically freed on driver detach.
- */
-int devm_gpio_request(struct device *dev, unsigned gpio, 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) {
- devres_free(dr);
- return rc;
- }
-
- *dr = gpio;
- devres_add(dev, dr);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(devm_gpio_request);
-
-/**
- * devm_gpio_request_one - request a single GPIO with initial setup
- * @dev: device to request for
- * @gpio: the GPIO number
- * @flags: GPIO configuration as specified by GPIOF_*
- * @label: a literal description string of this GPIO
- */
-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_one(gpio, flags, label);
- if (rc) {
- devres_free(dr);
- return rc;
- }
-
- *dr = gpio;
- devres_add(dev, dr);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(devm_gpio_request_one);
-
static void devm_gpio_chip_release(void *data)
{
struct gpio_chip *gc = data;
diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c
index 5a9911ae9125..28f1046fb670 100644
--- a/drivers/gpio/gpiolib-legacy.c
+++ b/drivers/gpio/gpiolib-legacy.c
@@ -1,4 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
@@ -22,6 +28,9 @@ EXPORT_SYMBOL_GPL(gpio_free);
* @label: a literal description string of this GPIO
*
* **DEPRECATED** This function is deprecated and must not be used in new code.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
{
@@ -40,11 +49,10 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
if (flags & GPIOF_ACTIVE_LOW)
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
- if (flags & GPIOF_DIR_IN)
+ if (flags & GPIOF_IN)
err = gpiod_direction_input(desc);
else
- err = gpiod_direction_output_raw(desc,
- (flags & GPIOF_INIT_HIGH) ? 1 : 0);
+ err = gpiod_direction_output_raw(desc, !!(flags & GPIOF_OUT_INIT_HIGH));
if (err)
goto free_gpio;
@@ -72,3 +80,83 @@ int gpio_request(unsigned gpio, const char *label)
return gpiod_request(desc, label);
}
EXPORT_SYMBOL_GPL(gpio_request);
+
+static void devm_gpio_release(struct device *dev, void *res)
+{
+ unsigned *gpio = res;
+
+ gpio_free(*gpio);
+}
+
+/**
+ * devm_gpio_request - request a GPIO for a managed device
+ * @dev: device to request the GPIO for
+ * @gpio: GPIO to allocate
+ * @label: the name of the requested GPIO
+ *
+ * Except for the extra @dev argument, this function takes the
+ * same arguments and performs the same function as gpio_request().
+ * GPIOs requested with this function will be automatically freed
+ * on driver detach.
+ *
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
+ */
+int devm_gpio_request(struct device *dev, unsigned gpio, 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) {
+ devres_free(dr);
+ return rc;
+ }
+
+ *dr = gpio;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_gpio_request);
+
+/**
+ * devm_gpio_request_one - request a single GPIO with initial setup
+ * @dev: device to request for
+ * @gpio: the GPIO number
+ * @flags: GPIO configuration as specified by GPIOF_*
+ * @label: a literal description string of this GPIO
+ *
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
+ */
+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_one(gpio, flags, label);
+ if (rc) {
+ devres_free(dr);
+ return rc;
+ }
+
+ *dr = gpio;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_gpio_request_one);
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index d75f6ee37028..880f1efcaca5 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -46,16 +46,19 @@ enum of_gpio_flags {
* @propname: property name containing gpio specifier(s)
*
* The function returns the count of GPIOs specified for a node.
- * Note that the empty GPIO specifiers count too. Returns either
- * Number of gpios defined in property,
- * -EINVAL for an incorrectly formed gpios property, or
- * -ENOENT for a missing gpios property
+ * NOTE: The empty GPIO specifiers count too.
*
- * Example:
- * gpios = <0
- * &gpio1 1 2
- * 0
- * &gpio2 3 4>;
+ * Returns:
+ * Either number of GPIOs defined in the property, or
+ * * %-EINVAL for an incorrectly formed "gpios" property, or
+ * * %-ENOENT for a missing "gpios" property.
+ *
+ * Example::
+ *
+ * gpios = <0
+ * &gpio1 1 2
+ * 0
+ * &gpio2 3 4>;
*
* The above example defines four GPIOs, two of which are not specified.
* This function will return '4'
@@ -77,6 +80,11 @@ static int of_gpio_named_count(const struct device_node *np,
* "gpios" for the chip select lines. If we detect this, we redirect
* the counting of "cs-gpios" to count "gpios" transparent to the
* driver.
+ *
+ * Returns:
+ * Either number of GPIOs defined in the property, or
+ * * %-EINVAL for an incorrectly formed "gpios" property, or
+ * * %-ENOENT for a missing "gpios" property.
*/
static int of_gpio_spi_cs_get_count(const struct device_node *np,
const char *con_id)
@@ -97,20 +105,12 @@ int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
const struct device_node *np = to_of_node(fwnode);
int ret;
char propname[32];
- unsigned int i;
ret = of_gpio_spi_cs_get_count(np, con_id);
if (ret > 0)
return ret;
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
- if (con_id)
- snprintf(propname, sizeof(propname), "%s-%s",
- con_id, gpio_suffixes[i]);
- else
- snprintf(propname, sizeof(propname), "%s",
- gpio_suffixes[i]);
-
+ for_each_gpio_property_name(propname, con_id) {
ret = of_gpio_named_count(np, propname);
if (ret > 0)
break;
@@ -203,6 +203,24 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np,
*/
{ "qi,lb60", "rb-gpios", true },
#endif
+#if IS_ENABLED(CONFIG_PCI_LANTIQ)
+ /*
+ * According to the PCI specification, the RST# pin is an
+ * active-low signal. However, most of the device trees that
+ * have been widely used for a long time incorrectly describe
+ * reset GPIO as active-high, and were also using wrong name
+ * for the property.
+ */
+ { "lantiq,pci-xway", "gpio-reset", false },
+#endif
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2005)
+ /*
+ * DTS for Nokia N900 incorrectly specified "active high"
+ * polarity for the reset line, while the chip actually
+ * treats it as "active low".
+ */
+ { "ti,tsc2005", "reset-gpios", false },
+#endif
};
unsigned int i;
@@ -320,11 +338,10 @@ static void of_gpio_flags_quirks(const struct device_node *np,
*/
if (IS_ENABLED(CONFIG_SPI_MASTER) && !strcmp(propname, "cs-gpios") &&
of_property_read_bool(np, "cs-gpios")) {
- struct device_node *child;
u32 cs;
int ret;
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
ret = of_property_read_u32(child, "reg", &cs);
if (ret)
continue;
@@ -345,7 +362,6 @@ static void of_gpio_flags_quirks(const struct device_node *np,
"spi-cs-high");
of_gpio_quirk_polarity(child, active_high,
flags);
- of_node_put(child);
break;
}
}
@@ -365,7 +381,8 @@ static void of_gpio_flags_quirks(const struct device_node *np,
* @index: index of the GPIO
* @flags: a flags pointer to fill in
*
- * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno
+ * Returns:
+ * GPIO descriptor to use with Linux GPIO API, or one of the errno
* value on the error condition. If @flags is not NULL the function also fills
* in flags for the GPIO.
*/
@@ -417,7 +434,8 @@ out:
*
* **DEPRECATED** This function is deprecated and must not be used in new code.
*
- * Returns GPIO number to use with Linux generic GPIO API, or one of the errno
+ * Returns:
+ * GPIO number to use with Linux generic GPIO API, or one of the errno
* value on the error condition.
*/
int of_get_named_gpio(const struct device_node *np, const char *propname,
@@ -504,9 +522,9 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np,
{ "reset", "reset-n-io", "marvell,nfc-uart" },
{ "reset", "reset-n-io", "mrvl,nfc-uart" },
#endif
-#if !IS_ENABLED(CONFIG_PCI_LANTIQ)
+#if IS_ENABLED(CONFIG_PCI_LANTIQ)
/* MIPS Lantiq PCI */
- { "reset", "gpios-reset", "lantiq,pci-xway" },
+ { "reset", "gpio-reset", "lantiq,pci-xway" },
#endif
/*
@@ -669,23 +687,14 @@ static const of_find_gpio_quirk of_find_gpio_quirks[] = {
struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
unsigned int idx, unsigned long *flags)
{
- char prop_name[32]; /* 32 is max size of property name */
+ char propname[32]; /* 32 is max size of property name */
enum of_gpio_flags of_flags;
const of_find_gpio_quirk *q;
struct gpio_desc *desc;
- unsigned int i;
/* Try GPIO property "foo-gpios" and "foo-gpio" */
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
- if (con_id)
- snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id,
- gpio_suffixes[i]);
- else
- snprintf(prop_name, sizeof(prop_name), "%s",
- gpio_suffixes[i]);
-
- desc = of_get_named_gpiod_flags(np, prop_name, idx, &of_flags);
-
+ for_each_gpio_property_name(propname, con_id) {
+ desc = of_get_named_gpiod_flags(np, propname, idx, &of_flags);
if (!gpiod_not_found(desc))
break;
}
@@ -712,7 +721,8 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
* of_find_gpio() or of_parse_own_gpio()
* @dflags: gpiod_flags - optional GPIO initialization flags
*
- * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno
+ * Returns:
+ * GPIO descriptor to use with Linux GPIO API, or one of the errno
* value on the error condition.
*/
static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
@@ -780,7 +790,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
* @chip: gpio chip to act on
* @hog: device node describing the hogs
*
- * Returns error if it fails otherwise 0 on success.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
{
@@ -814,22 +825,21 @@ static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
*
* This is only used by of_gpiochip_add to request/set GPIO initial
* configuration.
- * It returns error if it fails otherwise 0 on success.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
{
- struct device_node *np;
int ret;
- for_each_available_child_of_node(dev_of_node(&chip->gpiodev->dev), np) {
+ for_each_available_child_of_node_scoped(dev_of_node(&chip->gpiodev->dev), np) {
if (!of_property_read_bool(np, "gpio-hog"))
continue;
ret = of_gpiochip_add_hog(chip, np);
- if (ret < 0) {
- of_node_put(np);
+ if (ret < 0)
return ret;
- }
of_node_set_flag(np, OF_POPULATED);
}
@@ -927,6 +937,9 @@ struct notifier_block gpio_of_notifier = {
* This is simple translation function, suitable for the most 1:1 mapped
* GPIO chips. This function performs only one sanity check: whether GPIO
* is less than ngpios (that is specified in the gpio_chip).
+ *
+ * Returns:
+ * GPIO number (>= 0) on success, negative errno on failure.
*/
static int of_gpio_simple_xlate(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec,
@@ -976,6 +989,9 @@ static int of_gpio_simple_xlate(struct gpio_chip *gc,
* 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,
@@ -1040,13 +1056,13 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
int index = 0, ret, trim;
const char *name;
static const char group_names_propname[] = "gpio-ranges-group-names";
- struct property *group_names;
+ bool has_group_names;
np = dev_of_node(&chip->gpiodev->dev);
if (!np)
return 0;
- group_names = of_find_property(np, group_names_propname, NULL);
+ has_group_names = of_property_present(np, group_names_propname);
for (;; index++) {
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,
@@ -1067,7 +1083,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (pinspec.args[2]) {
/* npins != 0: linear range */
- if (group_names) {
+ if (has_group_names) {
of_property_read_string_index(np,
group_names_propname,
index, &name);
@@ -1105,7 +1121,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
break;
}
- if (!group_names) {
+ if (!has_group_names) {
pr_err("%pOF: GPIO group range requested but no %s property.\n",
np, group_names_propname);
break;
diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c
index cec1ab878af8..2b2dd7e92211 100644
--- a/drivers/gpio/gpiolib-swnode.c
+++ b/drivers/gpio/gpiolib-swnode.c
@@ -24,20 +24,6 @@
#define GPIOLIB_SWNODE_UNDEFINED_NAME "swnode-gpio-undefined"
-static void swnode_format_propname(const char *con_id, char *propname,
- size_t max_size)
-{
- /*
- * Note we do not need to try both -gpios and -gpio suffixes,
- * as, unlike OF and ACPI, we can fix software nodes to conform
- * to the proper binding.
- */
- if (con_id)
- snprintf(propname, max_size, "%s-gpios", con_id);
- else
- strscpy(propname, "gpios", max_size);
-}
-
static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
{
const struct software_node *gdev_node;
@@ -59,6 +45,17 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
return gdev ?: ERR_PTR(-EPROBE_DEFER);
}
+static int swnode_gpio_get_reference(const struct fwnode_handle *fwnode,
+ const char *propname, unsigned int idx,
+ struct fwnode_reference_args *args)
+{
+ /*
+ * We expect all swnode-described GPIOs have GPIO number and
+ * polarity arguments, hence nargs is set to 2.
+ */
+ return fwnode_property_get_reference_args(fwnode, propname, NULL, 2, idx, args);
+}
+
struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
const char *con_id, unsigned int idx,
unsigned long *flags)
@@ -67,23 +64,21 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
struct fwnode_reference_args args;
struct gpio_desc *desc;
char propname[32]; /* 32 is max size of property name */
- int error;
+ int ret;
swnode = to_software_node(fwnode);
if (!swnode)
return ERR_PTR(-EINVAL);
- swnode_format_propname(con_id, propname, sizeof(propname));
-
- /*
- * We expect all swnode-described GPIOs have GPIO number and
- * polarity arguments, hence nargs is set to 2.
- */
- error = fwnode_property_get_reference_args(fwnode, propname, NULL, 2, idx, &args);
- if (error) {
+ for_each_gpio_property_name(propname, con_id) {
+ ret = swnode_gpio_get_reference(fwnode, propname, idx, &args);
+ if (ret == 0)
+ break;
+ }
+ if (ret) {
pr_debug("%s: can't parse '%s' property of node '%pfwP[%d]'\n",
__func__, propname, fwnode, idx);
- return ERR_PTR(error);
+ return ERR_PTR(ret);
}
struct gpio_device *gdev __free(gpio_device_put) =
@@ -111,7 +106,7 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
* system-global GPIOs
* @con_id: function within the GPIO consumer
*
- * Return:
+ * Returns:
* The number of GPIOs associated with a device / function or %-ENOENT,
* if no GPIO has been assigned to the requested function.
*/
@@ -121,20 +116,21 @@ int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
char propname[32];
int count;
- swnode_format_propname(con_id, propname, sizeof(propname));
-
/*
* This is not very efficient, but GPIO lists usually have only
* 1 or 2 entries.
*/
- count = 0;
- while (fwnode_property_get_reference_args(fwnode, propname, NULL, 0,
- count, &args) == 0) {
- fwnode_handle_put(args.fwnode);
- count++;
+ for_each_gpio_property_name(propname, con_id) {
+ count = 0;
+ while (swnode_gpio_get_reference(fwnode, propname, count, &args) == 0) {
+ fwnode_handle_put(args.fwnode);
+ count++;
+ }
+ if (count)
+ return count;
}
- return count ?: -ENOENT;
+ return -ENOENT;
}
#if IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 26202586fd39..17ed229412af 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -568,7 +568,8 @@ static struct class gpio_class = {
* will see "direction" sysfs attribute which may be used to change
* the gpio's direction. A "value" attribute will always be provided.
*
- * Returns zero on success, else an error.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
@@ -667,7 +668,8 @@ static int match_export(struct device *dev, const void *desc)
* Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN
* node. Caller is responsible for unlinking.
*
- * Returns zero on success, else an error.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_export_link(struct device *dev, const char *name,
struct gpio_desc *desc)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index fa62367ee929..c6afbf434366 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/acpi.h>
+#include <linux/array_size.h>
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/compat.h>
@@ -17,6 +18,7 @@
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/module.h>
+#include <linux/nospec.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/seq_file.h>
@@ -53,7 +55,7 @@ static DEFINE_IDA(gpio_ida);
static dev_t gpio_devt;
#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
-static int gpio_bus_match(struct device *dev, struct device_driver *drv)
+static int gpio_bus_match(struct device *dev, const struct device_driver *drv)
{
struct fwnode_handle *fwnode = dev_fwnode(dev);
@@ -88,6 +90,8 @@ DEFINE_STATIC_SRCU(gpio_devices_srcu);
static DEFINE_MUTEX(gpio_machine_hogs_mutex);
static LIST_HEAD(gpio_machine_hogs);
+const char *const gpio_suffixes[] = { "gpios", "gpio", NULL };
+
static void gpiochip_free_hogs(struct gpio_chip *gc);
static int gpiochip_add_irqchip(struct gpio_chip *gc,
struct lock_class_key *lock_key,
@@ -105,16 +109,16 @@ const char *gpiod_get_label(struct gpio_desc *desc)
unsigned long flags;
flags = READ_ONCE(desc->flags);
- if (test_bit(FLAG_USED_AS_IRQ, &flags) &&
- !test_bit(FLAG_REQUESTED, &flags))
- return "interrupt";
-
- if (!test_bit(FLAG_REQUESTED, &flags))
- return NULL;
label = srcu_dereference_check(desc->label, &desc->gdev->desc_srcu,
srcu_read_lock_held(&desc->gdev->desc_srcu));
+ if (test_bit(FLAG_USED_AS_IRQ, &flags))
+ return label->str ?: "interrupt";
+
+ if (!test_bit(FLAG_REQUESTED, &flags))
+ return NULL;
+
return label->str;
}
@@ -174,7 +178,6 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc,
{
return gpio_device_get_desc(gc->gpiodev, hwnum);
}
-EXPORT_SYMBOL_GPL(gpiochip_get_desc);
/**
* gpio_device_get_desc() - get the GPIO descriptor corresponding to the given
@@ -198,7 +201,7 @@ gpio_device_get_desc(struct gpio_device *gdev, unsigned int hwnum)
if (hwnum >= gdev->ngpio)
return ERR_PTR(-EINVAL);
- return &gdev->descs[hwnum];
+ return &gdev->descs[array_index_nospec(hwnum, gdev->ngpio)];
}
EXPORT_SYMBOL_GPL(gpio_device_get_desc);
@@ -227,6 +230,9 @@ EXPORT_SYMBOL_GPL(desc_to_gpio);
* This function is unsafe and should not be used. Using the chip address
* without taking the SRCU read lock may result in dereferencing a dangling
* pointer.
+ *
+ * Returns:
+ * Address of the GPIO chip backing this device.
*/
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
{
@@ -339,7 +345,8 @@ static int gpiochip_find_base_unlocked(u16 ngpio)
* gpiod_get_direction - return the current direction of a GPIO
* @desc: GPIO to get the direction of
*
- * Returns 0 for output, 1 for input, or an error code in case of error.
+ * Returns:
+ * 0 for output, 1 for input, or an error code in case of error.
*
* This function may sleep if gpiod_cansleep() is true.
*/
@@ -353,7 +360,7 @@ int gpiod_get_direction(struct gpio_desc *desc)
* We cannot use VALIDATE_DESC() as we must not return 0 for a NULL
* descriptor like we usually do.
*/
- if (!desc || IS_ERR(desc))
+ if (IS_ERR_OR_NULL(desc))
return -EINVAL;
CLASS(gpio_chip_guard, guard)(desc);
@@ -396,8 +403,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
* Add a new chip to the global chips list, keeping the list of chips sorted
* by range(means [base, base + ngpio - 1]) order.
*
- * Return -EBUSY if the new chip overlaps with some other chip's integer
- * space.
+ * Returns:
+ * -EBUSY if the new chip overlaps with some other chip's integer space.
*/
static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
{
@@ -485,7 +492,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
* 1. Non-unique names are still accepted,
* 2. Name collisions within the same GPIO chip are not reported.
*/
-static int gpiochip_set_desc_names(struct gpio_chip *gc)
+static void gpiochip_set_desc_names(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
int i;
@@ -504,8 +511,6 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
/* Then add all names to the GPIO descriptors */
for (i = 0; i != gc->ngpio; ++i)
gdev->descs[i].name = gc->names[i];
-
- return 0;
}
/*
@@ -999,11 +1004,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
- if (gc->names) {
- ret = gpiochip_set_desc_names(gc);
- if (ret)
- goto err_cleanup_desc_srcu;
- }
+ if (gc->names)
+ gpiochip_set_desc_names(gc);
+
ret = gpiochip_set_names(gc);
if (ret)
goto err_cleanup_desc_srcu;
@@ -1517,6 +1520,9 @@ static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc,
* This function is a wrapper that calls gpiochip_lock_as_irq() and is to be
* used as the activate function for the &struct irq_domain_ops. The host_data
* for the IRQ domain must be the &struct gpio_chip.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
static int gpiochip_irq_domain_activate(struct irq_domain *domain,
struct irq_data *data, bool reserve)
@@ -1661,6 +1667,9 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
* This function will set up the mapping for a certain IRQ line on a
* gpiochip by assigning the gpiochip as chip data, and using the irqchip
* stored inside the gpiochip.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
@@ -1895,6 +1904,9 @@ static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc,
* @gc: the GPIO chip to add the IRQ chip to
* @lock_key: lockdep class for IRQ lock
* @request_key: lockdep class for IRQ request
+ *
+ * Returns:
+ * 0 on success, or a negative errno on failure.
*/
static int gpiochip_add_irqchip(struct gpio_chip *gc,
struct lock_class_key *lock_key,
@@ -2030,6 +2042,9 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc)
* @domain: the irqdomain to add to the gpiochip
*
* This function adds an IRQ domain to the gpiochip.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
struct irq_domain *domain)
@@ -2066,6 +2081,9 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc)
* gpiochip_generic_request() - request the gpio function for a pin
* @gc: the gpiochip owning the GPIO
* @offset: the offset of the GPIO to request for GPIO function
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset)
{
@@ -2099,6 +2117,9 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free);
* @gc: the gpiochip owning the GPIO
* @offset: the offset of the GPIO to apply the configuration
* @config: the configuration to be applied
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset,
unsigned long config)
@@ -2125,6 +2146,9 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_config);
* pinctrl driver is DEPRECATED. Please see Section 2.1 of
* Documentation/devicetree/bindings/gpio/gpio.txt on how to
* bind pinctrl and gpio drivers via the "gpio-ranges" property.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiochip_add_pingroup_range(struct gpio_chip *gc,
struct pinctrl_dev *pctldev,
@@ -2176,13 +2200,13 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
* @npins: the number of pins from the offset of each pin space (GPIO and
* pin controller) to accumulate in this range
*
- * Returns:
- * 0 on success, or a negative error-code on failure.
- *
* Calling this function directly from a DeviceTree-supported
* pinctrl driver is DEPRECATED. Please see Section 2.1 of
* Documentation/devicetree/bindings/gpio/gpio.txt on how to
* bind pinctrl and gpio drivers via the "gpio-ranges" property.
+ *
+ * Returns:
+ * 0 on success, or a negative errno on failure.
*/
int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
@@ -2586,7 +2610,8 @@ static int gpio_set_bias(struct gpio_desc *desc)
* The function calls the certain GPIO driver to set debounce timeout
* in the hardware.
*
- * Returns 0 on success, or negative error code otherwise.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
{
@@ -2602,7 +2627,8 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
* Set the direction of the passed GPIO to input, such as gpiod_get_value() can
* be called safely on it.
*
- * Return 0 in case of success, else an error code.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_direction_input(struct gpio_desc *desc)
{
@@ -2709,7 +2735,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
* be called safely on it. The initial value of the output must be specified
* as raw value on the physical line without regard for the ACTIVE_LOW status.
*
- * Return 0 in case of success, else an error code.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
@@ -2728,7 +2755,8 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
* as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
* account.
*
- * Return 0 in case of success, else an error code.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
@@ -2801,7 +2829,8 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output);
* @desc: GPIO to enable.
* @flags: Flags related to GPIO edge.
*
- * Return 0 in case of success, else negative error code.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
@@ -2833,7 +2862,8 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);
* @desc: GPIO to disable.
* @flags: Flags related to GPIO edge, same value as used during enable call.
*
- * Return 0 in case of success, else negative error code.
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
@@ -2925,7 +2955,8 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
* gpiod_is_active_low - test whether a GPIO is active-low or not
* @desc: the gpio descriptor to test
*
- * Returns 1 if the GPIO is active-low, 0 otherwise.
+ * Returns:
+ * 1 if the GPIO is active-low, 0 otherwise.
*/
int gpiod_is_active_low(const struct gpio_desc *desc)
{
@@ -3140,7 +3171,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
* gpiod_get_raw_value() - return a gpio's raw value
* @desc: gpio whose value will be returned
*
- * Return the GPIO's raw value, i.e. the value of the physical line disregarding
+ * Returns:
+ * The GPIO's raw value, i.e. the value of the physical line disregarding
* its ACTIVE_LOW status, or negative errno on failure.
*
* This function can be called from contexts where we cannot sleep, and will
@@ -3159,7 +3191,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
* gpiod_get_value() - return a gpio's value
* @desc: gpio whose value will be returned
*
- * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into
+ * Returns:
+ * The GPIO's logical value, i.e. taking the ACTIVE_LOW status into
* account, or negative errno on failure.
*
* This function can be called from contexts where we cannot sleep, and will
@@ -3192,11 +3225,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
- * without regard for their ACTIVE_LOW status. Return 0 in case of success,
- * else an error code.
+ * without regard for their ACTIVE_LOW status.
*
* This function can be called from contexts where we cannot sleep,
* and it will complain if the GPIO chip functions potentially sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -3219,10 +3254,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
- * into account. Return 0 in case of success, else an error code.
+ * into account.
*
* This function can be called from contexts where we cannot sleep,
* and it will complain if the GPIO chip functions potentially sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -3510,6 +3548,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
*
* This function can be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -3535,6 +3576,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
*
* This function can be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -3553,6 +3597,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value);
* gpiod_cansleep() - report whether gpio value access may sleep
* @desc: gpio to check
*
+ * Returns:
+ * 0 for non-sleepable, 1 for sleepable, or an error code in case of error.
*/
int gpiod_cansleep(const struct gpio_desc *desc)
{
@@ -3565,6 +3611,9 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
* gpiod_set_consumer_name() - set the consumer name for the descriptor
* @desc: gpio to set the consumer name on
* @name: the new consumer name
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
{
@@ -3578,8 +3627,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
* gpiod_to_irq() - return the IRQ corresponding to a GPIO
* @desc: gpio whose IRQ will be returned (already requested)
*
- * Return the IRQ corresponding to the passed GPIO, or an error code in case of
- * error.
+ * Returns:
+ * The IRQ corresponding to the passed GPIO, or an error code in case of error.
*/
int gpiod_to_irq(const struct gpio_desc *desc)
{
@@ -3592,7 +3641,7 @@ int gpiod_to_irq(const struct gpio_desc *desc)
* requires this function to not return zero on an invalid descriptor
* but rather a negative error number.
*/
- if (!desc || IS_ERR(desc))
+ if (IS_ERR_OR_NULL(desc))
return -EINVAL;
gdev = desc->gdev;
@@ -3633,6 +3682,9 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq);
*
* This is used directly by GPIO drivers that want to lock down
* a certain GPIO line to be used for IRQs.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
{
@@ -3784,7 +3836,8 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent);
* gpiod_get_raw_value_cansleep() - return a gpio's raw value
* @desc: gpio whose value will be returned
*
- * Return the GPIO's raw value, i.e. the value of the physical line disregarding
+ * Returns:
+ * The GPIO's raw value, i.e. the value of the physical line disregarding
* its ACTIVE_LOW status, or negative errno on failure.
*
* This function is to be called from contexts that can sleep.
@@ -3801,7 +3854,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep);
* gpiod_get_value_cansleep() - return a gpio's value
* @desc: gpio whose value will be returned
*
- * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into
+ * Returns:
+ * The GPIO's logical value, i.e. taking the ACTIVE_LOW status into
* account, or negative errno on failure.
*
* This function is to be called from contexts that can sleep.
@@ -3831,10 +3885,12 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
* @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
- * without regard for their ACTIVE_LOW status. Return 0 in case of success,
- * else an error code.
+ * without regard for their ACTIVE_LOW status.
*
* This function is to be called from contexts that can sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -3858,9 +3914,12 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
* @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
- * into account. Return 0 in case of success, else an error code.
+ * into account.
*
* This function is to be called from contexts that can sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -3923,6 +3982,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
* without regard for their ACTIVE_LOW status.
*
* This function is to be called from contexts that can sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -3965,6 +4027,9 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
* into account.
*
* This function is to be called from contexts that can sleep.
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
@@ -4298,9 +4363,12 @@ EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index);
/**
* gpiod_count - return the number of GPIOs associated with a device / function
- * or -ENOENT if no GPIO has been assigned to the requested function
* @dev: GPIO consumer, can be NULL for system-global GPIOs
* @con_id: function within the GPIO consumer
+ *
+ * Returns:
+ * The number of GPIOs associated with a device / function or -ENOENT if no
+ * GPIO has been assigned to the requested function.
*/
int gpiod_count(struct device *dev, const char *con_id)
{
@@ -4327,7 +4395,8 @@ EXPORT_SYMBOL_GPL(gpiod_count);
* @con_id: function within the GPIO consumer
* @flags: optional GPIO initialization flags
*
- * Return the GPIO descriptor corresponding to the function con_id of device
+ * Returns:
+ * The GPIO descriptor corresponding to the function @con_id of device
* dev, -ENOENT if no GPIO has been assigned to the requested function, or
* another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
@@ -4347,6 +4416,11 @@ EXPORT_SYMBOL_GPL(gpiod_get);
* This is equivalent to gpiod_get(), except that when no GPIO was assigned to
* the requested function it will return NULL. This is convenient for drivers
* that need to handle optional GPIOs.
+ *
+ * Returns:
+ * The GPIO descriptor corresponding to the function @con_id of device
+ * dev, NULL if no GPIO has been assigned to the requested function, or
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check gpiod_get_optional(struct device *dev,
const char *con_id,
@@ -4365,7 +4439,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional);
* of_find_gpio() or of_get_gpio_hog()
* @dflags: gpiod_flags - optional GPIO initialization flags
*
- * Return 0 on success, -ENOENT if no GPIO has been assigned to the
+ * Returns:
+ * 0 on success, -ENOENT if no GPIO has been assigned to the
* requested function and/or index, or another IS_ERR() code if an error
* occurred while trying to acquire the GPIO.
*/
@@ -4440,7 +4515,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
* This variant of gpiod_get() allows to access GPIOs other than the first
* defined one for functions that define several GPIOs.
*
- * Return a valid GPIO descriptor, -ENOENT if no GPIO has been assigned to the
+ * Returns:
+ * A valid GPIO descriptor, -ENOENT if no GPIO has been assigned to the
* requested function and/or index, or another IS_ERR() code if an error
* occurred while trying to acquire the GPIO.
*/
@@ -4468,6 +4544,11 @@ EXPORT_SYMBOL_GPL(gpiod_get_index);
* This is equivalent to gpiod_get_index(), except that when no GPIO with the
* specified index was assigned to the requested function it will return NULL.
* This is convenient for drivers that need to handle optional GPIOs.
+ *
+ * Returns:
+ * A valid GPIO descriptor, NULL if no GPIO has been assigned to the
+ * requested function and/or index, or another IS_ERR() code if an error
+ * occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev,
const char *con_id,
@@ -4491,6 +4572,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_index_optional);
* @lflags: bitmask of gpio_lookup_flags GPIO_* values - returned from
* of_find_gpio() or of_get_gpio_hog()
* @dflags: gpiod_flags - optional GPIO initialization flags
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
*/
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags)
@@ -4547,9 +4631,11 @@ static void gpiochip_free_hogs(struct gpio_chip *gc)
*
* This function acquires all the GPIOs defined under a given function.
*
- * Return a struct gpio_descs containing an array of descriptors, -ENOENT if
- * no GPIO has been assigned to the requested function, or another IS_ERR()
- * code if an error occurred while trying to acquire the GPIOs.
+ * Returns:
+ * The GPIO descriptors corresponding to the function @con_id of device
+ * dev, -ENOENT if no GPIO has been assigned to the requested function,
+ * or another IS_ERR() code if an error occurred while trying to acquire
+ * the GPIOs.
*/
struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
const char *con_id,
@@ -4675,6 +4761,12 @@ EXPORT_SYMBOL_GPL(gpiod_get_array);
*
* This is equivalent to gpiod_get_array(), except that when no GPIO was
* assigned to the requested function it will return NULL.
+ *
+ * Returns:
+ * The GPIO descriptors corresponding to the function @con_id of device
+ * dev, NULL if no GPIO has been assigned to the requested function,
+ * or another IS_ERR() code if an error occurred while trying to acquire
+ * the GPIOs.
*/
struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
const char *con_id,
@@ -4798,11 +4890,11 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
for_each_gpio_desc(gc, desc) {
guard(srcu)(&desc->gdev->desc_srcu);
- if (test_bit(FLAG_REQUESTED, &desc->flags)) {
+ is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags);
+ if (is_irq || test_bit(FLAG_REQUESTED, &desc->flags)) {
gpiod_get_direction(desc);
is_out = test_bit(FLAG_IS_OUT, &desc->flags);
value = gpio_chip_get_value(gc, desc);
- is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags);
active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags);
seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n",
gpio, desc->name ?: "", gpiod_get_label(desc),
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 48e086c2f416..067197d61d57 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -89,8 +89,21 @@ static inline struct gpio_device *to_gpio_device(struct device *dev)
return container_of(dev, struct gpio_device, dev);
}
-/* gpio suffixes used for ACPI and device tree lookup */
-static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
+/* GPIO suffixes used for ACPI and device tree lookup */
+extern const char *const gpio_suffixes[];
+
+#define for_each_gpio_property_name(propname, con_id) \
+ for (const char * const *__suffixes = gpio_suffixes; \
+ *__suffixes && ({ \
+ const char *__gs = *__suffixes; \
+ \
+ if (con_id) \
+ snprintf(propname, sizeof(propname), "%s-%s", con_id, __gs); \
+ else \
+ snprintf(propname, sizeof(propname), "%s", __gs); \
+ 1; \
+ }); \
+ __suffixes++)
/**
* struct gpio_array - Opaque descriptor for a structure of GPIO array attributes
@@ -242,6 +255,7 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags);
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);
/*