diff options
Diffstat (limited to 'drivers/gpio')
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); /* |