diff options
Diffstat (limited to 'drivers/gpio')
112 files changed, 8352 insertions, 2813 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1301cec94f12..98b4d1633b25 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -70,8 +70,7 @@ config GPIO_SYSFS ioctl() operations instead. config GPIO_CDEV - bool - prompt "Character device (/dev/gpiochipN) support" if EXPERT + bool "Character device (/dev/gpiochipN) support" if EXPERT default y help Say Y here to add the character device /dev/gpiochipN interface @@ -103,6 +102,15 @@ config GPIO_REGMAP select REGMAP tristate +config GPIO_SWNODE_UNDEFINED + bool + help + This adds a special place holder for software nodes to contain an + undefined GPIO reference, this is primarily used by SPI to allow a + list of GPIO chip selects to mark a certain chip select as being + controlled the SPI device's internal chip select mechanism and not + a GPIO. + # put drivers in the right section, in alphabetical order # This symbol is selected by both I2C and SPI expanders @@ -140,9 +148,7 @@ config GPIO_74XX_MMIO config GPIO_ALTERA tristate "Altera GPIO" - depends on OF_GPIO select GPIOLIB_IRQCHIP - select OF_GPIO_MM_GPIOCHIP help Say Y or M here to build support for the Altera PIO device. @@ -210,7 +216,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 @@ -234,7 +240,7 @@ config GPIO_CLPS711X config GPIO_DAVINCI tristate "TI Davinci/Keystone GPIO support" default y if ARCH_DAVINCI - depends on (ARM || ARM64) && (ARCH_DAVINCI || ARCH_KEYSTONE || ARCH_K3) + depends on ((ARM || ARM64) && (ARCH_DAVINCI || ARCH_KEYSTONE || ARCH_K3)) || COMPILE_TEST help Say yes here to enable GPIO support for TI Davinci/Keystone SoCs. @@ -312,9 +318,27 @@ config GPIO_GENERIC_PLATFORM help Say yes here to support basic platform_device memory-mapped GPIO controllers. +config GPIO_GRANITERAPIDS + tristate "Intel Granite Rapids-D vGPIO support" + depends on X86 || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + Select this to enable virtual GPIO support on platforms with the + following SoCs: + + - Intel Granite Rapids-D + + The driver enables basic GPIO functionality and implements interrupt + support. The virtual GPIO driver controls GPIO lines via a firmware + interface. The physical GPIO pins reside on device that is external + from the main SoC package, such as a BMC or a CPLD. + + To compile this driver as a module, choose M here: the module will + be called gpio-graniterapids. + config GPIO_GRGPIO tristate "Aeroflex Gaisler GRGPIO support" - depends on OF_GPIO + depends on OF || COMPILE_TEST select GPIO_GENERIC select IRQ_DOMAIN help @@ -459,9 +483,9 @@ config GPIO_MT7621 Say yes here to support the Mediatek MT7621 SoC GPIO device. config GPIO_MVEBU - def_bool y - depends on PLAT_ORION || ARCH_MVEBU - depends on OF_GPIO + bool "Marvell Orion and EBU GPIO support" if COMPILE_TEST + depends on PLAT_ORION || ARCH_MVEBU || COMPILE_TEST + default PLAT_ORION || ARCH_MVEBU select GENERIC_IRQ_CHIP select REGMAP_MMIO @@ -478,6 +502,18 @@ config GPIO_MXS select GPIO_GENERIC select GENERIC_IRQ_CHIP +config GPIO_NOMADIK + bool "Nomadik GPIO driver" + depends on ARCH_U8500 || ARCH_NOMADIK || MACH_EYEQ5 || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + Say yes here to support the Nomadik SoC GPIO block. This block is also + used by the Mobileye EyeQ5 SoC. + + It handles up to 32 GPIOs per bank, that can all be interrupt sources. + It is deeply interconnected with the associated pinctrl driver as GPIO + registers handle muxing ("alternate functions") as well. + config GPIO_NPCM_SGPIO bool "Nuvoton SGPIO support" depends on ARCH_NPCM || COMPILE_TEST @@ -494,9 +530,9 @@ config GPIO_OCTEON family of SOCs. config GPIO_OMAP - tristate "TI OMAP GPIO support" if ARCH_OMAP2PLUS || COMPILE_TEST + tristate "TI OMAP GPIO support" + depends on ARCH_OMAP || COMPILE_TEST default y if ARCH_OMAP - depends on ARM select GENERIC_IRQ_CHIP select GPIOLIB_IRQCHIP help @@ -510,6 +546,12 @@ config GPIO_PL061 help Say yes here to support the PrimeCell PL061 GPIO device. +config GPIO_POLARFIRE_SOC + bool "Microchip FPGA GPIO support" + select REGMAP_MMIO + help + Say yes here to support the GPIO controllers on Microchip FPGAs. + config GPIO_PXA bool "PXA GPIO support" depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST @@ -675,13 +717,13 @@ config GPIO_TEGRA config GPIO_TEGRA186 tristate "NVIDIA Tegra186 GPIO support" - default ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC - depends on ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || COMPILE_TEST + default ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC + depends on ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC || COMPILE_TEST depends on OF_GPIO select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY help - Say yes here to support GPIO pins on NVIDIA Tegra186 SoCs. + Say yes here to support GPIO pins on NVIDIA Tegra186, 194 and 234 SoCs. config GPIO_TS4800 tristate "TS-4800 DIO blocks and compatibles" @@ -711,8 +753,9 @@ config GPIO_UNIPHIER Say yes here to support UniPhier GPIOs. config GPIO_VF610 - def_bool y - depends on ARCH_MXC + bool "VF610 GPIO support" + default y if SOC_VF610 + depends on ARCH_MXC || COMPILE_TEST select GPIOLIB_IRQCHIP help Say yes here to support i.MX or Vybrid vf610 GPIOs. @@ -756,7 +799,6 @@ config GPIO_XGENE_SB config GPIO_XILINX tristate "Xilinx GPIO support" select GPIOLIB_IRQCHIP - depends on OF_GPIO help Say yes here to support the Xilinx FPGA GPIO device. @@ -1193,6 +1235,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 @@ -1240,6 +1289,26 @@ config GPIO_BD9571MWV This driver can also be built as a module. If so, the module will be called gpio-bd9571mwv. +config GPIO_CGBC + tristate "Congatec Board Controller GPIO support" + depends on MFD_CGBC + help + Select this option to enable GPIO support for the Congatec Board + Controller. + + This driver can also be built as a module. If so, the module will be + called gpio-cgbc. + +config GPIO_CROS_EC + tristate "ChromeOS EC GPIO support" + depends on CROS_EC + help + GPIO driver for the ChromeOS Embedded Controller (EC). GPIOs + cannot be set unless the system is unlocked. + + This driver can also be built as a module. If so, the module + will be called gpio-cros-ec. + config GPIO_CRYSTAL_COVE tristate "GPIO support for Crystal Cove PMIC" depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC @@ -1526,7 +1595,7 @@ config GPIO_TPS68470 are "output only" GPIOs. config GPIO_TQMX86 - tristate "TQ-Systems QTMX86 GPIO" + tristate "TQ-Systems TQMx86 GPIO" depends on MFD_TQMX86 || COMPILE_TEST depends on HAS_IOPORT_MAP select GPIOLIB_IRQCHIP @@ -1787,6 +1856,13 @@ config GPIO_VIPERBOARD River Tech's viperboard.h for detailed meaning of the module parameters. +config GPIO_MPSSE + tristate "FTDI MPSSE GPIO support" + select GPIOLIB_IRQCHIP + help + GPIO driver for FTDI's MPSSE interface. These can do input and + output. Each MPSSE provides 16 IO pins. + endmenu menu "Virtual GPIO drivers" @@ -1841,4 +1917,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 9e40af196aae..af3ba4d81b58 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 @@ -44,8 +45,10 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o +obj-$(CONFIG_GPIO_CGBC) += gpio-cgbc.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o +obj-$(CONFIG_GPIO_CROS_EC) += gpio-cros-ec.o obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o @@ -65,6 +68,7 @@ obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o obj-$(CONFIG_GPIO_FXL6408) += gpio-fxl6408.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o +obj-$(CONFIG_GPIO_GRANITERAPIDS) += gpio-graniterapids.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o @@ -111,11 +115,13 @@ obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o +obj-$(CONFIG_GPIO_MPSSE) += gpio-mpsse.o obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o +obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o @@ -129,6 +135,7 @@ obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o +obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o @@ -147,6 +154,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 @@ -178,6 +186,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/TODO b/drivers/gpio/TODO index 189c3abe7e79..942d1cd2bd3c 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -61,8 +61,8 @@ Work items: - Change all consumer drivers that #include <linux/of_gpio.h> to #include <linux/gpio/consumer.h> and stop doing custom parsing of the - GPIO lines from the device tree. This can be tricky and often ivolves - changing boardfiles, etc. + GPIO lines from the device tree. This can be tricky and often involves + changing board files, etc. - Pull semantics for legacy device tree (OF) GPIO lookups into gpiolib-of.c: in some cases subsystems are doing custom flags and diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index 4df9becaf349..cf5a50102d49 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -22,7 +22,7 @@ #include "gpio-i8255.h" -MODULE_IMPORT_NS(I8255); +MODULE_IMPORT_NS("I8255"); #define DIO48E_EXTENT 16 #define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT) @@ -339,4 +339,4 @@ module_isa_driver_with_irq(dio48e_driver, num_dio48e, num_irq); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(I8254); +MODULE_IMPORT_NS("I8254"); diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index f03ccd0f534c..ffe7e1cb6b23 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -126,4 +126,4 @@ module_isa_driver_with_irq(idio_16_driver, num_idio_16, num_irq); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(GPIO_IDIO_16); +MODULE_IMPORT_NS("GPIO_IDIO_16"); diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 753e7be039e4..fca6cd2eb1dd 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -143,24 +143,17 @@ static int gen_74x164_probe(struct spi_device *spi) chip->gpio_chip.parent = &spi->dev; chip->gpio_chip.owner = THIS_MODULE; - mutex_init(&chip->lock); + ret = devm_mutex_init(&spi->dev, &chip->lock); + if (ret) + return ret; ret = __gen_74x164_write_config(chip); - if (ret) { - dev_err(&spi->dev, "Failed writing: %d\n", ret); - goto exit_destroy; - } + if (ret) + return dev_err_probe(&spi->dev, ret, "Config write failed\n"); gpiod_set_value_cansleep(chip->gpiod_oe, 1); - ret = gpiochip_add_data(&chip->gpio_chip, chip); - if (!ret) - return 0; - -exit_destroy: - mutex_destroy(&chip->lock); - - return ret; + return devm_gpiochip_add_data(&spi->dev, &chip->gpio_chip, chip); } static void gen_74x164_remove(struct spi_device *spi) @@ -168,8 +161,6 @@ static void gen_74x164_remove(struct spi_device *spi) struct gen_74x164_chip *chip = spi_get_drvdata(spi); gpiod_set_value_cansleep(chip->gpiod_oe, 0); - gpiochip_remove(&chip->gpio_chip); - mutex_destroy(&chip->lock); } static const struct spi_device_id gen_74x164_spi_ids[] = { 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-aggregator.c b/drivers/gpio/gpio-aggregator.c index 38e0fff9afe7..d668ddb2e81d 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -65,11 +65,11 @@ static int aggr_parse(struct gpio_aggregator *aggr) { char *args = skip_spaces(aggr->args); char *name, *offsets, *p; - unsigned long *bitmap; unsigned int i, n = 0; int error = 0; - bitmap = bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); + unsigned long *bitmap __free(bitmap) = + bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); if (!bitmap) return -ENOMEM; @@ -82,7 +82,7 @@ static int aggr_parse(struct gpio_aggregator *aggr) /* Named GPIO line */ error = aggr_add_gpio(aggr, name, U16_MAX, &n); if (error) - goto free_bitmap; + return error; name = offsets; continue; @@ -92,13 +92,13 @@ static int aggr_parse(struct gpio_aggregator *aggr) error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); if (error) { pr_err("Cannot parse %s: %d\n", offsets, error); - goto free_bitmap; + return error; } for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { error = aggr_add_gpio(aggr, name, i, &n); if (error) - goto free_bitmap; + return error; } args = next_arg(args, &name, &p); @@ -106,12 +106,10 @@ static int aggr_parse(struct gpio_aggregator *aggr) if (!n) { pr_err("No GPIOs specified\n"); - error = -EINVAL; + return -EINVAL; } -free_bitmap: - bitmap_free(bitmap); - return error; + return 0; } static ssize_t new_device_store(struct device_driver *driver, const char *buf, @@ -121,10 +119,15 @@ static ssize_t new_device_store(struct device_driver *driver, const char *buf, struct platform_device *pdev; int res, id; + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + /* kernfs guarantees string termination, so count + 1 is safe */ aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) - return -ENOMEM; + if (!aggr) { + res = -ENOMEM; + goto put_module; + } memcpy(aggr->args, buf, count + 1); @@ -163,6 +166,7 @@ static ssize_t new_device_store(struct device_driver *driver, const char *buf, } aggr->pdev = pdev; + module_put(THIS_MODULE); return count; remove_table: @@ -177,6 +181,8 @@ free_table: kfree(aggr->lookups); free_ga: kfree(aggr); +put_module: + module_put(THIS_MODULE); return res; } @@ -205,13 +211,19 @@ static ssize_t delete_device_store(struct device_driver *driver, if (error) return error; + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + mutex_lock(&gpio_aggregator_lock); aggr = idr_remove(&gpio_aggregator_idr, id); mutex_unlock(&gpio_aggregator_lock); - if (!aggr) + if (!aggr) { + module_put(THIS_MODULE); return -ENOENT; + } gpio_aggregator_free(aggr); + module_put(THIS_MODULE); return count; } static DRIVER_ATTR_WO(delete_device); diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index c2edfbb231fc..17ab039c7413 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -4,11 +4,19 @@ * Based on gpio-mpc8xxx.c */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> #include <linux/io.h> +#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/gpio/driver.h> -#include <linux/gpio/legacy-of-mm-gpiochip.h> #include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <linux/gpio/driver.h> #define ALTERA_GPIO_MAX_NGPIO 32 #define ALTERA_GPIO_DATA 0x0 @@ -18,56 +26,52 @@ /** * struct altera_gpio_chip -* @mmchip : memory mapped chip structure. +* @gc : GPIO chip structure. +* @regs : memory mapped IO address for the controller registers. * @gpio_lock : synchronization lock so that new irq/set/get requests * will be blocked until the current one completes. * @interrupt_trigger : specifies the hardware configured IRQ trigger type * (rising, falling, both, high) -* @mapped_irq : kernel mapped irq number. */ struct altera_gpio_chip { - struct of_mm_gpio_chip mmchip; + struct gpio_chip gc; + void __iomem *regs; raw_spinlock_t gpio_lock; int interrupt_trigger; - int mapped_irq; }; static void altera_gpio_irq_unmask(struct irq_data *d) { - struct altera_gpio_chip *altera_gc; - struct of_mm_gpio_chip *mm_gc; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); unsigned long flags; u32 intmask; - altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); - mm_gc = &altera_gc->mmchip; - gpiochip_enable_irq(&mm_gc->gc, irqd_to_hwirq(d)); + gpiochip_enable_irq(gc, irqd_to_hwirq(d)); raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + intmask = readl(altera_gc->regs + ALTERA_GPIO_IRQ_MASK); /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ intmask |= BIT(irqd_to_hwirq(d)); - writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + writel(intmask, altera_gc->regs + ALTERA_GPIO_IRQ_MASK); raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } static void altera_gpio_irq_mask(struct irq_data *d) { - struct altera_gpio_chip *altera_gc; - struct of_mm_gpio_chip *mm_gc; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); unsigned long flags; u32 intmask; - altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); - mm_gc = &altera_gc->mmchip; - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + intmask = readl(altera_gc->regs + ALTERA_GPIO_IRQ_MASK); /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ intmask &= ~BIT(irqd_to_hwirq(d)); - writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + writel(intmask, altera_gc->regs + ALTERA_GPIO_IRQ_MASK); raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - gpiochip_disable_irq(&mm_gc->gc, irqd_to_hwirq(d)); + + gpiochip_disable_irq(gc, irqd_to_hwirq(d)); } /* @@ -77,9 +81,8 @@ static void altera_gpio_irq_mask(struct irq_data *d) static int altera_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - struct altera_gpio_chip *altera_gc; - - altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); if (type == IRQ_TYPE_NONE) { irq_set_handler_locked(d, handle_bad_irq); @@ -105,49 +108,39 @@ static unsigned int altera_gpio_irq_startup(struct irq_data *d) static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) { - struct of_mm_gpio_chip *mm_gc; + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - mm_gc = to_of_mm_gpio_chip(gc); - - return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); + return !!(readl(altera_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); } static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { - struct of_mm_gpio_chip *mm_gc; - struct altera_gpio_chip *chip; + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); unsigned long flags; unsigned int data_reg; - mm_gc = to_of_mm_gpio_chip(gc); - chip = gpiochip_get_data(gc); - - raw_spin_lock_irqsave(&chip->gpio_lock, flags); - data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); + raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); + data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); if (value) data_reg |= BIT(offset); else data_reg &= ~BIT(offset); - writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); - raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); + raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) { - struct of_mm_gpio_chip *mm_gc; - struct altera_gpio_chip *chip; + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); unsigned long flags; unsigned int gpio_ddr; - mm_gc = to_of_mm_gpio_chip(gc); - chip = gpiochip_get_data(gc); - - raw_spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); /* Set pin as input, assumes software controlled IP */ - gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); + gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); gpio_ddr &= ~BIT(offset); - writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); + raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); return 0; } @@ -155,53 +148,46 @@ static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) static int altera_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value) { - struct of_mm_gpio_chip *mm_gc; - struct altera_gpio_chip *chip; + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); unsigned long flags; unsigned int data_reg, gpio_ddr; - mm_gc = to_of_mm_gpio_chip(gc); - chip = gpiochip_get_data(gc); - - raw_spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); /* Sets the GPIO value */ - data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); + data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); if (value) data_reg |= BIT(offset); else data_reg &= ~BIT(offset); - writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); + writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); /* Set pin as output, assumes software controlled IP */ - gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); + gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); gpio_ddr |= BIT(offset); - writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); + raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); return 0; } static void altera_gpio_irq_edge_handler(struct irq_desc *desc) { - struct altera_gpio_chip *altera_gc; + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); + struct irq_domain *irqdomain = gc->irq.domain; struct irq_chip *chip; - struct of_mm_gpio_chip *mm_gc; - struct irq_domain *irqdomain; unsigned long status; int i; - altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); chip = irq_desc_get_chip(desc); - mm_gc = &altera_gc->mmchip; - irqdomain = altera_gc->mmchip.gc.irq.domain; chained_irq_enter(chip, desc); while ((status = - (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & - readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { - writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); - for_each_set_bit(i, &status, mm_gc->gc.ngpio) + (readl(altera_gc->regs + ALTERA_GPIO_EDGE_CAP) & + readl(altera_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { + writel(status, altera_gc->regs + ALTERA_GPIO_EDGE_CAP); + for_each_set_bit(i, &status, gc->ngpio) generic_handle_domain_irq(irqdomain, i); } @@ -210,24 +196,21 @@ static void altera_gpio_irq_edge_handler(struct irq_desc *desc) static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) { - struct altera_gpio_chip *altera_gc; + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); + struct irq_domain *irqdomain = gc->irq.domain; struct irq_chip *chip; - struct of_mm_gpio_chip *mm_gc; - struct irq_domain *irqdomain; unsigned long status; int i; - altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); chip = irq_desc_get_chip(desc); - mm_gc = &altera_gc->mmchip; - irqdomain = altera_gc->mmchip.gc.irq.domain; chained_irq_enter(chip, desc); - status = readl(mm_gc->regs + ALTERA_GPIO_DATA); - status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + status = readl(altera_gc->regs + ALTERA_GPIO_DATA); + status &= readl(altera_gc->regs + ALTERA_GPIO_IRQ_MASK); - for_each_set_bit(i, &status, mm_gc->gc.ngpio) + for_each_set_bit(i, &status, gc->ngpio) generic_handle_domain_irq(irqdomain, i); chained_irq_exit(chip, desc); @@ -246,10 +229,11 @@ static const struct irq_chip altera_gpio_irq_chip = { static int altera_gpio_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; int reg, ret; struct altera_gpio_chip *altera_gc; struct gpio_irq_chip *girq; + int mapped_irq; altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); if (!altera_gc) @@ -257,39 +241,47 @@ static int altera_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&altera_gc->gpio_lock); - if (of_property_read_u32(node, "altr,ngpio", ®)) + if (device_property_read_u32(dev, "altr,ngpio", ®)) /* By default assume maximum ngpio */ - altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; else - altera_gc->mmchip.gc.ngpio = reg; + altera_gc->gc.ngpio = reg; - if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { + if (altera_gc->gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { dev_warn(&pdev->dev, "ngpio is greater than %d, defaulting to %d\n", ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); - altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; } - altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input; - altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output; - altera_gc->mmchip.gc.get = altera_gpio_get; - altera_gc->mmchip.gc.set = altera_gpio_set; - altera_gc->mmchip.gc.owner = THIS_MODULE; - altera_gc->mmchip.gc.parent = &pdev->dev; + altera_gc->gc.direction_input = altera_gpio_direction_input; + altera_gc->gc.direction_output = altera_gpio_direction_output; + altera_gc->gc.get = altera_gpio_get; + altera_gc->gc.set = altera_gpio_set; + altera_gc->gc.owner = THIS_MODULE; + altera_gc->gc.parent = &pdev->dev; + altera_gc->gc.base = -1; - altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); + altera_gc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!altera_gc->gc.label) + return -ENOMEM; + + altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(altera_gc->regs)) + return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n"); - if (altera_gc->mapped_irq < 0) + mapped_irq = platform_get_irq_optional(pdev, 0); + if (mapped_irq < 0) goto skip_irq; - if (of_property_read_u32(node, "altr,interrupt-type", ®)) { + if (device_property_read_u32(dev, "altr,interrupt-type", ®)) { dev_err(&pdev->dev, "altr,interrupt-type value not set in device tree\n"); return -EINVAL; } altera_gc->interrupt_trigger = reg; - girq = &altera_gc->mmchip.gc.irq; + girq = &altera_gc->gc.irq; gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip); if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) @@ -303,27 +295,18 @@ static int altera_gpio_probe(struct platform_device *pdev) return -ENOMEM; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; - girq->parents[0] = altera_gc->mapped_irq; + girq->parents[0] = mapped_irq; skip_irq: - ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc); + ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc); if (ret) { dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); return ret; } - platform_set_drvdata(pdev, altera_gc); - return 0; } -static void altera_gpio_remove(struct platform_device *pdev) -{ - struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); - - of_mm_gpiochip_remove(&altera_gc->mmchip); -} - static const struct of_device_id altera_gpio_of_match[] = { { .compatible = "altr,pio-1.0", }, {}, @@ -336,7 +319,6 @@ static struct platform_driver altera_gpio_driver = { .of_match_table = altera_gpio_of_match, }, .probe = altera_gpio_probe, - .remove_new = altera_gpio_remove, }; static int __init altera_gpio_init(void) 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-amdpt.c b/drivers/gpio/gpio-amdpt.c index 0a2ea9db4682..b70036587d9c 100644 --- a/drivers/gpio/gpio-amdpt.c +++ b/drivers/gpio/gpio-amdpt.c @@ -106,7 +106,7 @@ static int pt_gpio_probe(struct platform_device *pdev) pt_gpio->gc.free = pt_gpio_free; pt_gpio->gc.ngpio = (uintptr_t)device_get_match_data(dev); - ret = gpiochip_add_data(&pt_gpio->gc, pt_gpio); + ret = devm_gpiochip_add_data(dev, &pt_gpio->gc, pt_gpio); if (ret) { dev_err(dev, "Failed to register GPIO lib\n"); return ret; @@ -122,13 +122,6 @@ static int pt_gpio_probe(struct platform_device *pdev) return ret; } -static void pt_gpio_remove(struct platform_device *pdev) -{ - struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev); - - gpiochip_remove(&pt_gpio->gc); -} - static const struct acpi_device_id pt_gpio_acpi_match[] = { { "AMDF030", PT_TOTAL_GPIO }, { "AMDIF030", PT_TOTAL_GPIO }, @@ -143,7 +136,6 @@ static struct platform_driver pt_gpio_driver = { .acpi_match_table = ACPI_PTR(pt_gpio_acpi_match), }, .probe = pt_gpio_probe, - .remove_new = pt_gpio_remove, }; module_platform_driver(pt_gpio_driver); diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 72755fee6478..34eb26298e32 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -420,7 +420,7 @@ static void aspeed_sgpio_irq_print_chip(struct irq_data *d, struct seq_file *p) int offset; irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); - seq_printf(p, dev_name(gpio->dev)); + seq_puts(p, dev_name(gpio->dev)); } static const struct irq_chip aspeed_sgpio_irq_chip = { diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 04c03402db6d..40c1bd80f8b0 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -30,6 +30,27 @@ #include <linux/gpio/consumer.h> #include "gpiolib.h" +/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + +#define GPIO_G7_IRQ_STS_BASE 0x100 +#define GPIO_G7_IRQ_STS_OFFSET(x) (GPIO_G7_IRQ_STS_BASE + (x) * 0x4) +#define GPIO_G7_CTRL_REG_BASE 0x180 +#define GPIO_G7_CTRL_REG_OFFSET(x) (GPIO_G7_CTRL_REG_BASE + (x) * 0x4) +#define GPIO_G7_CTRL_OUT_DATA BIT(0) +#define GPIO_G7_CTRL_DIR BIT(1) +#define GPIO_G7_CTRL_IRQ_EN BIT(2) +#define GPIO_G7_CTRL_IRQ_TYPE0 BIT(3) +#define GPIO_G7_CTRL_IRQ_TYPE1 BIT(4) +#define GPIO_G7_CTRL_IRQ_TYPE2 BIT(5) +#define GPIO_G7_CTRL_RST_TOLERANCE BIT(6) +#define GPIO_G7_CTRL_DEBOUNCE_SEL1 BIT(7) +#define GPIO_G7_CTRL_DEBOUNCE_SEL2 BIT(8) +#define GPIO_G7_CTRL_INPUT_MASK BIT(9) +#define GPIO_G7_CTRL_IRQ_STS BIT(12) +#define GPIO_G7_CTRL_IN_DATA BIT(13) + struct aspeed_bank_props { unsigned int bank; u32 input; @@ -39,6 +60,10 @@ struct aspeed_bank_props { struct aspeed_gpio_config { unsigned int nr_gpios; const struct aspeed_bank_props *props; + const struct aspeed_gpio_llops *llops; + const int *debounce_timers_array; + int debounce_timers_num; + bool require_dcache; }; /* @@ -77,7 +102,6 @@ struct aspeed_gpio_bank { uint16_t debounce_regs; uint16_t tolerance_regs; uint16_t cmdsrc_regs; - const char names[4][3]; }; /* @@ -92,6 +116,22 @@ struct aspeed_gpio_bank { */ static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 }; +static const int g7_debounce_timers[4] = { 0x00, 0x00, 0x04, 0x08 }; + +/* + * The debounce timers array is used to configure the debounce timer settings.Here’s how it works: + * Array Value: Indicates the offset for configuring the debounce timer. + * Array Index: Corresponds to the debounce setting register. + * The debounce timers array follows this pattern for configuring the debounce setting registers: + * Array Index 0: No debounce timer is set; + * Array Value is irrelevant (don’t care). + * Array Index 1: Debounce setting #2 is set to 1, and debounce setting #1 is set to 0. + * Array Value: offset for configuring debounce timer 0 (g4: 0x50, g7: 0x00) + * Array Index 2: Debounce setting #2 is set to 0, and debounce setting #1 is set to 1. + * Array Value: offset for configuring debounce timer 1 (g4: 0x54, g7: 0x04) + * Array Index 3: Debounce setting #2 is set to 1, and debounce setting #1 is set to 1. + * Array Value: offset for configuring debounce timer 2 (g4: 0x58, g7: 0x8) + */ static const struct aspeed_gpio_copro_ops *copro_ops; static void *copro_data; @@ -104,7 +144,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x0040, .tolerance_regs = 0x001c, .cmdsrc_regs = 0x0060, - .names = { "A", "B", "C", "D" }, }, { .val_regs = 0x0020, @@ -113,7 +152,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x0048, .tolerance_regs = 0x003c, .cmdsrc_regs = 0x0068, - .names = { "E", "F", "G", "H" }, }, { .val_regs = 0x0070, @@ -122,7 +160,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x00b0, .tolerance_regs = 0x00ac, .cmdsrc_regs = 0x0090, - .names = { "I", "J", "K", "L" }, }, { .val_regs = 0x0078, @@ -131,7 +168,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x0100, .tolerance_regs = 0x00fc, .cmdsrc_regs = 0x00e0, - .names = { "M", "N", "O", "P" }, }, { .val_regs = 0x0080, @@ -140,7 +176,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x0130, .tolerance_regs = 0x012c, .cmdsrc_regs = 0x0110, - .names = { "Q", "R", "S", "T" }, }, { .val_regs = 0x0088, @@ -149,7 +184,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x0160, .tolerance_regs = 0x015c, .cmdsrc_regs = 0x0140, - .names = { "U", "V", "W", "X" }, }, { .val_regs = 0x01E0, @@ -158,7 +192,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x0190, .tolerance_regs = 0x018c, .cmdsrc_regs = 0x0170, - .names = { "Y", "Z", "AA", "AB" }, }, { .val_regs = 0x01e8, @@ -167,7 +200,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .debounce_regs = 0x01c0, .tolerance_regs = 0x01bc, .cmdsrc_regs = 0x01a0, - .names = { "AC", "", "", "" }, }, }; @@ -187,6 +219,19 @@ enum aspeed_gpio_reg { reg_cmdsrc1, }; +struct aspeed_gpio_llops { + void (*reg_bit_set)(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg, bool val); + bool (*reg_bit_get)(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg); + int (*reg_bank_get)(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg); + void (*privilege_ctrl)(struct aspeed_gpio *gpio, unsigned int offset, int owner); + void (*privilege_init)(struct aspeed_gpio *gpio); + bool (*copro_request)(struct aspeed_gpio *gpio, unsigned int offset); + void (*copro_release)(struct aspeed_gpio *gpio, unsigned int offset); +}; + #define GPIO_VAL_VALUE 0x00 #define GPIO_VAL_DIR 0x04 @@ -207,9 +252,9 @@ enum aspeed_gpio_reg { #define GPIO_CMDSRC_RESERVED 3 /* This will be resolved at compile time */ -static inline void __iomem *bank_reg(struct aspeed_gpio *gpio, - const struct aspeed_gpio_bank *bank, - const enum aspeed_gpio_reg reg) +static void __iomem *aspeed_gpio_g4_bank_reg(struct aspeed_gpio *gpio, + const struct aspeed_gpio_bank *bank, + const enum aspeed_gpio_reg reg) { switch (reg) { case reg_val: @@ -242,14 +287,43 @@ static inline void __iomem *bank_reg(struct aspeed_gpio *gpio, BUG(); } +static u32 aspeed_gpio_g7_reg_mask(const enum aspeed_gpio_reg reg) +{ + switch (reg) { + case reg_val: + return GPIO_G7_CTRL_OUT_DATA; + case reg_dir: + return GPIO_G7_CTRL_DIR; + case reg_irq_enable: + return GPIO_G7_CTRL_IRQ_EN; + case reg_irq_type0: + return GPIO_G7_CTRL_IRQ_TYPE0; + case reg_irq_type1: + return GPIO_G7_CTRL_IRQ_TYPE1; + case reg_irq_type2: + return GPIO_G7_CTRL_IRQ_TYPE2; + case reg_tolerance: + return GPIO_G7_CTRL_RST_TOLERANCE; + case reg_debounce_sel1: + return GPIO_G7_CTRL_DEBOUNCE_SEL1; + case reg_debounce_sel2: + return GPIO_G7_CTRL_DEBOUNCE_SEL2; + case reg_rdata: + return GPIO_G7_CTRL_OUT_DATA; + case reg_irq_status: + return GPIO_G7_CTRL_IRQ_STS; + case reg_cmdsrc0: + case reg_cmdsrc1: + default: + WARN_ON_ONCE(1); + return 0; + } +} + #define GPIO_BANK(x) ((x) >> 5) #define GPIO_OFFSET(x) ((x) & 0x1f) #define GPIO_BIT(x) BIT(GPIO_OFFSET(x)) -#define _GPIO_SET_DEBOUNCE(t, o, i) ((!!((t) & BIT(i))) << GPIO_OFFSET(o)) -#define GPIO_SET_DEBOUNCE1(t, o) _GPIO_SET_DEBOUNCE(t, o, 1) -#define GPIO_SET_DEBOUNCE2(t, o) _GPIO_SET_DEBOUNCE(t, o, 0) - static const struct aspeed_gpio_bank *to_bank(unsigned int offset) { unsigned int bank = GPIO_BANK(offset); @@ -280,11 +354,11 @@ static inline const struct aspeed_bank_props *find_bank_props( static inline bool have_gpio(struct aspeed_gpio *gpio, unsigned int offset) { const struct aspeed_bank_props *props = find_bank_props(gpio, offset); - const struct aspeed_gpio_bank *bank = to_bank(offset); - unsigned int group = GPIO_OFFSET(offset) / 8; - return bank->names[group][0] != '\0' && - (!props || ((props->input | props->output) & GPIO_BIT(offset))); + if (offset >= gpio->chip.ngpio) + return false; + + return (!props || ((props->input | props->output) & GPIO_BIT(offset))); } static inline bool have_input(struct aspeed_gpio *gpio, unsigned int offset) @@ -304,108 +378,49 @@ static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset) return !props || (props->output & GPIO_BIT(offset)); } -static void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio, - const struct aspeed_gpio_bank *bank, - int bindex, int cmdsrc) +static void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio, unsigned int offset, int cmdsrc) { - void __iomem *c0 = bank_reg(gpio, bank, reg_cmdsrc0); - void __iomem *c1 = bank_reg(gpio, bank, reg_cmdsrc1); - u32 bit, reg; - - /* - * Each register controls 4 banks, so take the bottom 2 - * bits of the bank index, and use them to select the - * right control bit (0, 8, 16 or 24). - */ - bit = BIT((bindex & 3) << 3); - - /* Source 1 first to avoid illegal 11 combination */ - reg = ioread32(c1); - if (cmdsrc & 2) - reg |= bit; - else - reg &= ~bit; - iowrite32(reg, c1); - - /* Then Source 0 */ - reg = ioread32(c0); - if (cmdsrc & 1) - reg |= bit; - else - reg &= ~bit; - iowrite32(reg, c0); + if (gpio->config->llops->privilege_ctrl) + gpio->config->llops->privilege_ctrl(gpio, offset, cmdsrc); } static bool aspeed_gpio_copro_request(struct aspeed_gpio *gpio, unsigned int offset) { - const struct aspeed_gpio_bank *bank = to_bank(offset); - - if (!copro_ops || !gpio->cf_copro_bankmap) - return false; - if (!gpio->cf_copro_bankmap[offset >> 3]) - return false; - if (!copro_ops->request_access) - return false; - - /* Pause the coprocessor */ - copro_ops->request_access(copro_data); - - /* Change command source back to ARM */ - aspeed_gpio_change_cmd_source(gpio, bank, offset >> 3, GPIO_CMDSRC_ARM); - - /* Update cache */ - gpio->dcache[GPIO_BANK(offset)] = ioread32(bank_reg(gpio, bank, reg_rdata)); + if (gpio->config->llops->copro_request) + return gpio->config->llops->copro_request(gpio, offset); - return true; + return false; } static void aspeed_gpio_copro_release(struct aspeed_gpio *gpio, unsigned int offset) { - const struct aspeed_gpio_bank *bank = to_bank(offset); - - if (!copro_ops || !gpio->cf_copro_bankmap) - return; - if (!gpio->cf_copro_bankmap[offset >> 3]) - return; - if (!copro_ops->release_access) - return; - - /* Change command source back to ColdFire */ - aspeed_gpio_change_cmd_source(gpio, bank, offset >> 3, - GPIO_CMDSRC_COLDFIRE); + if (gpio->config->llops->copro_release) + gpio->config->llops->copro_release(gpio, offset); +} - /* Restart the coprocessor */ - copro_ops->release_access(copro_data); +static bool aspeed_gpio_support_copro(struct aspeed_gpio *gpio) +{ + return gpio->config->llops->copro_request && gpio->config->llops->copro_release && + gpio->config->llops->privilege_ctrl && gpio->config->llops->privilege_init; } static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - const struct aspeed_gpio_bank *bank = to_bank(offset); - return !!(ioread32(bank_reg(gpio, bank, reg_val)) & GPIO_BIT(offset)); + return gpio->config->llops->reg_bit_get(gpio, offset, reg_val); } static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - const struct aspeed_gpio_bank *bank = to_bank(offset); - void __iomem *addr; - u32 reg; - - addr = bank_reg(gpio, bank, reg_val); - reg = gpio->dcache[GPIO_BANK(offset)]; - if (val) - reg |= GPIO_BIT(offset); - else - reg &= ~GPIO_BIT(offset); - gpio->dcache[GPIO_BANK(offset)] = reg; - - iowrite32(reg, addr); + gpio->config->llops->reg_bit_set(gpio, offset, reg_val, val); + /* Flush write */ + gpio->config->llops->reg_bit_get(gpio, offset, reg_val); } static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, @@ -413,7 +428,7 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, { struct aspeed_gpio *gpio = gpiochip_get_data(gc); unsigned long flags; - bool copro; + bool copro = false; raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); @@ -428,22 +443,16 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - const struct aspeed_gpio_bank *bank = to_bank(offset); - void __iomem *addr = bank_reg(gpio, bank, reg_dir); unsigned long flags; - bool copro; - u32 reg; + bool copro = false; if (!have_input(gpio, offset)) return -ENOTSUPP; raw_spin_lock_irqsave(&gpio->lock, flags); - reg = ioread32(addr); - reg &= ~GPIO_BIT(offset); - copro = aspeed_gpio_copro_request(gpio, offset); - iowrite32(reg, addr); + gpio->config->llops->reg_bit_set(gpio, offset, reg_dir, 0); if (copro) aspeed_gpio_copro_release(gpio, offset); @@ -456,23 +465,17 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - const struct aspeed_gpio_bank *bank = to_bank(offset); - void __iomem *addr = bank_reg(gpio, bank, reg_dir); unsigned long flags; - bool copro; - u32 reg; + bool copro = false; if (!have_output(gpio, offset)) return -ENOTSUPP; raw_spin_lock_irqsave(&gpio->lock, flags); - reg = ioread32(addr); - reg |= GPIO_BIT(offset); - copro = aspeed_gpio_copro_request(gpio, offset); __aspeed_gpio_set(gc, offset, val); - iowrite32(reg, addr); + gpio->config->llops->reg_bit_set(gpio, offset, reg_dir, 1); if (copro) aspeed_gpio_copro_release(gpio, offset); @@ -484,7 +487,6 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc, static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - const struct aspeed_gpio_bank *bank = to_bank(offset); unsigned long flags; u32 val; @@ -496,7 +498,7 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) raw_spin_lock_irqsave(&gpio->lock, flags); - val = ioread32(bank_reg(gpio, bank, reg_dir)) & GPIO_BIT(offset); + val = gpio->config->llops->reg_bit_get(gpio, offset, reg_dir); raw_spin_unlock_irqrestore(&gpio->lock, flags); @@ -505,8 +507,7 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) static inline int irqd_to_aspeed_gpio_data(struct irq_data *d, struct aspeed_gpio **gpio, - const struct aspeed_gpio_bank **bank, - u32 *bit, int *offset) + int *offset) { struct aspeed_gpio *internal; @@ -519,32 +520,25 @@ static inline int irqd_to_aspeed_gpio_data(struct irq_data *d, return -ENOTSUPP; *gpio = internal; - *bank = to_bank(*offset); - *bit = GPIO_BIT(*offset); return 0; } static void aspeed_gpio_irq_ack(struct irq_data *d) { - const struct aspeed_gpio_bank *bank; struct aspeed_gpio *gpio; unsigned long flags; - void __iomem *status_addr; int rc, offset; - bool copro; - u32 bit; + bool copro = false; - rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); + rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset); if (rc) return; - status_addr = bank_reg(gpio, bank, reg_irq_status); - raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); - iowrite32(bit, status_addr); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_status, 1); if (copro) aspeed_gpio_copro_release(gpio, offset); @@ -553,20 +547,15 @@ static void aspeed_gpio_irq_ack(struct irq_data *d) static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) { - const struct aspeed_gpio_bank *bank; struct aspeed_gpio *gpio; unsigned long flags; - u32 reg, bit; - void __iomem *addr; int rc, offset; - bool copro; + bool copro = false; - rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); + rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset); if (rc) return; - addr = bank_reg(gpio, bank, reg_irq_enable); - /* Unmasking the IRQ */ if (set) gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d)); @@ -574,12 +563,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); - reg = ioread32(addr); - if (set) - reg |= bit; - else - reg &= ~bit; - iowrite32(reg, addr); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_enable, set); if (copro) aspeed_gpio_copro_release(gpio, offset); @@ -605,34 +589,31 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) u32 type0 = 0; u32 type1 = 0; u32 type2 = 0; - u32 bit, reg; - const struct aspeed_gpio_bank *bank; irq_flow_handler_t handler; struct aspeed_gpio *gpio; unsigned long flags; - void __iomem *addr; int rc, offset; - bool copro; + bool copro = false; - rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); + rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset); if (rc) return -EINVAL; switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_BOTH: - type2 |= bit; + type2 = 1; fallthrough; case IRQ_TYPE_EDGE_RISING: - type0 |= bit; + type0 = 1; fallthrough; case IRQ_TYPE_EDGE_FALLING: handler = handle_edge_irq; break; case IRQ_TYPE_LEVEL_HIGH: - type0 |= bit; + type0 = 1; fallthrough; case IRQ_TYPE_LEVEL_LOW: - type1 |= bit; + type1 = 1; handler = handle_level_irq; break; default: @@ -642,20 +623,9 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); - addr = bank_reg(gpio, bank, reg_irq_type0); - reg = ioread32(addr); - reg = (reg & ~bit) | type0; - iowrite32(reg, addr); - - addr = bank_reg(gpio, bank, reg_irq_type1); - reg = ioread32(addr); - reg = (reg & ~bit) | type1; - iowrite32(reg, addr); - - addr = bank_reg(gpio, bank, reg_irq_type2); - reg = ioread32(addr); - reg = (reg & ~bit) | type2; - iowrite32(reg, addr); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type0, type0); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type1, type1); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type2, type2); if (copro) aspeed_gpio_copro_release(gpio, offset); @@ -670,7 +640,6 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct irq_chip *ic = irq_desc_get_chip(desc); - struct aspeed_gpio *data = gpiochip_get_data(gc); unsigned int i, p, banks; unsigned long reg; struct aspeed_gpio *gpio = gpiochip_get_data(gc); @@ -679,9 +648,7 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc) banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); for (i = 0; i < banks; i++) { - const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i]; - - reg = ioread32(bank_reg(data, bank, reg_irq_status)); + reg = gpio->config->llops->reg_bank_get(gpio, i * 32, reg_irq_status); for_each_set_bit(p, ®, 32) generic_handle_domain_irq(gc->irq.domain, i * 32 + p); @@ -720,23 +687,12 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, { struct aspeed_gpio *gpio = gpiochip_get_data(chip); unsigned long flags; - void __iomem *treg; - bool copro; - u32 val; - - treg = bank_reg(gpio, to_bank(offset), reg_tolerance); + bool copro = false; raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); - val = readl(treg); - - if (enable) - val |= GPIO_BIT(offset); - else - val &= ~GPIO_BIT(offset); - - writel(val, treg); + gpio->config->llops->reg_bit_set(gpio, offset, reg_tolerance, enable); if (copro) aspeed_gpio_copro_release(gpio, offset); @@ -830,21 +786,11 @@ static inline bool timer_allocation_registered(struct aspeed_gpio *gpio, static void configure_timer(struct aspeed_gpio *gpio, unsigned int offset, unsigned int timer) { - const struct aspeed_gpio_bank *bank = to_bank(offset); - const u32 mask = GPIO_BIT(offset); - void __iomem *addr; - u32 val; - /* Note: Debounce timer isn't under control of the command * source registers, so no need to sync with the coprocessor */ - addr = bank_reg(gpio, bank, reg_debounce_sel1); - val = ioread32(addr); - iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE1(timer, offset), addr); - - addr = bank_reg(gpio, bank, reg_debounce_sel2); - val = ioread32(addr); - iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE2(timer, offset), addr); + gpio->config->llops->reg_bit_set(gpio, offset, reg_debounce_sel1, !!(timer & BIT(1))); + gpio->config->llops->reg_bit_set(gpio, offset, reg_debounce_sel2, !!(timer & BIT(0))); } static int enable_debounce(struct gpio_chip *chip, unsigned int offset, @@ -875,15 +821,15 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, } /* Try to find a timer already configured for the debounce period */ - for (i = 1; i < ARRAY_SIZE(debounce_timers); i++) { + for (i = 1; i < gpio->config->debounce_timers_num; i++) { u32 cycles; - cycles = ioread32(gpio->base + debounce_timers[i]); + cycles = ioread32(gpio->base + gpio->config->debounce_timers_array[i]); if (requested_cycles == cycles) break; } - if (i == ARRAY_SIZE(debounce_timers)) { + if (i == gpio->config->debounce_timers_num) { int j; /* @@ -897,8 +843,8 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, if (j == ARRAY_SIZE(gpio->timer_users)) { dev_warn(chip->parent, - "Debounce timers exhausted, cannot debounce for period %luus\n", - usecs); + "Debounce timers exhausted, cannot debounce for period %luus\n", + usecs); rc = -EPERM; @@ -914,7 +860,7 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, i = j; - iowrite32(requested_cycles, gpio->base + debounce_timers[i]); + iowrite32(requested_cycles, gpio->base + gpio->config->debounce_timers_array[i]); } if (WARN(i == 0, "Cannot register index of disabled timer\n")) { @@ -1017,6 +963,9 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, const struct aspeed_gpio_bank *bank = to_bank(offset); unsigned long flags; + if (!aspeed_gpio_support_copro(gpio)) + return -EOPNOTSUPP; + if (!gpio->cf_copro_bankmap) gpio->cf_copro_bankmap = kzalloc(gpio->chip.ngpio >> 3, GFP_KERNEL); if (!gpio->cf_copro_bankmap) @@ -1036,7 +985,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, /* Switch command source */ if (gpio->cf_copro_bankmap[bindex] == 1) - aspeed_gpio_change_cmd_source(gpio, bank, bindex, + aspeed_gpio_change_cmd_source(gpio, offset, GPIO_CMDSRC_COLDFIRE); if (vreg_offset) @@ -1060,9 +1009,11 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) struct gpio_chip *chip = gpiod_to_chip(desc); struct aspeed_gpio *gpio = gpiochip_get_data(chip); int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); - const struct aspeed_gpio_bank *bank = to_bank(offset); unsigned long flags; + if (!aspeed_gpio_support_copro(gpio)) + return -EOPNOTSUPP; + if (!gpio->cf_copro_bankmap) return -ENXIO; @@ -1081,7 +1032,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) /* Switch command source */ if (gpio->cf_copro_bankmap[bindex] == 0) - aspeed_gpio_change_cmd_source(gpio, bank, bindex, + aspeed_gpio_change_cmd_source(gpio, offset, GPIO_CMDSRC_ARM); bail: raw_spin_unlock_irqrestore(&gpio->lock, flags); @@ -1091,16 +1042,14 @@ EXPORT_SYMBOL_GPL(aspeed_gpio_copro_release_gpio); static void aspeed_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { - const struct aspeed_gpio_bank *bank; struct aspeed_gpio *gpio; - u32 bit; int rc, offset; - rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset); + rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset); if (rc) return; - seq_printf(p, dev_name(gpio->dev)); + seq_puts(p, dev_name(gpio->dev)); } static const struct irq_chip aspeed_gpio_irq_chip = { @@ -1113,6 +1062,173 @@ static const struct irq_chip aspeed_gpio_irq_chip = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; +static void aspeed_g4_reg_bit_set(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg, bool val) +{ + const struct aspeed_gpio_bank *bank = to_bank(offset); + void __iomem *addr = aspeed_gpio_g4_bank_reg(gpio, bank, reg); + u32 temp; + + if (reg == reg_val) + temp = gpio->dcache[GPIO_BANK(offset)]; + else + temp = ioread32(addr); + + if (val) + temp |= GPIO_BIT(offset); + else + temp &= ~GPIO_BIT(offset); + + if (reg == reg_val) + gpio->dcache[GPIO_BANK(offset)] = temp; + iowrite32(temp, addr); +} + +static bool aspeed_g4_reg_bit_get(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg) +{ + const struct aspeed_gpio_bank *bank = to_bank(offset); + void __iomem *addr = aspeed_gpio_g4_bank_reg(gpio, bank, reg); + + return !!(ioread32(addr) & GPIO_BIT(offset)); +} + +static int aspeed_g4_reg_bank_get(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg) +{ + const struct aspeed_gpio_bank *bank = to_bank(offset); + void __iomem *addr = aspeed_gpio_g4_bank_reg(gpio, bank, reg); + + if (reg == reg_rdata || reg == reg_irq_status) + return ioread32(addr); + else + return -EOPNOTSUPP; +} + +static void aspeed_g4_privilege_ctrl(struct aspeed_gpio *gpio, unsigned int offset, int cmdsrc) +{ + /* + * The command source register is only valid in bits 0, 8, 16, and 24, so we use + * (offset & ~(0x7)) to ensure that reg_bits_set always targets a valid bit. + */ + /* Source 1 first to avoid illegal 11 combination */ + aspeed_g4_reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc1, !!(cmdsrc & BIT(1))); + /* Then Source 0 */ + aspeed_g4_reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc0, !!(cmdsrc & BIT(0))); +} + +static void aspeed_g4_privilege_init(struct aspeed_gpio *gpio) +{ + u32 i; + + /* Switch all command sources to the ARM by default */ + for (i = 0; i < DIV_ROUND_UP(gpio->chip.ngpio, 32); i++) { + aspeed_g4_privilege_ctrl(gpio, (i << 5) + 0, GPIO_CMDSRC_ARM); + aspeed_g4_privilege_ctrl(gpio, (i << 5) + 8, GPIO_CMDSRC_ARM); + aspeed_g4_privilege_ctrl(gpio, (i << 5) + 16, GPIO_CMDSRC_ARM); + aspeed_g4_privilege_ctrl(gpio, (i << 5) + 24, GPIO_CMDSRC_ARM); + } +} + +static bool aspeed_g4_copro_request(struct aspeed_gpio *gpio, unsigned int offset) +{ + if (!copro_ops || !gpio->cf_copro_bankmap) + return false; + if (!gpio->cf_copro_bankmap[offset >> 3]) + return false; + if (!copro_ops->request_access) + return false; + + /* Pause the coprocessor */ + copro_ops->request_access(copro_data); + + /* Change command source back to ARM */ + aspeed_g4_privilege_ctrl(gpio, offset, GPIO_CMDSRC_ARM); + + /* Update cache */ + gpio->dcache[GPIO_BANK(offset)] = aspeed_g4_reg_bank_get(gpio, offset, reg_rdata); + + return true; +} + +static void aspeed_g4_copro_release(struct aspeed_gpio *gpio, unsigned int offset) +{ + if (!copro_ops || !gpio->cf_copro_bankmap) + return; + if (!gpio->cf_copro_bankmap[offset >> 3]) + return; + if (!copro_ops->release_access) + return; + + /* Change command source back to ColdFire */ + aspeed_g4_privilege_ctrl(gpio, offset, GPIO_CMDSRC_COLDFIRE); + + /* Restart the coprocessor */ + copro_ops->release_access(copro_data); +} + +static const struct aspeed_gpio_llops aspeed_g4_llops = { + .reg_bit_set = aspeed_g4_reg_bit_set, + .reg_bit_get = aspeed_g4_reg_bit_get, + .reg_bank_get = aspeed_g4_reg_bank_get, + .privilege_ctrl = aspeed_g4_privilege_ctrl, + .privilege_init = aspeed_g4_privilege_init, + .copro_request = aspeed_g4_copro_request, + .copro_release = aspeed_g4_copro_release, +}; + +static void aspeed_g7_reg_bit_set(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg, bool val) +{ + u32 mask = aspeed_gpio_g7_reg_mask(reg); + void __iomem *addr = gpio->base + GPIO_G7_CTRL_REG_OFFSET(offset); + u32 write_val; + + if (mask) { + write_val = (ioread32(addr) & ~(mask)) | field_prep(mask, val); + iowrite32(write_val, addr); + } +} + +static bool aspeed_g7_reg_bit_get(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg) +{ + u32 mask = aspeed_gpio_g7_reg_mask(reg); + void __iomem *addr; + + addr = gpio->base + GPIO_G7_CTRL_REG_OFFSET(offset); + if (reg == reg_val) + mask = GPIO_G7_CTRL_IN_DATA; + + if (mask) + return field_get(mask, ioread32(addr)); + else + return 0; +} + +static int aspeed_g7_reg_bank_get(struct aspeed_gpio *gpio, unsigned int offset, + const enum aspeed_gpio_reg reg) +{ + void __iomem *addr; + + if (reg == reg_irq_status) { + addr = gpio->base + GPIO_G7_IRQ_STS_OFFSET(offset >> 5); + return ioread32(addr); + } else { + return -EOPNOTSUPP; + } +} + +static const struct aspeed_gpio_llops aspeed_g7_llops = { + .reg_bit_set = aspeed_g7_reg_bit_set, + .reg_bit_get = aspeed_g7_reg_bit_get, + .reg_bank_get = aspeed_g7_reg_bank_get, + .privilege_ctrl = NULL, + .privilege_init = NULL, + .copro_request = NULL, + .copro_release = NULL, +}; + /* * Any banks not specified in a struct aspeed_bank_props array are assumed to * have the properties: @@ -1129,7 +1245,14 @@ static const struct aspeed_bank_props ast2400_bank_props[] = { static const struct aspeed_gpio_config ast2400_config = /* 220 for simplicity, really 216 with two 4-GPIO holes, four at end */ - { .nr_gpios = 220, .props = ast2400_bank_props, }; + { + .nr_gpios = 220, + .props = ast2400_bank_props, + .llops = &aspeed_g4_llops, + .debounce_timers_array = debounce_timers, + .debounce_timers_num = ARRAY_SIZE(debounce_timers), + .require_dcache = true, + }; static const struct aspeed_bank_props ast2500_bank_props[] = { /* input output */ @@ -1141,7 +1264,14 @@ static const struct aspeed_bank_props ast2500_bank_props[] = { static const struct aspeed_gpio_config ast2500_config = /* 232 for simplicity, actual number is 228 (4-GPIO hole in GPIOAB) */ - { .nr_gpios = 232, .props = ast2500_bank_props, }; + { + .nr_gpios = 232, + .props = ast2500_bank_props, + .llops = &aspeed_g4_llops, + .debounce_timers_array = debounce_timers, + .debounce_timers_num = ARRAY_SIZE(debounce_timers), + .require_dcache = true, + }; static const struct aspeed_bank_props ast2600_bank_props[] = { /* input output */ @@ -1157,17 +1287,48 @@ static const struct aspeed_gpio_config ast2600_config = * We expect ngpio being set in the device tree and this is a fallback * option. */ - { .nr_gpios = 208, .props = ast2600_bank_props, }; + { + .nr_gpios = 208, + .props = ast2600_bank_props, + .llops = &aspeed_g4_llops, + .debounce_timers_array = debounce_timers, + .debounce_timers_num = ARRAY_SIZE(debounce_timers), + .require_dcache = true, + }; + +static const struct aspeed_bank_props ast2700_bank_props[] = { + /* input output */ + { 1, 0x0fffffff, 0x0fffffff }, /* E/F/G/H, 4-GPIO hole */ + { 6, 0x00ffffff, 0x00ff0000 }, /* Y/Z/AA */ + {}, +}; + +static const struct aspeed_gpio_config ast2700_config = + /* + * ast2700 has two controllers one with 212 GPIOs and one with 16 GPIOs. + * 216 for simplicity, actual number is 212 (4-GPIO hole in GPIOH) + * We expect ngpio being set in the device tree and this is a fallback + * option. + */ + { + .nr_gpios = 216, + .props = ast2700_bank_props, + .llops = &aspeed_g7_llops, + .debounce_timers_array = g7_debounce_timers, + .debounce_timers_num = ARRAY_SIZE(g7_debounce_timers), + .require_dcache = false, + }; static const struct of_device_id aspeed_gpio_of_table[] = { { .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, }, { .compatible = "aspeed,ast2500-gpio", .data = &ast2500_config, }, { .compatible = "aspeed,ast2600-gpio", .data = &ast2600_config, }, + { .compatible = "aspeed,ast2700-gpio", .data = &ast2700_config, }, {} }; MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table); -static int __init aspeed_gpio_probe(struct platform_device *pdev) +static int aspeed_gpio_probe(struct platform_device *pdev) { const struct of_device_id *gpio_id; struct gpio_irq_chip *girq; @@ -1191,7 +1352,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) if (!gpio_id) return -EINVAL; - gpio->clk = of_clk_get(pdev->dev.of_node, 0); + gpio->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(gpio->clk)) { dev_warn(&pdev->dev, "Failed to get clock from devicetree, debouncing disabled\n"); @@ -1200,6 +1361,10 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) gpio->config = gpio_id->data; + if (!gpio->config->llops->reg_bit_set || !gpio->config->llops->reg_bit_get || + !gpio->config->llops->reg_bank_get) + return -EINVAL; + gpio->chip.parent = &pdev->dev; err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio); gpio->chip.ngpio = (u16) ngpio; @@ -1216,27 +1381,23 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; - /* Allocate a cache of the output registers */ - banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); - gpio->dcache = devm_kcalloc(&pdev->dev, - banks, sizeof(u32), GFP_KERNEL); - if (!gpio->dcache) - return -ENOMEM; - - /* - * Populate it with initial values read from the HW and switch - * all command sources to the ARM by default - */ - for (i = 0; i < banks; i++) { - const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i]; - void __iomem *addr = bank_reg(gpio, bank, reg_rdata); - gpio->dcache[i] = ioread32(addr); - aspeed_gpio_change_cmd_source(gpio, bank, 0, GPIO_CMDSRC_ARM); - aspeed_gpio_change_cmd_source(gpio, bank, 1, GPIO_CMDSRC_ARM); - aspeed_gpio_change_cmd_source(gpio, bank, 2, GPIO_CMDSRC_ARM); - aspeed_gpio_change_cmd_source(gpio, bank, 3, GPIO_CMDSRC_ARM); + if (gpio->config->require_dcache) { + /* Allocate a cache of the output registers */ + banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); + gpio->dcache = devm_kcalloc(&pdev->dev, banks, sizeof(u32), GFP_KERNEL); + if (!gpio->dcache) + return -ENOMEM; + /* + * Populate it with initial values read from the HW + */ + for (i = 0; i < banks; i++) + gpio->dcache[i] = + gpio->config->llops->reg_bank_get(gpio, (i << 5), reg_rdata); } + if (gpio->config->llops->privilege_init) + gpio->config->llops->privilege_init(gpio); + /* Set up an irqchip */ irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -1268,13 +1429,14 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) } static struct platform_driver aspeed_gpio_driver = { + .probe = aspeed_gpio_probe, .driver = { .name = KBUILD_MODNAME, .of_match_table = aspeed_gpio_of_table, }, }; -module_platform_driver_probe(aspeed_gpio_driver, aspeed_gpio_probe); +module_platform_driver(aspeed_gpio_driver); MODULE_DESCRIPTION("Aspeed GPIO Driver"); MODULE_LICENSE("GPL"); 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-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 5321ef98f442..64908f1a5e7f 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -69,6 +69,22 @@ struct bcm_kona_gpio { struct bcm_kona_gpio_bank { int id; int irq; + /* + * Used to keep track of lock/unlock operations for each GPIO in the + * bank. + * + * All GPIOs are locked by default (see bcm_kona_gpio_reset), and the + * unlock count for all GPIOs is 0 by default. Each unlock increments + * the counter, and each lock decrements the counter. + * + * The lock function only locks the GPIO once its unlock counter is + * down to 0. This is necessary because the GPIO is unlocked in two + * places in this driver: once for requested GPIOs, and once for + * requested IRQs. Since it is possible for a GPIO to be requested + * as both a GPIO and an IRQ, we need to ensure that we don't lock it + * too early. + */ + u8 gpio_unlock_count[GPIO_PER_BANK]; /* Used in the interrupt handler */ struct bcm_kona_gpio *kona_gpio; }; @@ -86,14 +102,24 @@ static void bcm_kona_gpio_lock_gpio(struct bcm_kona_gpio *kona_gpio, u32 val; unsigned long flags; int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + struct bcm_kona_gpio_bank *bank = &kona_gpio->banks[bank_id]; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + if (bank->gpio_unlock_count[bit] == 0) { + dev_err(kona_gpio->gpio_chip.parent, + "Unbalanced locks for GPIO %u\n", gpio); + return; + } - val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id)); - val |= BIT(gpio); - bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val); + if (--bank->gpio_unlock_count[bit] == 0) { + raw_spin_lock_irqsave(&kona_gpio->lock, flags); - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); + val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id)); + val |= BIT(bit); + bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val); + + raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); + } } static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio, @@ -102,14 +128,20 @@ static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio, u32 val; unsigned long flags; int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + struct bcm_kona_gpio_bank *bank = &kona_gpio->banks[bank_id]; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + if (bank->gpio_unlock_count[bit] == 0) { + raw_spin_lock_irqsave(&kona_gpio->lock, flags); - val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id)); - val &= ~BIT(gpio); - bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val); + val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id)); + val &= ~BIT(bit); + bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val); - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); + raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); + } + + ++bank->gpio_unlock_count[bit]; } static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio) @@ -360,6 +392,7 @@ static void bcm_kona_gpio_irq_mask(struct irq_data *d) kona_gpio = irq_data_get_irq_chip_data(d); reg_base = kona_gpio->reg_base; + raw_spin_lock_irqsave(&kona_gpio->lock, flags); val = readl(reg_base + GPIO_INT_MASK(bank_id)); @@ -382,6 +415,7 @@ static void bcm_kona_gpio_irq_unmask(struct irq_data *d) kona_gpio = irq_data_get_irq_chip_data(d); reg_base = kona_gpio->reg_base; + raw_spin_lock_irqsave(&kona_gpio->lock, flags); val = readl(reg_base + GPIO_INT_MSKCLR(bank_id)); @@ -477,15 +511,26 @@ static void bcm_kona_gpio_irq_handler(struct irq_desc *desc) static int bcm_kona_gpio_irq_reqres(struct irq_data *d) { struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d); + unsigned int gpio = d->hwirq; - return gpiochip_reqres_irq(&kona_gpio->gpio_chip, d->hwirq); + /* + * We need to unlock the GPIO before any other operations are performed + * on the relevant GPIO configuration registers + */ + bcm_kona_gpio_unlock_gpio(kona_gpio, gpio); + + return gpiochip_reqres_irq(&kona_gpio->gpio_chip, gpio); } static void bcm_kona_gpio_irq_relres(struct irq_data *d) { struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d); + unsigned int gpio = d->hwirq; + + /* Once we no longer use it, lock the GPIO again */ + bcm_kona_gpio_lock_gpio(kona_gpio, gpio); - gpiochip_relres_irq(&kona_gpio->gpio_chip, d->hwirq); + gpiochip_relres_irq(&kona_gpio->gpio_chip, gpio); } static struct irq_chip bcm_gpio_irq_chip = { @@ -614,7 +659,7 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) bank->irq = platform_get_irq(pdev, i); bank->kona_gpio = kona_gpio; if (bank->irq < 0) { - dev_err(dev, "Couldn't get IRQ for bank %d", i); + dev_err(dev, "Couldn't get IRQ for bank %d\n", i); ret = -ENOENT; goto err_irq_domain; } diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index a789af4a5c85..491b529d25f8 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -50,7 +50,6 @@ struct brcmstb_gpio_priv { struct irq_domain *irq_domain; struct irq_chip irq_chip; int parent_irq; - int gpio_base; int num_gpios; int parent_wake_irq; }; @@ -92,7 +91,7 @@ brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank) static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct brcmstb_gpio_bank *bank) { - return hwirq - (bank->gc.base - bank->parent_priv->gpio_base); + return hwirq - bank->gc.offset; } static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, @@ -118,7 +117,7 @@ static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); /* gc_offset is relative to this gpio_chip; want real offset */ - int hwirq = offset + (gc->base - priv->gpio_base); + int hwirq = offset + gc->offset; if (hwirq >= priv->num_gpios) return -ENXIO; @@ -263,7 +262,7 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) { struct brcmstb_gpio_priv *priv = bank->parent_priv; struct irq_domain *domain = priv->irq_domain; - int hwbase = bank->gc.base - priv->gpio_base; + int hwbase = bank->gc.offset; unsigned long status; while ((status = brcmstb_gpio_get_active_irqs(bank))) { @@ -412,7 +411,7 @@ static int brcmstb_gpio_of_xlate(struct gpio_chip *gc, if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) return -EINVAL; - offset = gpiospec->args[0] - (gc->base - priv->gpio_base); + offset = gpiospec->args[0] - bank->gc.offset; if (offset >= gc->ngpio || offset < 0) return -EINVAL; @@ -592,12 +591,10 @@ 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; int err; - static int gpio_base; unsigned long flags = 0; bool need_wakeup_event = false; @@ -611,7 +608,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) if (IS_ERR(reg_base)) return PTR_ERR(reg_base); - priv->gpio_base = gpio_base; priv->reg_base = reg_base; priv->pdev = pdev; @@ -638,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; @@ -651,7 +646,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) dev_dbg(dev, "Width 0 found: Empty bank @ %d\n", num_banks); num_banks++; - gpio_base += MAX_GPIO_PER_BANK; + num_gpios += MAX_GPIO_PER_BANK; continue; } @@ -691,12 +686,13 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) err = -ENOMEM; goto fail; } - gc->base = gpio_base; gc->of_gpio_n_cells = 2; gc->of_xlate = brcmstb_gpio_of_xlate; /* not all ngpio lines are valid, will use bank width later */ gc->ngpio = MAX_GPIO_PER_BANK; gc->offset = bank->id * MAX_GPIO_PER_BANK; + gc->request = gpiochip_generic_request; + gc->free = gpiochip_generic_free; if (priv->parent_irq > 0) gc->to_irq = brcmstb_gpio_to_irq; @@ -713,7 +709,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) bank->id); goto fail; } - gpio_base += gc->ngpio; + num_gpios += gc->ngpio; dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, gc->base, gc->ngpio, bank->width); @@ -724,7 +720,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) num_banks++; } - priv->num_gpios = gpio_base - priv->gpio_base; + priv->num_gpios = num_gpios; if (priv->parent_irq > 0) { err = brcmstb_gpio_irq_setup(pdev, priv); if (err) @@ -755,7 +751,7 @@ static struct platform_driver brcmstb_gpio_driver = { .pm = &brcmstb_gpio_pm_ops, }, .probe = brcmstb_gpio_probe, - .remove_new = brcmstb_gpio_remove, + .remove = brcmstb_gpio_remove, .shutdown = brcmstb_gpio_shutdown, }; module_platform_driver(brcmstb_gpio_driver); diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index 6a439cf78459..e9dd2564c54f 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[] = { @@ -288,7 +277,7 @@ static struct platform_driver cdns_gpio_driver = { .of_match_table = cdns_of_ids, }, .probe = cdns_gpio_probe, - .remove_new = cdns_gpio_remove, + .remove = cdns_gpio_remove, }; module_platform_driver(cdns_gpio_driver); diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c new file mode 100644 index 000000000000..9213faa11522 --- /dev/null +++ b/drivers/gpio/gpio-cgbc.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Congatec Board Controller GPIO driver + * + * Copyright (C) 2024 Bootlin + * Author: Thomas Richard <thomas.richard@bootlin.com> + */ + +#include <linux/gpio/driver.h> +#include <linux/mfd/cgbc.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#define CGBC_GPIO_NGPIO 14 + +#define CGBC_GPIO_CMD_GET 0x64 +#define CGBC_GPIO_CMD_SET 0x65 +#define CGBC_GPIO_CMD_DIR_GET 0x66 +#define CGBC_GPIO_CMD_DIR_SET 0x67 + +struct cgbc_gpio_data { + struct gpio_chip chip; + struct cgbc_device_data *cgbc; + struct mutex lock; +}; + +static int cgbc_gpio_cmd(struct cgbc_device_data *cgbc, + u8 cmd0, u8 cmd1, u8 cmd2, u8 *value) +{ + u8 cmd[3] = {cmd0, cmd1, cmd2}; + + return cgbc_command(cgbc, cmd, sizeof(cmd), value, 1, NULL); +} + +static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + int ret; + u8 val; + + scoped_guard(mutex, &gpio->lock) + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); + + offset %= 8; + + if (ret) + return ret; + else + return (int)(val & (u8)BIT(offset)); +} + +static void __cgbc_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + u8 val; + int ret; + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); + if (ret) + return; + + if (value) + val |= BIT(offset % 8); + else + val &= ~(BIT(offset % 8)); + + cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val); +} + +static void cgbc_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + + scoped_guard(mutex, &gpio->lock) + __cgbc_gpio_set(chip, offset, value); +} + +static int cgbc_gpio_direction_set(struct gpio_chip *chip, + unsigned int offset, int direction) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + int ret; + u8 val; + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); + if (ret) + goto end; + + if (direction == GPIO_LINE_DIRECTION_IN) + val &= ~(BIT(offset % 8)); + else + val |= BIT(offset % 8); + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_SET, (offset > 7) ? 1 : 0, val, &val); + +end: + return ret; +} + +static int cgbc_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + + guard(mutex)(&gpio->lock); + return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_IN); +} + +static int cgbc_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + + guard(mutex)(&gpio->lock); + + __cgbc_gpio_set(chip, offset, value); + return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT); +} + +static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + int ret; + u8 val; + + scoped_guard(mutex, &gpio->lock) + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); + + if (ret) + return ret; + + if (val & BIT(offset % 8)) + return GPIO_LINE_DIRECTION_OUT; + else + return GPIO_LINE_DIRECTION_IN; +} + +static int cgbc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cgbc_device_data *cgbc = dev_get_drvdata(dev->parent); + struct cgbc_gpio_data *gpio; + struct gpio_chip *chip; + int ret; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->cgbc = cgbc; + + platform_set_drvdata(pdev, gpio); + + chip = &gpio->chip; + chip->label = dev_name(&pdev->dev); + chip->owner = THIS_MODULE; + chip->parent = dev; + chip->base = -1; + chip->direction_input = cgbc_gpio_direction_input; + chip->direction_output = cgbc_gpio_direction_output; + chip->get_direction = cgbc_gpio_get_direction; + chip->get = cgbc_gpio_get; + chip->set = cgbc_gpio_set; + chip->ngpio = CGBC_GPIO_NGPIO; + + ret = devm_mutex_init(dev, &gpio->lock); + if (ret) + return ret; + + ret = devm_gpiochip_add_data(dev, chip, gpio); + if (ret) + return dev_err_probe(dev, ret, "Could not register GPIO chip\n"); + + return 0; +} + +static struct platform_driver cgbc_gpio_driver = { + .driver = { + .name = "cgbc-gpio", + }, + .probe = cgbc_gpio_probe, +}; + +module_platform_driver(cgbc_gpio_driver); + +MODULE_DESCRIPTION("Congatec Board Controller GPIO Driver"); +MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cgbc-gpio"); diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c new file mode 100644 index 000000000000..0c09bb54dc0c --- /dev/null +++ b/drivers/gpio/gpio-cros-ec.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2024 Google LLC + * + * This driver provides the ability to control GPIOs on the Chrome OS EC. + * There isn't any direction control, and setting values on GPIOs is only + * possible when the system is unlocked. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> + +/* Prefix all names to avoid collisions with EC <-> AP nets */ +static const char cros_ec_gpio_prefix[] = "EC:"; + +/* Setting gpios is only supported when the system is unlocked */ +static void cros_ec_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_set params = { + .val = val, + }; + int ret; + ssize_t copied; + + copied = strscpy(params.name, name, sizeof(params.name)); + if (copied < 0) + return; + + ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_SET, ¶ms, + sizeof(params), NULL, 0); + if (ret < 0) + dev_err(gc->parent, "error setting gpio%d (%s) on EC: %d\n", gpio, name, ret); +} + +static int cros_ec_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_get params; + struct ec_response_gpio_get response; + int ret; + ssize_t copied; + + copied = strscpy(params.name, name, sizeof(params.name)); + if (copied < 0) + return -EINVAL; + + ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err(gc->parent, "error getting gpio%d (%s) on EC: %d\n", gpio, name, ret); + return ret; + } + + return response.val; +} + +#define CROS_EC_GPIO_INPUT BIT(8) +#define CROS_EC_GPIO_OUTPUT BIT(9) + +static int cros_ec_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_INFO, + .get_info.index = gpio, + }; + struct ec_response_gpio_get_v1 response; + int ret; + + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err(gc->parent, "error getting direction of gpio%d (%s) on EC: %d\n", gpio, name, ret); + return ret; + } + + if (response.get_info.flags & CROS_EC_GPIO_INPUT) + return GPIO_LINE_DIRECTION_IN; + + if (response.get_info.flags & CROS_EC_GPIO_OUTPUT) + return GPIO_LINE_DIRECTION_OUT; + + return -EINVAL; +} + +/* Query EC for all gpio line names */ +static int cros_ec_gpio_init_names(struct cros_ec_device *cros_ec, struct gpio_chip *gc) +{ + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_INFO, + }; + struct ec_response_gpio_get_v1 response; + int ret, i; + /* EC may not NUL terminate */ + size_t name_len = strlen(cros_ec_gpio_prefix) + sizeof(response.get_info.name) + 1; + ssize_t copied; + const char **names; + char *str; + + names = devm_kcalloc(gc->parent, gc->ngpio, sizeof(*names), GFP_KERNEL); + if (!names) + return -ENOMEM; + gc->names = names; + + str = devm_kcalloc(gc->parent, gc->ngpio, name_len, GFP_KERNEL); + if (!str) + return -ENOMEM; + + /* Get gpio line names one at a time */ + for (i = 0; i < gc->ngpio; i++) { + params.get_info.index = i; + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err_probe(gc->parent, ret, "error getting gpio%d info\n", i); + return ret; + } + + names[i] = str; + copied = scnprintf(str, name_len, "%s%s", cros_ec_gpio_prefix, + response.get_info.name); + if (copied < 0) + return copied; + + str += copied + 1; + } + + return 0; +} + +/* Query EC for number of gpios */ +static int cros_ec_gpio_ngpios(struct cros_ec_device *cros_ec) +{ + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_COUNT, + }; + struct ec_response_gpio_get_v1 response; + int ret; + + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) + return ret; + + return response.get_count.val; +} + +static int cros_ec_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + struct cros_ec_dev *ec_dev = dev_get_drvdata(parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct gpio_chip *gc; + int ngpios; + int ret; + + /* Use the fwnode from the protocol device, e.g. cros-ec-spi */ + device_set_node(dev, dev_fwnode(cros_ec->dev)); + + ngpios = cros_ec_gpio_ngpios(cros_ec); + if (ngpios < 0) { + dev_err_probe(dev, ngpios, "error getting gpio count\n"); + return ngpios; + } + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->ngpio = ngpios; + gc->parent = dev; + ret = cros_ec_gpio_init_names(cros_ec, gc); + if (ret) + return ret; + + gc->can_sleep = true; + gc->label = dev_name(dev); + gc->base = -1; + gc->set = cros_ec_gpio_set; + gc->get = cros_ec_gpio_get; + gc->get_direction = cros_ec_gpio_get_direction; + + return devm_gpiochip_add_data(dev, gc, cros_ec); +} + +static const struct platform_device_id cros_ec_gpio_id[] = { + { "cros-ec-gpio", 0 }, + {} +}; +MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id); + +static struct platform_driver cros_ec_gpio_driver = { + .probe = cros_ec_gpio_probe, + .driver = { + .name = "cros-ec-gpio", + }, + .id_table = cros_ec_gpio_id, +}; +module_platform_driver(cros_ec_gpio_driver); + +MODULE_DESCRIPTION("ChromeOS EC GPIO Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 1ee62cd58582..25db014494a4 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -92,7 +92,7 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type) case 0x5e: return GPIOPANELCTL; default: - return -EOPNOTSUPP; + return -ENOTSUPP; } } diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index bb499e362912..8c033e8cf3c9 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -15,10 +15,8 @@ #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/module.h> -#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 +152,44 @@ 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; - /* * 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 = device_property_read_u32(dev, "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 = device_property_read_u32(dev, "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 +212,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 +221,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]; @@ -284,7 +251,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) * serve as EDMA event triggers. */ -static void gpio_irq_disable(struct irq_data *d) +static void gpio_irq_mask(struct irq_data *d) { struct davinci_gpio_regs __iomem *g = irq2regs(d); uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d); @@ -293,7 +260,7 @@ static void gpio_irq_disable(struct irq_data *d) writel_relaxed(mask, &g->clr_rising); } -static void gpio_irq_enable(struct irq_data *d) +static void gpio_irq_unmask(struct irq_data *d) { struct davinci_gpio_regs __iomem *g = irq2regs(d); uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d); @@ -319,8 +286,8 @@ static int gpio_irq_type(struct irq_data *d, unsigned trigger) static struct irq_chip gpio_irqchip = { .name = "GPIO", - .irq_enable = gpio_irq_enable, - .irq_disable = gpio_irq_disable, + .irq_unmask = gpio_irq_unmask, + .irq_mask = gpio_irq_mask, .irq_set_type = gpio_irq_type, .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, }; @@ -477,13 +444,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 +462,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 +482,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 +500,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 +518,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 +550,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 +627,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 +637,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); @@ -705,7 +660,7 @@ static struct platform_driver davinci_gpio_driver = { .driver = { .name = "davinci_gpio", .pm = pm_sleep_ptr(&davinci_gpio_dev_pm_ops), - .of_match_table = of_match_ptr(davinci_gpio_ids), + .of_match_table = davinci_gpio_ids, }, }; diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 7ead1f51128a..596da59d4b13 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -512,7 +512,7 @@ static void dln2_gpio_remove(struct platform_device *pdev) static struct platform_driver dln2_gpio_driver = { .driver.name = "dln2-gpio", .probe = dln2_gpio_probe, - .remove_new = dln2_gpio_remove, + .remove = dln2_gpio_remove, }; module_platform_driver(dln2_gpio_driver); diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 798235791f70..43b667b41f5d 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -571,7 +571,6 @@ static void dwapb_get_irq(struct device *dev, struct fwnode_handle *fwnode, static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev) { - struct fwnode_handle *fwnode; struct dwapb_platform_data *pdata; struct dwapb_port_property *pp; int nports; @@ -592,7 +591,7 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev) pdata->nports = nports; i = 0; - device_for_each_child_node(dev, fwnode) { + device_for_each_child_node_scoped(dev, fwnode) { pp = &pdata->properties[i++]; pp->fwnode = fwnode; @@ -600,7 +599,6 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev) pp->idx >= DWAPB_MAX_PORTS) { dev_err(dev, "missing/invalid port index for port%d\n", i); - fwnode_handle_put(fwnode); return ERR_PTR(-EINVAL); } @@ -694,6 +692,7 @@ static const struct acpi_device_id dwapb_acpi_match[] = { {"HISI0181", GPIO_REG_OFFSET_V1}, {"APMC0D07", GPIO_REG_OFFSET_V1}, {"APMC0D81", GPIO_REG_OFFSET_V2}, + {"FUJI200A", GPIO_REG_OFFSET_V1}, { } }; MODULE_DEVICE_TABLE(acpi, dwapb_acpi_match); diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 806b88d8dfb7..d4bf8d187e16 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -10,8 +10,8 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/notifier.h> -#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/spinlock.h> /* EIC registers definition */ @@ -108,7 +108,6 @@ static struct sprd_eic *to_sprd_eic(struct notifier_block *nb) struct sprd_eic_variant_data { enum sprd_eic_type type; - u32 num_eics; }; static const char *sprd_eic_label_name[SPRD_EIC_MAX] = { @@ -118,22 +117,18 @@ static const char *sprd_eic_label_name[SPRD_EIC_MAX] = { static const struct sprd_eic_variant_data sc9860_eic_dbnc_data = { .type = SPRD_EIC_DEBOUNCE, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_latch_data = { .type = SPRD_EIC_LATCH, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_async_data = { .type = SPRD_EIC_ASYNC, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_sync_data = { .type = SPRD_EIC_SYNC, - .num_eics = 8, }; static inline void __iomem *sprd_eic_offset_base(struct sprd_eic *sprd_eic, @@ -619,9 +614,10 @@ static int sprd_eic_probe(struct platform_device *pdev) struct gpio_irq_chip *irq; struct sprd_eic *sprd_eic; struct resource *res; + u16 num_banks = 0; int ret, i; - pdata = of_device_get_match_data(dev); + pdata = device_get_match_data(dev); if (!pdata) { dev_err(dev, "No matching driver data found.\n"); return -EINVAL; @@ -652,10 +648,12 @@ static int sprd_eic_probe(struct platform_device *pdev) sprd_eic->base[i] = devm_ioremap_resource(dev, res); if (IS_ERR(sprd_eic->base[i])) return PTR_ERR(sprd_eic->base[i]); + + num_banks++; } sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type]; - sprd_eic->chip.ngpio = pdata->num_eics; + sprd_eic->chip.ngpio = num_banks * SPRD_EIC_PER_BANK_NR; sprd_eic->chip.base = -1; sprd_eic->chip.parent = dev; sprd_eic->chip.direction_input = sprd_eic_direction_input; diff --git a/drivers/gpio/gpio-elkhartlake.c b/drivers/gpio/gpio-elkhartlake.c index 887c0fe99d39..95de52d2cc63 100644 --- a/drivers/gpio/gpio-elkhartlake.c +++ b/drivers/gpio/gpio-elkhartlake.c @@ -75,4 +75,4 @@ MODULE_AUTHOR("Pandith N <pandith.n@intel.com>"); MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>"); MODULE_DESCRIPTION("Intel Elkhart Lake PSE GPIO driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(GPIO_TANGIER); +MODULE_IMPORT_NS("GPIO_TANGIER"); diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 6cedf46efec6..58d2464c07bc 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/slab.h> @@ -19,29 +20,8 @@ #include <linux/bitops.h> #include <linux/seq_file.h> -#define EP93XX_GPIO_F_INT_STATUS 0x5c -#define EP93XX_GPIO_A_INT_STATUS 0xa0 -#define EP93XX_GPIO_B_INT_STATUS 0xbc - -/* Maximum value for gpio line identifiers */ -#define EP93XX_GPIO_LINE_MAX 63 - -/* Number of GPIO chips in EP93XX */ -#define EP93XX_GPIO_CHIP_NUM 8 - -/* Maximum value for irq capable line identifiers */ -#define EP93XX_GPIO_LINE_MAX_IRQ 23 - -#define EP93XX_GPIO_A_IRQ_BASE 64 -#define EP93XX_GPIO_B_IRQ_BASE 72 -/* - * Static mapping of GPIO bank F IRQS: - * F0..F7 (16..24) to irq 80..87. - */ -#define EP93XX_GPIO_F_IRQ_BASE 80 - struct ep93xx_gpio_irq_chip { - u8 irq_offset; + void __iomem *base; u8 int_unmasked; u8 int_enabled; u8 int_type1; @@ -50,15 +30,11 @@ struct ep93xx_gpio_irq_chip { }; struct ep93xx_gpio_chip { + void __iomem *base; struct gpio_chip gc; struct ep93xx_gpio_irq_chip *eic; }; -struct ep93xx_gpio { - void __iomem *base; - struct ep93xx_gpio_chip gc[EP93XX_GPIO_CHIP_NUM]; -}; - #define to_ep93xx_gpio_chip(x) container_of(x, struct ep93xx_gpio_chip, gc) static struct ep93xx_gpio_irq_chip *to_ep93xx_gpio_irq_chip(struct gpio_chip *gc) @@ -79,25 +55,23 @@ static struct ep93xx_gpio_irq_chip *to_ep93xx_gpio_irq_chip(struct gpio_chip *gc #define EP93XX_INT_RAW_STATUS_OFFSET 0x14 #define EP93XX_INT_DEBOUNCE_OFFSET 0x18 -static void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg, - struct ep93xx_gpio_irq_chip *eic) +static void ep93xx_gpio_update_int_params(struct ep93xx_gpio_irq_chip *eic) { - writeb_relaxed(0, epg->base + eic->irq_offset + EP93XX_INT_EN_OFFSET); + writeb_relaxed(0, eic->base + EP93XX_INT_EN_OFFSET); writeb_relaxed(eic->int_type2, - epg->base + eic->irq_offset + EP93XX_INT_TYPE2_OFFSET); + eic->base + EP93XX_INT_TYPE2_OFFSET); writeb_relaxed(eic->int_type1, - epg->base + eic->irq_offset + EP93XX_INT_TYPE1_OFFSET); + eic->base + EP93XX_INT_TYPE1_OFFSET); writeb_relaxed(eic->int_unmasked & eic->int_enabled, - epg->base + eic->irq_offset + EP93XX_INT_EN_OFFSET); + eic->base + EP93XX_INT_EN_OFFSET); } static void ep93xx_gpio_int_debounce(struct gpio_chip *gc, unsigned int offset, bool enable) { - struct ep93xx_gpio *epg = gpiochip_get_data(gc); struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); int port_mask = BIT(offset); @@ -106,53 +80,43 @@ static void ep93xx_gpio_int_debounce(struct gpio_chip *gc, else eic->int_debounce &= ~port_mask; - writeb(eic->int_debounce, - epg->base + eic->irq_offset + EP93XX_INT_DEBOUNCE_OFFSET); + writeb(eic->int_debounce, eic->base + EP93XX_INT_DEBOUNCE_OFFSET); } -static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) +static u32 ep93xx_gpio_ab_irq_handler(struct gpio_chip *gc) { - struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct ep93xx_gpio *epg = gpiochip_get_data(gc); - struct irq_chip *irqchip = irq_desc_get_chip(desc); + struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); unsigned long stat; int offset; - chained_irq_enter(irqchip, desc); - - /* - * Dispatch the IRQs to the irqdomain of each A and B - * gpiochip irqdomains depending on what has fired. - * The tricky part is that the IRQ line is shared - * between bank A and B and each has their own gpiochip. - */ - stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); + stat = readb(eic->base + EP93XX_INT_STATUS_OFFSET); for_each_set_bit(offset, &stat, 8) - generic_handle_domain_irq(epg->gc[0].gc.irq.domain, - offset); + generic_handle_domain_irq(gc->irq.domain, offset); - stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS); - for_each_set_bit(offset, &stat, 8) - generic_handle_domain_irq(epg->gc[1].gc.irq.domain, - offset); + return stat; +} - chained_irq_exit(irqchip, desc); +static irqreturn_t ep93xx_ab_irq_handler(int irq, void *dev_id) +{ + return IRQ_RETVAL(ep93xx_gpio_ab_irq_handler(dev_id)); } static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) { - /* - * map discontiguous hw irq range to continuous sw irq range: - * - * IRQ_EP93XX_GPIO{0..7}MUX -> EP93XX_GPIO_LINE_F{0..7} - */ struct irq_chip *irqchip = irq_desc_get_chip(desc); - unsigned int irq = irq_desc_get_irq(desc); - int port_f_idx = (irq & 7) ^ 4; /* {20..23,48..51} -> {0..7} */ - int gpio_irq = EP93XX_GPIO_F_IRQ_BASE + port_f_idx; + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct gpio_irq_chip *gic = &gc->irq; + unsigned int parent = irq_desc_get_irq(desc); + unsigned int i; chained_irq_enter(irqchip, desc); - generic_handle_irq(gpio_irq); + for (i = 0; i < gic->num_parents; i++) + if (gic->parents[i] == parent) + break; + + if (i < gic->num_parents) + generic_handle_domain_irq(gc->irq.domain, i); + chained_irq_exit(irqchip, desc); } @@ -160,54 +124,53 @@ static void ep93xx_gpio_irq_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); - struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int port_mask = BIT(d->irq & 7); + int port_mask = BIT(irqd_to_hwirq(d)); if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { eic->int_type2 ^= port_mask; /* switch edge direction */ - ep93xx_gpio_update_int_params(epg, eic); + ep93xx_gpio_update_int_params(eic); } - writeb(port_mask, epg->base + eic->irq_offset + EP93XX_INT_EOI_OFFSET); + writeb(port_mask, eic->base + EP93XX_INT_EOI_OFFSET); } static void ep93xx_gpio_irq_mask_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); - struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int port_mask = BIT(d->irq & 7); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + int port_mask = BIT(hwirq); if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) eic->int_type2 ^= port_mask; /* switch edge direction */ eic->int_unmasked &= ~port_mask; - ep93xx_gpio_update_int_params(epg, eic); + ep93xx_gpio_update_int_params(eic); - writeb(port_mask, epg->base + eic->irq_offset + EP93XX_INT_EOI_OFFSET); - gpiochip_disable_irq(gc, irqd_to_hwirq(d)); + writeb(port_mask, eic->base + EP93XX_INT_EOI_OFFSET); + gpiochip_disable_irq(gc, hwirq); } static void ep93xx_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); - struct ep93xx_gpio *epg = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - eic->int_unmasked &= ~BIT(d->irq & 7); - ep93xx_gpio_update_int_params(epg, eic); - gpiochip_disable_irq(gc, irqd_to_hwirq(d)); + eic->int_unmasked &= ~BIT(hwirq); + ep93xx_gpio_update_int_params(eic); + gpiochip_disable_irq(gc, hwirq); } static void ep93xx_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); - struct ep93xx_gpio *epg = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - gpiochip_enable_irq(gc, irqd_to_hwirq(d)); - eic->int_unmasked |= BIT(d->irq & 7); - ep93xx_gpio_update_int_params(epg, eic); + gpiochip_enable_irq(gc, hwirq); + eic->int_unmasked |= BIT(hwirq); + ep93xx_gpio_update_int_params(eic); } /* @@ -219,12 +182,11 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); - struct ep93xx_gpio *epg = gpiochip_get_data(gc); - int offset = d->irq & 7; - int port_mask = BIT(offset); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + int port_mask = BIT(hwirq); irq_flow_handler_t handler; - gc->direction_input(gc, offset); + gc->direction_input(gc, hwirq); switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -250,7 +212,7 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) case IRQ_TYPE_EDGE_BOTH: eic->int_type1 |= port_mask; /* set initial polarity based on current input level */ - if (gc->get(gc, offset)) + if (gc->get(gc, hwirq)) eic->int_type2 &= ~port_mask; /* falling */ else eic->int_type2 |= port_mask; /* rising */ @@ -264,51 +226,11 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) eic->int_enabled |= port_mask; - ep93xx_gpio_update_int_params(epg, eic); + ep93xx_gpio_update_int_params(eic); return 0; } -/************************************************************************* - * gpiolib interface for EP93xx on-chip GPIOs - *************************************************************************/ -struct ep93xx_gpio_bank { - const char *label; - int data; - int dir; - int irq; - int base; - bool has_irq; - bool has_hierarchical_irq; - unsigned int irq_base; -}; - -#define EP93XX_GPIO_BANK(_label, _data, _dir, _irq, _base, _has_irq, _has_hier, _irq_base) \ - { \ - .label = _label, \ - .data = _data, \ - .dir = _dir, \ - .irq = _irq, \ - .base = _base, \ - .has_irq = _has_irq, \ - .has_hierarchical_irq = _has_hier, \ - .irq_base = _irq_base, \ - } - -static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { - /* Bank A has 8 IRQs */ - EP93XX_GPIO_BANK("A", 0x00, 0x10, 0x90, 0, true, false, EP93XX_GPIO_A_IRQ_BASE), - /* Bank B has 8 IRQs */ - EP93XX_GPIO_BANK("B", 0x04, 0x14, 0xac, 8, true, false, EP93XX_GPIO_B_IRQ_BASE), - EP93XX_GPIO_BANK("C", 0x08, 0x18, 0x00, 40, false, false, 0), - EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 0x00, 24, false, false, 0), - EP93XX_GPIO_BANK("E", 0x20, 0x24, 0x00, 32, false, false, 0), - /* Bank F has 8 IRQs */ - EP93XX_GPIO_BANK("F", 0x30, 0x34, 0x4c, 16, false, true, EP93XX_GPIO_F_IRQ_BASE), - EP93XX_GPIO_BANK("G", 0x38, 0x3c, 0x00, 48, false, false, 0), - EP93XX_GPIO_BANK("H", 0x40, 0x44, 0x00, 56, false, false, 0), -}; - static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, unsigned long config) { @@ -327,7 +249,7 @@ static void ep93xx_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip gpio_eic_irq_chip = { @@ -342,115 +264,112 @@ static const struct irq_chip gpio_eic_irq_chip = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; -static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, - struct platform_device *pdev, - struct ep93xx_gpio *epg, - struct ep93xx_gpio_bank *bank) +static int ep93xx_setup_irqs(struct platform_device *pdev, + struct ep93xx_gpio_chip *egc) { - void __iomem *data = epg->base + bank->data; - void __iomem *dir = epg->base + bank->dir; struct gpio_chip *gc = &egc->gc; struct device *dev = &pdev->dev; - struct gpio_irq_chip *girq; - int err; - - err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0); - if (err) - return err; - - gc->label = bank->label; - gc->base = bank->base; - - girq = &gc->irq; - if (bank->has_irq || bank->has_hierarchical_irq) { - gc->set_config = ep93xx_gpio_set_config; - egc->eic = devm_kcalloc(dev, 1, - sizeof(*egc->eic), - GFP_KERNEL); - if (!egc->eic) - return -ENOMEM; - egc->eic->irq_offset = bank->irq; - gpio_irq_chip_set_chip(girq, &gpio_eic_irq_chip); - } + struct gpio_irq_chip *girq = &gc->irq; + int ret, irq, i; + void __iomem *intr; - if (bank->has_irq) { - int ab_parent_irq = platform_get_irq(pdev, 0); - - girq->parent_handler = ep93xx_gpio_ab_irq_handler; - girq->num_parents = 1; - girq->parents = devm_kcalloc(dev, girq->num_parents, - sizeof(*girq->parents), - GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_level_irq; - girq->parents[0] = ab_parent_irq; - girq->first = bank->irq_base; - } + intr = devm_platform_ioremap_resource_byname(pdev, "intr"); + if (IS_ERR(intr)) + return PTR_ERR(intr); + + gc->set_config = ep93xx_gpio_set_config; + egc->eic = devm_kzalloc(dev, sizeof(*egc->eic), GFP_KERNEL); + if (!egc->eic) + return -ENOMEM; + + egc->eic->base = intr; + gpio_irq_chip_set_chip(girq, &gpio_eic_irq_chip); + girq->num_parents = platform_irq_count(pdev); + if (girq->num_parents == 0) + return -EINVAL; + + girq->parents = devm_kcalloc(dev, girq->num_parents, sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; - /* Only bank F has especially funky IRQ handling */ - if (bank->has_hierarchical_irq) { - int gpio_irq; - int i; + if (girq->num_parents == 1) { /* A/B irqchips */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; - /* - * FIXME: convert this to use hierarchical IRQ support! - * this requires fixing the root irqchip to be hierarchical. - */ + ret = devm_request_irq(dev, irq, ep93xx_ab_irq_handler, + IRQF_SHARED, gc->label, gc); + if (ret) + return dev_err_probe(dev, ret, "requesting IRQ: %d\n", irq); + + girq->parents[0] = irq; + } else { /* F irqchip */ girq->parent_handler = ep93xx_gpio_f_irq_handler; - girq->num_parents = 8; - girq->parents = devm_kcalloc(dev, girq->num_parents, - sizeof(*girq->parents), - GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; - /* Pick resources 1..8 for these IRQs */ + for (i = 0; i < girq->num_parents; i++) { - girq->parents[i] = platform_get_irq(pdev, i + 1); - gpio_irq = bank->irq_base + i; - irq_set_chip_data(gpio_irq, &epg->gc[5]); - irq_set_chip_and_handler(gpio_irq, - girq->chip, - handle_level_irq); - irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); + irq = platform_get_irq_optional(pdev, i); + if (irq < 0) + continue; + + girq->parents[i] = irq; } - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_level_irq; - girq->first = bank->irq_base; + + girq->map = girq->parents; } - return devm_gpiochip_add_data(dev, gc, epg); + girq->default_type = IRQ_TYPE_NONE; + /* TODO: replace with handle_bad_irq() once we are fully hierarchical */ + girq->handler = handle_simple_irq; + + return 0; } static int ep93xx_gpio_probe(struct platform_device *pdev) { - struct ep93xx_gpio *epg; - int i; - - epg = devm_kzalloc(&pdev->dev, sizeof(*epg), GFP_KERNEL); - if (!epg) + struct ep93xx_gpio_chip *egc; + struct gpio_chip *gc; + void __iomem *data; + void __iomem *dir; + int ret; + + egc = devm_kzalloc(&pdev->dev, sizeof(*egc), GFP_KERNEL); + if (!egc) return -ENOMEM; - epg->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(epg->base)) - return PTR_ERR(epg->base); - - for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { - struct ep93xx_gpio_chip *gc = &epg->gc[i]; - struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i]; - - if (ep93xx_gpio_add_bank(gc, pdev, epg, bank)) - dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", - bank->label); + data = devm_platform_ioremap_resource_byname(pdev, "data"); + if (IS_ERR(data)) + return PTR_ERR(data); + + dir = devm_platform_ioremap_resource_byname(pdev, "dir"); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + gc = &egc->gc; + ret = bgpio_init(gc, &pdev->dev, 1, data, NULL, NULL, dir, NULL, 0); + if (ret) + return dev_err_probe(&pdev->dev, ret, "unable to init generic GPIO\n"); + + gc->label = dev_name(&pdev->dev); + if (platform_irq_count(pdev) > 0) { + dev_dbg(&pdev->dev, "setting up irqs for %s\n", dev_name(&pdev->dev)); + ret = ep93xx_setup_irqs(pdev, egc); + if (ret) + dev_err_probe(&pdev->dev, ret, "setup irqs failed"); } - return 0; + return devm_gpiochip_add_data(&pdev->dev, gc, egc); } +static const struct of_device_id ep93xx_gpio_match[] = { + { .compatible = "cirrus,ep9301-gpio" }, + { /* sentinel */ } +}; + static struct platform_driver ep93xx_gpio_driver = { .driver = { .name = "gpio-ep93xx", + .of_match_table = ep93xx_gpio_match, }, .probe = ep93xx_gpio_probe, }; diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 5170fe7599cd..d5909a4f0433 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -99,11 +99,13 @@ static void exar_set_value(struct gpio_chip *chip, unsigned int offset, struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset); unsigned int bit = exar_offset_to_bit(exar_gpio, offset); + unsigned int bit_value = value ? BIT(bit) : 0; - if (value) - regmap_set_bits(exar_gpio->regmap, addr, BIT(bit)); - else - regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit)); + /* + * regmap_write_bits() forces value to be written when an external + * pull up/down might otherwise indicate value was already set. + */ + regmap_write_bits(exar_gpio->regmap, addr, BIT(bit), bit_value); } static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index 97d345b59352..c35eaa2851d8 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -253,18 +253,13 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) if (irq < 0) return irq; - g->clk = devm_clk_get(dev, NULL); - if (!IS_ERR(g->clk)) { - ret = clk_prepare_enable(g->clk); - if (ret) - return ret; - } else if (PTR_ERR(g->clk) == -EPROBE_DEFER) { + g->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(g->clk) && PTR_ERR(g->clk) == -EPROBE_DEFER) /* * Percolate deferrals, for anything else, * just live without the clocking. */ return PTR_ERR(g->clk); - } ret = bgpio_init(&g->gc, dev, 4, g->base + GPIO_DATA_IN, @@ -273,10 +268,9 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) g->base + GPIO_DIR, NULL, 0); - if (ret) { - dev_err(dev, "unable to init generic GPIO\n"); - goto dis_clk; - } + if (ret) + return dev_err_probe(dev, ret, "unable to init generic GPIO\n"); + g->gc.label = dev_name(dev); g->gc.base = -1; g->gc.parent = dev; @@ -293,10 +287,9 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), GFP_KERNEL); - if (!girq->parents) { - ret = -ENOMEM; - goto dis_clk; - } + if (!girq->parents) + return -ENOMEM; + girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; girq->parents[0] = irq; @@ -309,26 +302,7 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) /* Clear any use of debounce */ writel(0x0, g->base + GPIO_DEBOUNCE_EN); - ret = devm_gpiochip_add_data(dev, &g->gc, g); - if (ret) - goto dis_clk; - - platform_set_drvdata(pdev, g); - dev_info(dev, "FTGPIO010 @%p registered\n", g->base); - - return 0; - -dis_clk: - clk_disable_unprepare(g->clk); - - return ret; -} - -static void ftgpio_gpio_remove(struct platform_device *pdev) -{ - struct ftgpio_gpio *g = platform_get_drvdata(pdev); - - clk_disable_unprepare(g->clk); + return devm_gpiochip_add_data(dev, &g->gc, g); } static const struct of_device_id ftgpio_gpio_of_match[] = { @@ -350,6 +324,5 @@ static struct platform_driver ftgpio_gpio_driver = { .of_match_table = ftgpio_gpio_of_match, }, .probe = ftgpio_gpio_probe, - .remove_new = ftgpio_gpio_remove, }; builtin_platform_driver(ftgpio_gpio_driver); 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-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index 43d823a56e59..fb7c510bf2fa 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -18,7 +18,7 @@ #include "gpio-i8255.h" -MODULE_IMPORT_NS(I8255); +MODULE_IMPORT_NS("I8255"); #define GPIOMM_EXTENT 8 #define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT) diff --git a/drivers/gpio/gpio-graniterapids.c b/drivers/gpio/gpio-graniterapids.c new file mode 100644 index 000000000000..ad6a045fd3d2 --- /dev/null +++ b/drivers/gpio/gpio-graniterapids.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Granite Rapids-D vGPIO driver + * + * Copyright (c) 2024, Intel Corporation. + * + * Author: Aapo Vienamo <aapo.vienamo@linux.intel.com> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gfp_types.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/math.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <linux/gpio/driver.h> + +#define GNR_NUM_PINS 128 +#define GNR_PINS_PER_REG 32 +#define GNR_NUM_REGS DIV_ROUND_UP(GNR_NUM_PINS, GNR_PINS_PER_REG) + +#define GNR_CFG_PADBAR 0x00 +#define GNR_CFG_LOCK_OFFSET 0x04 +#define GNR_GPI_STATUS_OFFSET 0x14 +#define GNR_GPI_ENABLE_OFFSET 0x24 + +#define GNR_CFG_DW_HOSTSW_MODE BIT(27) +#define GNR_CFG_DW_RX_MASK GENMASK(23, 22) +#define GNR_CFG_DW_INTSEL_MASK GENMASK(21, 14) +#define GNR_CFG_DW_RX_DISABLE FIELD_PREP(GNR_CFG_DW_RX_MASK, 2) +#define GNR_CFG_DW_RX_EDGE FIELD_PREP(GNR_CFG_DW_RX_MASK, 1) +#define GNR_CFG_DW_RX_LEVEL FIELD_PREP(GNR_CFG_DW_RX_MASK, 0) +#define GNR_CFG_DW_RXDIS BIT(4) +#define GNR_CFG_DW_TXDIS BIT(3) +#define GNR_CFG_DW_RXSTATE BIT(1) +#define GNR_CFG_DW_TXSTATE BIT(0) + +/** + * struct gnr_gpio - Intel Granite Rapids-D vGPIO driver state + * @gc: GPIO controller interface + * @reg_base: base address of the GPIO registers + * @pad_base: base address of the vGPIO pad configuration registers + * @ro_bitmap: bitmap of read-only pins + * @lock: guard the registers + * @pad_backup: backup of the register state for suspend + */ +struct gnr_gpio { + struct gpio_chip gc; + void __iomem *reg_base; + void __iomem *pad_base; + DECLARE_BITMAP(ro_bitmap, GNR_NUM_PINS); + raw_spinlock_t lock; + u32 pad_backup[]; +}; + +static void __iomem *gnr_gpio_get_padcfg_addr(const struct gnr_gpio *priv, + unsigned int gpio) +{ + return priv->pad_base + gpio * sizeof(u32); +} + +static int gnr_gpio_configure_line(struct gpio_chip *gc, unsigned int gpio, + u32 clear_mask, u32 set_mask) +{ + struct gnr_gpio *priv = gpiochip_get_data(gc); + void __iomem *addr = gnr_gpio_get_padcfg_addr(priv, gpio); + u32 dw; + + if (test_bit(gpio, priv->ro_bitmap)) + return -EACCES; + + guard(raw_spinlock_irqsave)(&priv->lock); + + dw = readl(addr); + dw &= ~clear_mask; + dw |= set_mask; + writel(dw, addr); + + return 0; +} + +static int gnr_gpio_request(struct gpio_chip *gc, unsigned int gpio) +{ + struct gnr_gpio *priv = gpiochip_get_data(gc); + u32 dw; + + dw = readl(gnr_gpio_get_padcfg_addr(priv, gpio)); + if (!(dw & GNR_CFG_DW_HOSTSW_MODE)) { + dev_warn(gc->parent, "GPIO %u is not owned by host", gpio); + return -EBUSY; + } + + return 0; +} + +static int gnr_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + const struct gnr_gpio *priv = gpiochip_get_data(gc); + u32 dw; + + dw = readl(gnr_gpio_get_padcfg_addr(priv, gpio)); + + return !!(dw & GNR_CFG_DW_RXSTATE); +} + +static void gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +{ + u32 clear = 0; + u32 set = 0; + + if (value) + set = GNR_CFG_DW_TXSTATE; + else + clear = GNR_CFG_DW_TXSTATE; + + gnr_gpio_configure_line(gc, gpio, clear, set); +} + +static int gnr_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct gnr_gpio *priv = gpiochip_get_data(gc); + u32 dw; + + dw = readl(gnr_gpio_get_padcfg_addr(priv, gpio)); + + if (dw & GNR_CFG_DW_TXDIS) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int gnr_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio) +{ + return gnr_gpio_configure_line(gc, gpio, GNR_CFG_DW_RXDIS, 0); +} + +static int gnr_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, int value) +{ + u32 clear = GNR_CFG_DW_TXDIS; + u32 set = value ? GNR_CFG_DW_TXSTATE : 0; + + return gnr_gpio_configure_line(gc, gpio, clear, set); +} + +static const struct gpio_chip gnr_gpio_chip = { + .owner = THIS_MODULE, + .request = gnr_gpio_request, + .get = gnr_gpio_get, + .set = gnr_gpio_set, + .get_direction = gnr_gpio_get_direction, + .direction_input = gnr_gpio_direction_input, + .direction_output = gnr_gpio_direction_output, +}; + +static void __iomem *gnr_gpio_get_reg_addr(const struct gnr_gpio *priv, + unsigned int base, + unsigned int gpio) +{ + return priv->reg_base + base + gpio * sizeof(u32); +} + +static void gnr_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gnr_gpio *priv = gpiochip_get_data(gc); + irq_hw_number_t gpio = irqd_to_hwirq(d); + unsigned int reg_idx = gpio / GNR_PINS_PER_REG; + unsigned int bit_idx = gpio % GNR_PINS_PER_REG; + void __iomem *addr = gnr_gpio_get_reg_addr(priv, GNR_GPI_STATUS_OFFSET, reg_idx); + u32 reg; + + guard(raw_spinlock_irqsave)(&priv->lock); + + reg = readl(addr); + reg |= BIT(bit_idx); + writel(reg, addr); +} + +static void gnr_gpio_irq_mask_unmask(struct gpio_chip *gc, unsigned long gpio, bool mask) +{ + struct gnr_gpio *priv = gpiochip_get_data(gc); + unsigned int reg_idx = gpio / GNR_PINS_PER_REG; + unsigned int bit_idx = gpio % GNR_PINS_PER_REG; + void __iomem *addr = gnr_gpio_get_reg_addr(priv, GNR_GPI_ENABLE_OFFSET, reg_idx); + u32 reg; + + guard(raw_spinlock_irqsave)(&priv->lock); + + reg = readl(addr); + if (mask) + reg &= ~BIT(bit_idx); + else + reg |= BIT(bit_idx); + writel(reg, addr); +} + +static void gnr_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gnr_gpio_irq_mask_unmask(gc, hwirq, true); + gpiochip_disable_irq(gc, hwirq); +} + +static void gnr_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + gnr_gpio_irq_mask_unmask(gc, hwirq, false); +} + +static int gnr_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gnr_gpio *priv = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 reg; + u32 set; + + /* Allow interrupts only if Interrupt Select field is non-zero */ + reg = readl(gnr_gpio_get_padcfg_addr(priv, hwirq)); + if (!(reg & GNR_CFG_DW_INTSEL_MASK)) { + dev_dbg(gc->parent, "GPIO %lu cannot be used as IRQ", hwirq); + return -EPERM; + } + + /* Falling edge and level low triggers not supported by the GPIO controller */ + switch (type) { + case IRQ_TYPE_NONE: + set = GNR_CFG_DW_RX_DISABLE; + break; + case IRQ_TYPE_EDGE_RISING: + set = GNR_CFG_DW_RX_EDGE; + irq_set_handler_locked(d, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + set = GNR_CFG_DW_RX_LEVEL; + irq_set_handler_locked(d, handle_level_irq); + break; + default: + return -EINVAL; + } + + return gnr_gpio_configure_line(gc, hwirq, GNR_CFG_DW_RX_MASK, set); +} + +static const struct irq_chip gnr_gpio_irq_chip = { + .name = "gpio-graniterapids", + .irq_ack = gnr_gpio_irq_ack, + .irq_mask = gnr_gpio_irq_mask, + .irq_unmask = gnr_gpio_irq_unmask, + .irq_set_type = gnr_gpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void gnr_gpio_init_pin_ro_bits(struct device *dev, + const void __iomem *cfg_lock_base, + unsigned long *ro_bitmap) +{ + u32 tmp[GNR_NUM_REGS]; + + memcpy_fromio(tmp, cfg_lock_base, sizeof(tmp)); + bitmap_from_arr32(ro_bitmap, tmp, GNR_NUM_PINS); +} + +static irqreturn_t gnr_gpio_irq(int irq, void *data) +{ + struct gnr_gpio *priv = data; + unsigned int handled = 0; + + for (unsigned int i = 0; i < GNR_NUM_REGS; i++) { + const void __iomem *reg = priv->reg_base + i * sizeof(u32); + unsigned long pending; + unsigned long enabled; + unsigned int bit_idx; + + scoped_guard(raw_spinlock, &priv->lock) { + pending = readl(reg + GNR_GPI_STATUS_OFFSET); + enabled = readl(reg + GNR_GPI_ENABLE_OFFSET); + } + + /* Only enabled interrupts */ + pending &= enabled; + + for_each_set_bit(bit_idx, &pending, GNR_PINS_PER_REG) { + unsigned int hwirq = i * GNR_PINS_PER_REG + bit_idx; + + generic_handle_domain_irq(priv->gc.irq.domain, hwirq); + } + + handled += pending ? 1 : 0; + + } + return IRQ_RETVAL(handled); +} + +static int gnr_gpio_probe(struct platform_device *pdev) +{ + size_t num_backup_pins = IS_ENABLED(CONFIG_PM_SLEEP) ? GNR_NUM_PINS : 0; + struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; + struct gnr_gpio *priv; + void __iomem *regs; + int irq, ret; + u32 offset; + + priv = devm_kzalloc(dev, struct_size(priv, pad_backup, num_backup_pins), GFP_KERNEL); + 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); + + priv->reg_base = regs; + offset = readl(priv->reg_base + GNR_CFG_PADBAR); + priv->pad_base = priv->reg_base + offset; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, gnr_gpio_irq, IRQF_SHARED | IRQF_NO_THREAD, + dev_name(dev), priv); + if (ret) + return dev_err_probe(dev, ret, "failed to request interrupt\n"); + + gnr_gpio_init_pin_ro_bits(dev, priv->reg_base + GNR_CFG_LOCK_OFFSET, + priv->ro_bitmap); + + priv->gc = gnr_gpio_chip; + priv->gc.label = dev_name(dev); + priv->gc.parent = dev; + priv->gc.ngpio = GNR_NUM_PINS; + priv->gc.base = -1; + + girq = &priv->gc.irq; + gpio_irq_chip_set_chip(girq, &gnr_gpio_irq_chip); + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + + platform_set_drvdata(pdev, priv); + + return devm_gpiochip_add_data(dev, &priv->gc, priv); +} + +static int gnr_gpio_suspend(struct device *dev) +{ + struct gnr_gpio *priv = dev_get_drvdata(dev); + unsigned int i; + + guard(raw_spinlock_irqsave)(&priv->lock); + + for_each_clear_bit(i, priv->ro_bitmap, priv->gc.ngpio) + priv->pad_backup[i] = readl(gnr_gpio_get_padcfg_addr(priv, i)); + + return 0; +} + +static int gnr_gpio_resume(struct device *dev) +{ + struct gnr_gpio *priv = dev_get_drvdata(dev); + unsigned int i; + + guard(raw_spinlock_irqsave)(&priv->lock); + + for_each_clear_bit(i, priv->ro_bitmap, priv->gc.ngpio) + writel(priv->pad_backup[i], gnr_gpio_get_padcfg_addr(priv, i)); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(gnr_gpio_pm_ops, gnr_gpio_suspend, gnr_gpio_resume); + +static const struct acpi_device_id gnr_gpio_acpi_match[] = { + { "INTC1109" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, gnr_gpio_acpi_match); + +static struct platform_driver gnr_gpio_driver = { + .driver = { + .name = "gpio-graniterapids", + .pm = pm_sleep_ptr(&gnr_gpio_pm_ops), + .acpi_match_table = gnr_gpio_acpi_match, + }, + .probe = gnr_gpio_probe, +}; +module_platform_driver(gnr_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aapo Vienamo <aapo.vienamo@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Granite Rapids-D vGPIO driver"); diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 017c7170eb57..169f33c41c59 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -16,20 +16,20 @@ * Contributors: Andreas Larsson <andreas@gaisler.com> */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/gpio/driver.h> -#include <linux/slab.h> +#include <linux/bitops.h> #include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/irq.h> #include <linux/irqdomain.h> -#include <linux/bitops.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> #define GRGPIO_MAX_NGPIO 32 @@ -318,6 +318,13 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); } +static void grgpio_irq_domain_remove(void *data) +{ + struct irq_domain *domain = data; + + irq_domain_remove(domain); +} + static const struct irq_domain_ops grgpio_irq_domain_ops = { .map = grgpio_irq_map, .unmap = grgpio_irq_unmap, @@ -328,6 +335,7 @@ static const struct irq_domain_ops grgpio_irq_domain_ops = { static int grgpio_probe(struct platform_device *ofdev) { struct device_node *np = ofdev->dev.of_node; + struct device *dev = &ofdev->dev; void __iomem *regs; struct gpio_chip *gc; struct grgpio_priv *priv; @@ -337,7 +345,7 @@ static int grgpio_probe(struct platform_device *ofdev) int size; int i; - priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -346,28 +354,31 @@ static int grgpio_probe(struct platform_device *ofdev) return PTR_ERR(regs); gc = &priv->gc; - err = bgpio_init(gc, &ofdev->dev, 4, regs + GRGPIO_DATA, + err = bgpio_init(gc, dev, 4, regs + GRGPIO_DATA, regs + GRGPIO_OUTPUT, NULL, regs + GRGPIO_DIR, NULL, BGPIOF_BIG_ENDIAN_BYTE_ORDER); if (err) { - dev_err(&ofdev->dev, "bgpio_init() failed\n"); + dev_err(dev, "bgpio_init() failed\n"); return err; } priv->regs = regs; priv->imask = gc->read_reg(regs + GRGPIO_IMASK); - priv->dev = &ofdev->dev; + priv->dev = dev; gc->owner = THIS_MODULE; gc->to_irq = grgpio_to_irq; - gc->label = devm_kasprintf(&ofdev->dev, GFP_KERNEL, "%pOF", np); + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); + if (!gc->label) + return -ENOMEM; + gc->base = -1; err = of_property_read_u32(np, "nbits", &prop); if (err || prop <= 0 || prop > GRGPIO_MAX_NGPIO) { gc->ngpio = GRGPIO_MAX_NGPIO; - dev_dbg(&ofdev->dev, - "No or invalid nbits property: assume %d\n", gc->ngpio); + dev_dbg(dev, "No or invalid nbits property: assume %d\n", + gc->ngpio); } else { gc->ngpio = prop; } @@ -379,7 +390,7 @@ static int grgpio_probe(struct platform_device *ofdev) irqmap = (s32 *)of_get_property(np, "irqmap", &size); if (irqmap) { if (size < gc->ngpio) { - dev_err(&ofdev->dev, + dev_err(dev, "irqmap shorter than ngpio (%d < %d)\n", size, gc->ngpio); return -EINVAL; @@ -389,10 +400,15 @@ static int grgpio_probe(struct platform_device *ofdev) &grgpio_irq_domain_ops, priv); if (!priv->domain) { - dev_err(&ofdev->dev, "Could not add irq domain\n"); + dev_err(dev, "Could not add irq domain\n"); return -EINVAL; } + err = devm_add_action_or_reset(dev, grgpio_irq_domain_remove, + priv->domain); + if (err) + return err; + for (i = 0; i < gc->ngpio; i++) { struct grgpio_lirq *lirq; int ret; @@ -415,32 +431,18 @@ static int grgpio_probe(struct platform_device *ofdev) } } - platform_set_drvdata(ofdev, priv); - - err = gpiochip_add_data(gc, priv); + err = devm_gpiochip_add_data(dev, gc, priv); if (err) { - dev_err(&ofdev->dev, "Could not add gpiochip\n"); - if (priv->domain) - irq_domain_remove(priv->domain); + dev_err(dev, "Could not add gpiochip\n"); return err; } - dev_info(&ofdev->dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n", + dev_info(dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n", priv->regs, gc->base, gc->ngpio, priv->domain ? "on" : "off"); return 0; } -static void grgpio_remove(struct platform_device *ofdev) -{ - struct grgpio_priv *priv = platform_get_drvdata(ofdev); - - gpiochip_remove(&priv->gc); - - if (priv->domain) - irq_domain_remove(priv->domain); -} - static const struct of_device_id grgpio_match[] = { {.name = "GAISLER_GPIO"}, {.name = "01_01a"}, @@ -455,7 +457,6 @@ static struct platform_driver grgpio_driver = { .of_match_table = grgpio_match, }, .probe = grgpio_probe, - .remove_new = grgpio_remove, }; module_platform_driver(grgpio_driver); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 899335da93c7..7e29a2d8de1a 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -130,5 +130,6 @@ static struct i2c_driver gw_pld_driver = { }; module_i2c_driver(gw_pld_driver); +MODULE_DESCRIPTION("Gateworks I2C PLD GPIO expander"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index 1bcfc1835dae..0580f6712bea 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -210,7 +210,7 @@ static void hlwd_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) struct hlwd_gpio *hlwd = gpiochip_get_data(irq_data_get_irq_chip_data(data)); - seq_printf(p, dev_name(hlwd->dev)); + seq_puts(p, dev_name(hlwd->dev)); } static const struct irq_chip hlwd_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-i8255.c b/drivers/gpio/gpio-i8255.c index 64ab80fc4a1e..953018bfa2b1 100644 --- a/drivers/gpio/gpio-i8255.c +++ b/drivers/gpio/gpio-i8255.c @@ -134,7 +134,7 @@ int devm_i8255_regmap_register(struct device *const dev, return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); } -EXPORT_SYMBOL_NS_GPL(devm_i8255_regmap_register, I8255); +EXPORT_SYMBOL_NS_GPL(devm_i8255_regmap_register, "I8255"); MODULE_AUTHOR("William Breathitt Gray"); MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface"); diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c index 53b1eb876a12..0103be977c66 100644 --- a/drivers/gpio/gpio-idio-16.c +++ b/drivers/gpio/gpio-idio-16.c @@ -3,6 +3,9 @@ * GPIO library for the ACCES IDIO-16 family * Copyright (C) 2022 William Breathitt Gray */ + +#define DEFAULT_SYMBOL_NAMESPACE "GPIO_IDIO_16" + #include <linux/bits.h> #include <linux/device.h> #include <linux/err.h> @@ -14,8 +17,6 @@ #include "gpio-idio-16.h" -#define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16 - #define IDIO_16_DAT_BASE 0x0 #define IDIO_16_OUT_BASE IDIO_16_DAT_BASE #define IDIO_16_IN_BASE (IDIO_16_DAT_BASE + 1) 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-ljca.c b/drivers/gpio/gpio-ljca.c index dfec9fbfc7a9..817ecb12d550 100644 --- a/drivers/gpio/gpio-ljca.c +++ b/drivers/gpio/gpio-ljca.c @@ -82,9 +82,9 @@ static int ljca_gpio_config(struct ljca_gpio_dev *ljca_gpio, u8 gpio_id, int ret; mutex_lock(&ljca_gpio->trans_lock); + packet->num = 1; packet->item[0].index = gpio_id; packet->item[0].value = config | ljca_gpio->connect_mode[gpio_id]; - packet->num = 1; ret = ljca_transfer(ljca_gpio->ljca, LJCA_GPIO_CONFIG, (u8 *)packet, struct_size(packet, item, packet->num), NULL, 0); @@ -420,8 +420,14 @@ static int ljca_gpio_probe(struct auxiliary_device *auxdev, if (!ljca_gpio->connect_mode) return -ENOMEM; - mutex_init(&ljca_gpio->irq_lock); - mutex_init(&ljca_gpio->trans_lock); + ret = devm_mutex_init(&auxdev->dev, &ljca_gpio->irq_lock); + if (ret) + return ret; + + ret = devm_mutex_init(&auxdev->dev, &ljca_gpio->trans_lock); + if (ret) + return ret; + ljca_gpio->gc.direction_input = ljca_gpio_direction_input; ljca_gpio->gc.direction_output = ljca_gpio_direction_output; ljca_gpio->gc.get_direction = ljca_gpio_get_direction; @@ -453,11 +459,8 @@ static int ljca_gpio_probe(struct auxiliary_device *auxdev, INIT_WORK(&ljca_gpio->work, ljca_gpio_async); ret = gpiochip_add_data(&ljca_gpio->gc, ljca_gpio); - if (ret) { + if (ret) ljca_unregister_event_cb(ljca); - mutex_destroy(&ljca_gpio->irq_lock); - mutex_destroy(&ljca_gpio->trans_lock); - } return ret; } @@ -469,8 +472,6 @@ static void ljca_gpio_remove(struct auxiliary_device *auxdev) gpiochip_remove(&ljca_gpio->gc); ljca_unregister_event_cb(ljca_gpio->ljca); cancel_work_sync(&ljca_gpio->work); - mutex_destroy(&ljca_gpio->irq_lock); - mutex_destroy(&ljca_gpio->trans_lock); } static const struct auxiliary_device_id ljca_gpio_id_table[] = { @@ -491,4 +492,4 @@ MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>"); MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>"); MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-GPIO driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(LJCA); +MODULE_IMPORT_NS("LJCA"); diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index 6749d4dd6d64..7f4d78fd800e 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -237,9 +237,9 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = { .label = "ls2k2000_gpio", .mode = BIT_CTRL_MODE, - .conf_offset = 0x84, - .in_offset = 0x88, - .out_offset = 0x80, + .conf_offset = 0x4, + .in_offset = 0x8, + .out_offset = 0x0, }; static const struct loongson_gpio_chip_data loongson_gpio_ls3a5000_data = { diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index 5c6bb57a8c99..2cf9fb4637a2 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[] = { @@ -399,7 +388,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_gpio_match); static struct platform_driver lpc18xx_gpio_driver = { .probe = lpc18xx_gpio_probe, - .remove_new = lpc18xx_gpio_remove, + .remove = lpc18xx_gpio_remove, .driver = { .name = "lpc18xx-gpio", .of_match_table = lpc18xx_gpio_match, diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 5ef8af824980..c097e310c9e8 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -529,6 +529,7 @@ static const struct of_device_id lpc32xx_gpio_of_match[] = { { .compatible = "nxp,lpc3220-gpio", }, { }, }; +MODULE_DEVICE_TABLE(of, lpc32xx_gpio_of_match); static struct platform_driver lpc32xx_gpio_driver = { .driver = { diff --git a/drivers/gpio/gpio-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-max730x.c b/drivers/gpio/gpio-max730x.c index 701795b9d329..e688c13c8cc3 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -165,7 +165,10 @@ int __max730x_probe(struct max7301 *ts) pdata = dev_get_platdata(dev); - mutex_init(&ts->lock); + ret = devm_mutex_init(ts->dev, &ts->lock); + if (ret) + return ret; + dev_set_drvdata(dev, ts); /* Power up the chip and disable IRQ output */ @@ -206,17 +209,11 @@ int __max730x_probe(struct max7301 *ts) int offset = (i - 1) * 4 + j; ret = max7301_direction_input(&ts->chip, offset); if (ret) - goto exit_destroy; + return ret; } } - ret = gpiochip_add_data(&ts->chip, ts); - if (!ret) - return ret; - -exit_destroy: - mutex_destroy(&ts->lock); - return ret; + return devm_gpiochip_add_data(ts->dev, &ts->chip, ts); } EXPORT_SYMBOL_GPL(__max730x_probe); @@ -226,8 +223,6 @@ void __max730x_remove(struct device *dev) /* Power down the chip and disable IRQ output */ ts->write(dev, 0x04, 0x00); - gpiochip_remove(&ts->chip); - mutex_destroy(&ts->lock); } EXPORT_SYMBOL_GPL(__max730x_remove); diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 7fb298b4571b..7ee891ef6905 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; }; @@ -146,8 +145,6 @@ static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) irq = platform_get_irq(to_platform_device(gc->parent), index); if (irq < 0) return irq; - if (irq == 0) - break; if (irq_get_irq_data(irq)->hwirq == offset) return irq; } @@ -157,6 +154,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 +167,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 +187,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 +202,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[] = { @@ -234,7 +225,7 @@ static struct platform_driver mb86s70_gpio_driver = { .acpi_match_table = ACPI_PTR(mb86s70_gpio_acpi_ids), }, .probe = mb86s70_gpio_probe, - .remove_new = mb86s70_gpio_remove, + .remove = mb86s70_gpio_remove, }; module_platform_driver(mb86s70_gpio_driver); diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index cd9b16dbe1a9..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; @@ -168,5 +168,6 @@ static void __exit mc33880_exit(void) module_exit(mc33880_exit); MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); +MODULE_DESCRIPTION("MC33880 high-side/low-side switch GPIO driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c index a035a9bcb57c..ebe5da4933bc 100644 --- a/drivers/gpio/gpio-menz127.c +++ b/drivers/gpio/gpio-menz127.c @@ -127,6 +127,13 @@ static int men_z127_set_config(struct gpio_chip *gc, unsigned offset, return -ENOTSUPP; } +static void men_z127_release_mem(void *data) +{ + struct resource *res = data; + + mcb_release_mem(res); +} + static int men_z127_probe(struct mcb_device *mdev, const struct mcb_device_id *id) { @@ -140,17 +147,19 @@ static int men_z127_probe(struct mcb_device *mdev, return -ENOMEM; men_z127_gpio->mem = mcb_request_mem(mdev, dev_name(dev)); - if (IS_ERR(men_z127_gpio->mem)) { - dev_err(dev, "failed to request device memory"); - return PTR_ERR(men_z127_gpio->mem); - } + if (IS_ERR(men_z127_gpio->mem)) + return dev_err_probe(dev, PTR_ERR(men_z127_gpio->mem), + "failed to request device memory"); - men_z127_gpio->reg_base = ioremap(men_z127_gpio->mem->start, - resource_size(men_z127_gpio->mem)); - if (men_z127_gpio->reg_base == NULL) { - ret = -ENXIO; - goto err_release; - } + ret = devm_add_action_or_reset(dev, men_z127_release_mem, + men_z127_gpio->mem); + if (ret) + return ret; + + men_z127_gpio->reg_base = devm_ioremap(dev, men_z127_gpio->mem->start, + resource_size(men_z127_gpio->mem)); + if (men_z127_gpio->reg_base == NULL) + return -ENXIO; mcb_set_drvdata(mdev, men_z127_gpio); @@ -161,34 +170,16 @@ static int men_z127_probe(struct mcb_device *mdev, men_z127_gpio->reg_base + MEN_Z127_GPIODR, NULL, 0); if (ret) - goto err_unmap; + return ret; men_z127_gpio->gc.set_config = men_z127_set_config; - ret = gpiochip_add_data(&men_z127_gpio->gc, men_z127_gpio); - if (ret) { - dev_err(dev, "failed to register MEN 16Z127 GPIO controller"); - goto err_unmap; - } - - dev_info(dev, "MEN 16Z127 GPIO driver registered"); + ret = devm_gpiochip_add_data(dev, &men_z127_gpio->gc, men_z127_gpio); + if (ret) + return dev_err_probe(dev, ret, + "failed to register MEN 16Z127 GPIO controller"); return 0; - -err_unmap: - iounmap(men_z127_gpio->reg_base); -err_release: - mcb_release_mem(men_z127_gpio->mem); - return ret; -} - -static void men_z127_remove(struct mcb_device *mdev) -{ - struct men_z127_gpio *men_z127_gpio = mcb_get_drvdata(mdev); - - gpiochip_remove(&men_z127_gpio->gc); - iounmap(men_z127_gpio->reg_base); - mcb_release_mem(men_z127_gpio->mem); } static const struct mcb_device_id men_z127_ids[] = { @@ -202,7 +193,6 @@ static struct mcb_driver men_z127_driver = { .name = "z127-gpio", }, .probe = men_z127_probe, - .remove = men_z127_remove, .id_table = men_z127_ids, }; module_mcb_driver(men_z127_driver); @@ -211,4 +201,4 @@ MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); MODULE_DESCRIPTION("MEN 16z127 GPIO Controller"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mcb:16z127"); -MODULE_IMPORT_NS(MCB); +MODULE_IMPORT_NS("MCB"); diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 421d7e3a6c66..4335a5d8e4f6 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -78,24 +78,25 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id if (retval) return retval; - retval = pcim_iomap_regions(pdev, BIT(1) | BIT(0), pci_name(pdev)); - if (retval) - return dev_err_probe(dev, retval, "I/O memory mapping error\n"); - - base = pcim_iomap_table(pdev)[1]; + base = pcim_iomap_region(pdev, 1, pci_name(pdev)); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "I/O memory mapping error\n"); irq_base = readl(base + 0 * sizeof(u32)); gpio_base = readl(base + 1 * sizeof(u32)); /* Release the IO mapping, since we already get the info from BAR1 */ - pcim_iounmap_regions(pdev, BIT(1)); + pcim_iounmap_region(pdev, 1); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; - priv->reg_base = pcim_iomap_table(pdev)[0]; + priv->reg_base = pcim_iomap_region(pdev, 0, pci_name(pdev)); + if (IS_ERR(priv->reg_base)) + return dev_err_probe(dev, PTR_ERR(priv->reg_base), + "I/O memory mapping error\n"); priv->pin_info.pin_ranges = mrfld_gpio_ranges; priv->pin_info.nranges = ARRAY_SIZE(mrfld_gpio_ranges); @@ -141,4 +142,4 @@ module_pci_driver(mrfld_gpio_driver); MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_DESCRIPTION("Intel Merrifield SoC GPIO driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(GPIO_TANGIER); +MODULE_IMPORT_NS("GPIO_TANGIER"); diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 6abe01bc39c3..6f3dda6b635f 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -331,7 +331,7 @@ static void mlxbf2_gpio_irq_print_chip(struct irq_data *irqd, struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); - seq_printf(p, dev_name(gs->dev)); + seq_puts(p, dev_name(gs->dev)); } static const struct irq_chip mlxbf2_gpio_irq_chip = { 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-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index e855c68c981b..14ae25783438 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -136,7 +136,7 @@ MODULE_DEVICE_TABLE(of, ltq_mm_match); static struct platform_driver ltq_mm_driver = { .probe = ltq_mm_probe, - .remove_new = ltq_mm_remove, + .remove = ltq_mm_remove, .driver = { .name = "gpio-mm-ltq", .of_match_table = ltq_mm_match, diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 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-mockup.c b/drivers/gpio/gpio-mockup.c index 455eecf6380e..d39c6618bade 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -347,7 +347,6 @@ static const struct file_operations gpio_mockup_debugfs_ops = { .open = gpio_mockup_debugfs_open, .read = gpio_mockup_debugfs_read, .write = gpio_mockup_debugfs_write, - .llseek = no_llseek, .release = single_release, }; diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c index a199dce3394a..091d96f2d682 100644 --- a/drivers/gpio/gpio-mpc5200.c +++ b/drivers/gpio/gpio-mpc5200.c @@ -183,7 +183,7 @@ static struct platform_driver mpc52xx_wkup_gpiochip_driver = { .of_match_table = mpc52xx_wkup_gpiochip_match, }, .probe = mpc52xx_wkup_gpiochip_probe, - .remove_new = mpc52xx_gpiochip_remove, + .remove = mpc52xx_gpiochip_remove, }; /* @@ -336,7 +336,7 @@ static struct platform_driver mpc52xx_simple_gpiochip_driver = { .of_match_table = mpc52xx_simple_gpiochip_match, }, .probe = mpc52xx_simple_gpiochip_probe, - .remove_new = mpc52xx_gpiochip_remove, + .remove = mpc52xx_gpiochip_remove, }; static struct platform_driver * const drivers[] = { diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index c0125ac73906..0cd4c36ae8aa 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -7,19 +7,20 @@ */ #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/of.h> -#include <linux/property.h> +#include <linux/irq.h> +#include <linux/kernel.h> #include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/property.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 @@ -284,6 +285,7 @@ static const struct mpc8xxx_gpio_devtype mpc8xxx_gpio_devtype_default = { }; static const struct of_device_id mpc8xxx_gpio_ids[] = { + { .compatible = "fsl,mpc8314-gpio", }, { .compatible = "fsl,mpc8349-gpio", }, { .compatible = "fsl,mpc8572-gpio", .data = &mpc8572_gpio_devtype, }, { .compatible = "fsl,mpc8610-gpio", }, @@ -298,14 +300,14 @@ static const struct of_device_id mpc8xxx_gpio_ids[] = { static int mpc8xxx_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; - struct mpc8xxx_gpio_chip *mpc8xxx_gc; - struct gpio_chip *gc; const struct mpc8xxx_gpio_devtype *devtype = NULL; + struct mpc8xxx_gpio_chip *mpc8xxx_gc; + struct device *dev = &pdev->dev; struct fwnode_handle *fwnode; + struct gpio_chip *gc; int ret; - mpc8xxx_gc = devm_kzalloc(&pdev->dev, sizeof(*mpc8xxx_gc), GFP_KERNEL); + mpc8xxx_gc = devm_kzalloc(dev, sizeof(*mpc8xxx_gc), GFP_KERNEL); if (!mpc8xxx_gc) return -ENOMEM; @@ -318,32 +320,28 @@ static int mpc8xxx_probe(struct platform_device *pdev) return PTR_ERR(mpc8xxx_gc->regs); gc = &mpc8xxx_gc->gc; - gc->parent = &pdev->dev; - - if (device_property_read_bool(&pdev->dev, "little-endian")) { - ret = bgpio_init(gc, &pdev->dev, 4, - mpc8xxx_gc->regs + GPIO_DAT, - NULL, NULL, - mpc8xxx_gc->regs + GPIO_DIR, NULL, - BGPIOF_BIG_ENDIAN); + gc->parent = dev; + + if (device_property_read_bool(dev, "little-endian")) { + ret = bgpio_init(gc, dev, 4, mpc8xxx_gc->regs + GPIO_DAT, + NULL, NULL, mpc8xxx_gc->regs + GPIO_DIR, + NULL, BGPIOF_BIG_ENDIAN); if (ret) return ret; - dev_dbg(&pdev->dev, "GPIO registers are LITTLE endian\n"); + dev_dbg(dev, "GPIO registers are LITTLE endian\n"); } else { - ret = bgpio_init(gc, &pdev->dev, 4, - mpc8xxx_gc->regs + GPIO_DAT, - NULL, NULL, - mpc8xxx_gc->regs + GPIO_DIR, NULL, - BGPIOF_BIG_ENDIAN + ret = bgpio_init(gc, dev, 4, mpc8xxx_gc->regs + GPIO_DAT, + NULL, NULL, mpc8xxx_gc->regs + GPIO_DIR, + NULL, BGPIOF_BIG_ENDIAN | BGPIOF_BIG_ENDIAN_BYTE_ORDER); if (ret) return ret; - dev_dbg(&pdev->dev, "GPIO registers are BIG endian\n"); + dev_dbg(dev, "GPIO registers are BIG endian\n"); } mpc8xxx_gc->direction_output = gc->direction_output; - devtype = device_get_match_data(&pdev->dev); + devtype = device_get_match_data(dev); if (!devtype) devtype = &mpc8xxx_gpio_devtype_default; @@ -368,10 +366,10 @@ static int mpc8xxx_probe(struct platform_device *pdev) * associated input enable must be set (GPIOxGPIE[IEn]=1) to propagate * the port value to the GPIO Data Register. */ - fwnode = dev_fwnode(&pdev->dev); - if (of_device_is_compatible(np, "fsl,qoriq-gpio") || - of_device_is_compatible(np, "fsl,ls1028a-gpio") || - of_device_is_compatible(np, "fsl,ls1088a-gpio") || + fwnode = dev_fwnode(dev); + if (device_is_compatible(dev, "fsl,qoriq-gpio") || + device_is_compatible(dev, "fsl,ls1028a-gpio") || + device_is_compatible(dev, "fsl,ls1088a-gpio") || is_acpi_node(fwnode)) { gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff); /* Also, latch state of GPIOs configured as output by bootloader. */ @@ -379,9 +377,9 @@ static int mpc8xxx_probe(struct platform_device *pdev) gc->read_reg(mpc8xxx_gc->regs + GPIO_DIR); } - ret = devm_gpiochip_add_data(&pdev->dev, gc, mpc8xxx_gc); + ret = devm_gpiochip_add_data(dev, gc, mpc8xxx_gc); if (ret) { - dev_err(&pdev->dev, + dev_err(dev, "GPIO chip registration failed with status %d\n", ret); return ret; } @@ -402,17 +400,18 @@ static int mpc8xxx_probe(struct platform_device *pdev) gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff); gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0); - ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn, + ret = devm_request_irq(dev, mpc8xxx_gc->irqn, mpc8xxx_gpio_irq_cascade, IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade", mpc8xxx_gc); if (ret) { - dev_err(&pdev->dev, - "failed to devm_request_irq(%d), ret = %d\n", + dev_err(dev, "failed to devm_request_irq(%d), ret = %d\n", mpc8xxx_gc->irqn, ret); goto err; } + device_init_wakeup(dev, true); + return 0; err: irq_domain_remove(mpc8xxx_gc->irq); @@ -429,6 +428,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",}, @@ -439,11 +461,12 @@ MODULE_DEVICE_TABLE(acpi, gpio_acpi_ids); static struct platform_driver mpc8xxx_plat_driver = { .probe = mpc8xxx_probe, - .remove_new = mpc8xxx_remove, + .remove = mpc8xxx_remove, .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-mpfs.c b/drivers/gpio/gpio-mpfs.c new file mode 100644 index 000000000000..561a961c97a6 --- /dev/null +++ b/drivers/gpio/gpio-mpfs.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: (GPL-2.0) +/* + * Microchip PolarFire SoC (MPFS) GPIO controller driver + * + * Copyright (c) 2018-2024 Microchip Technology Inc. and its subsidiaries + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/spinlock.h> + +#define MPFS_GPIO_CTRL(i) (0x4 * (i)) +#define MPFS_MAX_NUM_GPIO 32 +#define MPFS_GPIO_EN_INT 3 +#define MPFS_GPIO_EN_OUT_BUF BIT(2) +#define MPFS_GPIO_EN_IN BIT(1) +#define MPFS_GPIO_EN_OUT BIT(0) +#define MPFS_GPIO_DIR_MASK GENMASK(2, 0) + +#define MPFS_GPIO_TYPE_INT_EDGE_BOTH 0x80 +#define MPFS_GPIO_TYPE_INT_EDGE_NEG 0x60 +#define MPFS_GPIO_TYPE_INT_EDGE_POS 0x40 +#define MPFS_GPIO_TYPE_INT_LEVEL_LOW 0x20 +#define MPFS_GPIO_TYPE_INT_LEVEL_HIGH 0x00 +#define MPFS_GPIO_TYPE_INT_MASK GENMASK(7, 5) +#define MPFS_IRQ_REG 0x80 + +#define MPFS_INP_REG 0x84 +#define COREGPIO_INP_REG 0x90 +#define MPFS_OUTP_REG 0x88 +#define COREGPIO_OUTP_REG 0xA0 + +struct mpfs_gpio_reg_offsets { + u8 inp; + u8 outp; +}; + +struct mpfs_gpio_chip { + struct regmap *regs; + const struct mpfs_gpio_reg_offsets *offsets; + struct gpio_chip gc; +}; + +static const struct regmap_config mpfs_gpio_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), + MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN); + + return 0; +} + +static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_index, int value) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), + MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN); + regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index), + value << gpio_index); + + return 0; +} + +static int mpfs_gpio_get_direction(struct gpio_chip *gc, + unsigned int gpio_index) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + unsigned int gpio_cfg; + + regmap_read(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), &gpio_cfg); + if (gpio_cfg & MPFS_GPIO_EN_IN) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + + if (mpfs_gpio_get_direction(gc, gpio_index) == GPIO_LINE_DIRECTION_OUT) + return regmap_test_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index)); + else + return regmap_test_bits(mpfs_gpio->regs, mpfs_gpio->offsets->inp, BIT(gpio_index)); +} + +static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + + mpfs_gpio_get(gc, gpio_index); + + regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index), + value << gpio_index); + + mpfs_gpio_get(gc, gpio_index); +} + +static int mpfs_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mpfs_gpio_chip *mpfs_gpio; + struct clk *clk; + void __iomem *base; + int ngpios; + + mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL); + if (!mpfs_gpio) + return -ENOMEM; + + mpfs_gpio->offsets = device_get_match_data(&pdev->dev); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "failed to ioremap memory resource\n"); + + mpfs_gpio->regs = devm_regmap_init_mmio(dev, base, &mpfs_gpio_regmap_config); + if (IS_ERR(mpfs_gpio->regs)) + return dev_err_probe(dev, PTR_ERR(mpfs_gpio->regs), + "failed to initialise regmap\n"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get and enable clock\n"); + + ngpios = MPFS_MAX_NUM_GPIO; + device_property_read_u32(dev, "ngpios", &ngpios); + if (ngpios > MPFS_MAX_NUM_GPIO) + ngpios = MPFS_MAX_NUM_GPIO; + + mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input; + mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output; + mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction; + mpfs_gpio->gc.get = mpfs_gpio_get; + mpfs_gpio->gc.set = mpfs_gpio_set; + mpfs_gpio->gc.base = -1; + mpfs_gpio->gc.ngpio = ngpios; + mpfs_gpio->gc.label = dev_name(dev); + mpfs_gpio->gc.parent = dev; + mpfs_gpio->gc.owner = THIS_MODULE; + + return devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio); +} + +static const struct mpfs_gpio_reg_offsets mpfs_reg_offsets = { + .inp = MPFS_INP_REG, + .outp = MPFS_OUTP_REG, +}; + +static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = { + .inp = COREGPIO_INP_REG, + .outp = COREGPIO_OUTP_REG, +}; + +static const struct of_device_id mpfs_gpio_of_ids[] = { + { + .compatible = "microchip,mpfs-gpio", + .data = &mpfs_reg_offsets, + }, { + .compatible = "microchip,coregpio-rtl-v3", + .data = &coregpio_reg_offsets, + }, + { /* end of list */ } +}; + +static struct platform_driver mpfs_gpio_driver = { + .probe = mpfs_gpio_probe, + .driver = { + .name = "microchip,mpfs-gpio", + .of_match_table = mpfs_gpio_of_ids, + }, +}; +builtin_platform_driver(mpfs_gpio_driver); diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c new file mode 100644 index 000000000000..3ea32c5e33d1 --- /dev/null +++ b/drivers/gpio/gpio-mpsse.c @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTDI MPSSE GPIO support + * + * Based on code by Anatolij Gustschin + * + * Copyright (C) 2024 Mary Strodl <mstrodl@csh.rit.edu> + */ + +#include <linux/cleanup.h> +#include <linux/gpio/driver.h> +#include <linux/mutex.h> +#include <linux/usb.h> + +struct mpsse_priv { + struct gpio_chip gpio; + struct usb_device *udev; /* USB device encompassing all MPSSEs */ + struct usb_interface *intf; /* USB interface for this MPSSE */ + u8 intf_id; /* USB interface number for this MPSSE */ + struct work_struct irq_work; /* polling work thread */ + struct mutex irq_mutex; /* lock over irq_data */ + atomic_t irq_type[16]; /* pin -> edge detection type */ + atomic_t irq_enabled; + int id; + + u8 gpio_outputs[2]; /* Output states for GPIOs [L, H] */ + u8 gpio_dir[2]; /* Directions for GPIOs [L, H] */ + + u8 *bulk_in_buf; /* Extra recv buffer to grab status bytes */ + + struct usb_endpoint_descriptor *bulk_in; + struct usb_endpoint_descriptor *bulk_out; + + struct mutex io_mutex; /* sync I/O with disconnect */ +}; + +struct bulk_desc { + bool tx; /* direction of bulk transfer */ + u8 *data; /* input (tx) or output (rx) */ + int len; /* Length of `data` if tx, or length of */ + /* Data to read if rx */ + int len_actual; /* Length successfully transferred */ + int timeout; +}; + +static const struct usb_device_id gpio_mpsse_table[] = { + { USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, gpio_mpsse_table); + +static DEFINE_IDA(gpio_mpsse_ida); + +/* MPSSE commands */ +#define SET_BITS_CMD 0x80 +#define GET_BITS_CMD 0x81 + +#define SET_BITMODE_REQUEST 0x0B +#define MODE_MPSSE (2 << 8) +#define MODE_RESET 0 + +/* Arbitrarily decided. This could probably be much less */ +#define MPSSE_WRITE_TIMEOUT 5000 +#define MPSSE_READ_TIMEOUT 5000 + +/* 1 millisecond, also pretty arbitrary */ +#define MPSSE_POLL_INTERVAL 1000 + +static int mpsse_bulk_xfer(struct usb_interface *intf, struct bulk_desc *desc) +{ + struct mpsse_priv *priv = usb_get_intfdata(intf); + struct usb_device *udev = priv->udev; + unsigned int pipe; + int ret; + + if (desc->tx) + pipe = usb_sndbulkpipe(udev, priv->bulk_out->bEndpointAddress); + else + pipe = usb_rcvbulkpipe(udev, priv->bulk_in->bEndpointAddress); + + ret = usb_bulk_msg(udev, pipe, desc->data, desc->len, + &desc->len_actual, desc->timeout); + if (ret) + dev_dbg(&udev->dev, "mpsse: bulk transfer failed: %d\n", ret); + + return ret; +} + +static int mpsse_write(struct usb_interface *intf, + u8 *buf, size_t len) +{ + int ret; + struct bulk_desc desc; + + desc.len_actual = 0; + desc.tx = true; + desc.data = buf; + desc.len = len; + desc.timeout = MPSSE_WRITE_TIMEOUT; + + ret = mpsse_bulk_xfer(intf, &desc); + + return ret; +} + +static int mpsse_read(struct usb_interface *intf, u8 *buf, size_t len) +{ + int ret; + struct bulk_desc desc; + struct mpsse_priv *priv = usb_get_intfdata(intf); + + desc.len_actual = 0; + desc.tx = false; + desc.data = priv->bulk_in_buf; + /* Device sends 2 additional status bytes, read len + 2 */ + desc.len = min_t(size_t, len + 2, usb_endpoint_maxp(priv->bulk_in)); + desc.timeout = MPSSE_READ_TIMEOUT; + + ret = mpsse_bulk_xfer(intf, &desc); + if (ret) + return ret; + + /* Did we get enough data? */ + if (desc.len_actual < desc.len) + return -EIO; + + memcpy(buf, desc.data + 2, desc.len_actual - 2); + + return ret; +} + +static int gpio_mpsse_set_bank(struct mpsse_priv *priv, u8 bank) +{ + int ret; + u8 tx_buf[3] = { + SET_BITS_CMD | (bank << 1), + priv->gpio_outputs[bank], + priv->gpio_dir[bank], + }; + + ret = mpsse_write(priv->intf, tx_buf, 3); + + return ret; +} + +static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank) +{ + int ret; + u8 buf = GET_BITS_CMD | (bank << 1); + + ret = mpsse_write(priv->intf, &buf, 1); + if (ret) + return ret; + + ret = mpsse_read(priv->intf, &buf, 1); + if (ret) + return ret; + + return buf; +} + +static void gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + unsigned long i, bank, bank_mask, bank_bits; + int ret; + struct mpsse_priv *priv = gpiochip_get_data(chip); + + guard(mutex)(&priv->io_mutex); + for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { + bank = i / 8; + + if (bank_mask) { + bank_bits = bitmap_get_value8(bits, i); + /* Zero out pins we want to change */ + priv->gpio_outputs[bank] &= ~bank_mask; + /* Set pins we care about */ + priv->gpio_outputs[bank] |= bank_bits & bank_mask; + + ret = gpio_mpsse_set_bank(priv, bank); + if (ret) + dev_err(&priv->intf->dev, + "Couldn't set values for bank %ld!", + bank); + } + } +} + +static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + unsigned long i, bank, bank_mask; + int ret; + struct mpsse_priv *priv = gpiochip_get_data(chip); + + guard(mutex)(&priv->io_mutex); + for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { + bank = i / 8; + + if (bank_mask) { + ret = gpio_mpsse_get_bank(priv, bank); + if (ret < 0) + return ret; + + bitmap_set_value8(bits, ret & bank_mask, i); + } + } + + return 0; +} + +static int gpio_mpsse_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + int err; + unsigned long mask = 0, bits = 0; + + __set_bit(offset, &mask); + err = gpio_mpsse_get_multiple(chip, &mask, &bits); + if (err) + return err; + + /* == is not guaranteed to give 1 if true */ + if (bits) + return 1; + else + return 0; +} + +static void gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + unsigned long mask = 0, bits = 0; + + __set_bit(offset, &mask); + if (value) + __set_bit(offset, &bits); + + gpio_mpsse_set_multiple(chip, &mask, &bits); +} + +static int gpio_mpsse_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct mpsse_priv *priv = gpiochip_get_data(chip); + int bank = (offset & 8) >> 3; + int bank_offset = offset & 7; + + scoped_guard(mutex, &priv->io_mutex) + priv->gpio_dir[bank] |= BIT(bank_offset); + + gpio_mpsse_gpio_set(chip, offset, value); + + return 0; +} + +static int gpio_mpsse_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct mpsse_priv *priv = gpiochip_get_data(chip); + int bank = (offset & 8) >> 3; + int bank_offset = offset & 7; + + guard(mutex)(&priv->io_mutex); + priv->gpio_dir[bank] &= ~BIT(bank_offset); + gpio_mpsse_set_bank(priv, bank); + + return 0; +} + +static int gpio_mpsse_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + int ret; + int bank = (offset & 8) >> 3; + int bank_offset = offset & 7; + struct mpsse_priv *priv = gpiochip_get_data(chip); + + guard(mutex)(&priv->io_mutex); + /* MPSSE directions are inverted */ + if (priv->gpio_dir[bank] & BIT(bank_offset)) + ret = GPIO_LINE_DIRECTION_OUT; + else + ret = GPIO_LINE_DIRECTION_IN; + + return ret; +} + +static void gpio_mpsse_poll(struct work_struct *work) +{ + unsigned long pin_mask, pin_states, flags; + int irq_enabled, offset, err, value, fire_irq, + irq, old_value[16], irq_type[16]; + struct mpsse_priv *priv = container_of(work, struct mpsse_priv, + irq_work); + + for (offset = 0; offset < priv->gpio.ngpio; ++offset) + old_value[offset] = -1; + + while ((irq_enabled = atomic_read(&priv->irq_enabled))) { + usleep_range(MPSSE_POLL_INTERVAL, MPSSE_POLL_INTERVAL + 1000); + /* Cleanup will trigger at the end of the loop */ + guard(mutex)(&priv->irq_mutex); + + pin_mask = 0; + pin_states = 0; + for (offset = 0; offset < priv->gpio.ngpio; ++offset) { + irq_type[offset] = atomic_read(&priv->irq_type[offset]); + if (irq_type[offset] != IRQ_TYPE_NONE && + irq_enabled & BIT(offset)) + pin_mask |= BIT(offset); + else + old_value[offset] = -1; + } + + err = gpio_mpsse_get_multiple(&priv->gpio, &pin_mask, + &pin_states); + if (err) { + dev_err_ratelimited(&priv->intf->dev, + "Error polling!\n"); + continue; + } + + /* Check each value */ + for (offset = 0; offset < priv->gpio.ngpio; ++offset) { + if (old_value[offset] == -1) + continue; + + fire_irq = 0; + value = pin_states & BIT(offset); + + switch (irq_type[offset]) { + case IRQ_TYPE_EDGE_RISING: + fire_irq = value > old_value[offset]; + break; + case IRQ_TYPE_EDGE_FALLING: + fire_irq = value < old_value[offset]; + break; + case IRQ_TYPE_EDGE_BOTH: + fire_irq = value != old_value[offset]; + break; + } + if (!fire_irq) + continue; + + irq = irq_find_mapping(priv->gpio.irq.domain, + offset); + local_irq_save(flags); + generic_handle_irq(irq); + local_irq_disable(); + local_irq_restore(flags); + } + + /* Sync back values so we can refer to them next tick */ + for (offset = 0; offset < priv->gpio.ngpio; ++offset) + if (irq_type[offset] != IRQ_TYPE_NONE && + irq_enabled & BIT(offset)) + old_value[offset] = pin_states & BIT(offset); + } +} + +static int gpio_mpsse_set_irq_type(struct irq_data *irqd, unsigned int type) +{ + int offset; + struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); + + offset = irqd->hwirq; + atomic_set(&priv->irq_type[offset], type & IRQ_TYPE_EDGE_BOTH); + + return 0; +} + +static void gpio_mpsse_irq_disable(struct irq_data *irqd) +{ + struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); + + atomic_and(~BIT(irqd->hwirq), &priv->irq_enabled); + gpiochip_disable_irq(&priv->gpio, irqd->hwirq); +} + +static void gpio_mpsse_irq_enable(struct irq_data *irqd) +{ + struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd); + + gpiochip_enable_irq(&priv->gpio, irqd->hwirq); + /* If no-one else was using the IRQ, enable it */ + if (!atomic_fetch_or(BIT(irqd->hwirq), &priv->irq_enabled)) { + INIT_WORK(&priv->irq_work, gpio_mpsse_poll); + schedule_work(&priv->irq_work); + } +} + +static const struct irq_chip gpio_mpsse_irq_chip = { + .name = "gpio-mpsse-irq", + .irq_enable = gpio_mpsse_irq_enable, + .irq_disable = gpio_mpsse_irq_disable, + .irq_set_type = gpio_mpsse_set_irq_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void gpio_mpsse_ida_remove(void *data) +{ + struct mpsse_priv *priv = data; + + ida_free(&gpio_mpsse_ida, priv->id); +} + +static int gpio_mpsse_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct mpsse_priv *priv; + struct device *dev; + int err; + + dev = &interface->dev; + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->udev = usb_get_dev(interface_to_usbdev(interface)); + priv->intf = interface; + priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber; + + priv->id = ida_alloc(&gpio_mpsse_ida, GFP_KERNEL); + if (priv->id < 0) + return priv->id; + + err = devm_add_action_or_reset(dev, gpio_mpsse_ida_remove, priv); + if (err) + return err; + + err = devm_mutex_init(dev, &priv->io_mutex); + if (err) + return err; + + err = devm_mutex_init(dev, &priv->irq_mutex); + if (err) + return err; + + priv->gpio.label = devm_kasprintf(dev, GFP_KERNEL, + "gpio-mpsse.%d.%d", + priv->id, priv->intf_id); + if (!priv->gpio.label) + return -ENOMEM; + + priv->gpio.owner = THIS_MODULE; + priv->gpio.parent = interface->usb_dev; + priv->gpio.get_direction = gpio_mpsse_get_direction; + priv->gpio.direction_input = gpio_mpsse_direction_input; + priv->gpio.direction_output = gpio_mpsse_direction_output; + priv->gpio.get = gpio_mpsse_gpio_get; + priv->gpio.set = gpio_mpsse_gpio_set; + priv->gpio.get_multiple = gpio_mpsse_get_multiple; + priv->gpio.set_multiple = gpio_mpsse_set_multiple; + priv->gpio.base = -1; + priv->gpio.ngpio = 16; + priv->gpio.offset = priv->intf_id * priv->gpio.ngpio; + priv->gpio.can_sleep = 1; + + err = usb_find_common_endpoints(interface->cur_altsetting, + &priv->bulk_in, &priv->bulk_out, + NULL, NULL); + if (err) + return err; + + priv->bulk_in_buf = devm_kmalloc(dev, usb_endpoint_maxp(priv->bulk_in), + GFP_KERNEL); + if (!priv->bulk_in_buf) + return -ENOMEM; + + usb_set_intfdata(interface, priv); + + /* Reset mode, needed to correctly enter MPSSE mode */ + err = usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + SET_BITMODE_REQUEST, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + MODE_RESET, priv->intf_id + 1, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (err) + return err; + + /* Enter MPSSE mode */ + err = usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + SET_BITMODE_REQUEST, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + MODE_MPSSE, priv->intf_id + 1, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (err) + return err; + + gpio_irq_chip_set_chip(&priv->gpio.irq, &gpio_mpsse_irq_chip); + + priv->gpio.irq.parent_handler = NULL; + priv->gpio.irq.num_parents = 0; + priv->gpio.irq.parents = NULL; + priv->gpio.irq.default_type = IRQ_TYPE_NONE; + priv->gpio.irq.handler = handle_simple_irq; + + err = devm_gpiochip_add_data(dev, &priv->gpio, priv); + if (err) + return err; + + return 0; +} + +static void gpio_mpsse_disconnect(struct usb_interface *intf) +{ + struct mpsse_priv *priv = usb_get_intfdata(intf); + + priv->intf = NULL; + usb_set_intfdata(intf, NULL); + usb_put_dev(priv->udev); +} + +static struct usb_driver gpio_mpsse_driver = { + .name = "gpio-mpsse", + .probe = gpio_mpsse_probe, + .disconnect = gpio_mpsse_disconnect, + .id_table = gpio_mpsse_table, +}; + +module_usb_driver(gpio_mpsse_driver); + +MODULE_AUTHOR("Mary Strodl <mstrodl@csh.rit.edu>"); +MODULE_DESCRIPTION("MPSSE GPIO driver"); +MODULE_LICENSE("GPL"); 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-mvebu.c b/drivers/gpio/gpio-mvebu.c index e93b4474cb20..ed2b70e72959 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -99,7 +99,6 @@ struct mvebu_pwm { u32 offset; unsigned long clk_rate; struct gpio_desc *gpiod; - struct pwm_chip chip; spinlock_t lock; struct mvebu_gpio_chip *mvchip; @@ -615,7 +614,7 @@ static const struct regmap_config mvebu_gpio_regmap_config = { */ static struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip) { - return container_of(chip, struct mvebu_pwm, chip); + return pwmchip_get_drvdata(chip); } static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -789,13 +788,14 @@ static int mvebu_pwm_probe(struct platform_device *pdev, { struct device *dev = &pdev->dev; struct mvebu_pwm *mvpwm; + struct pwm_chip *chip; void __iomem *base; u32 offset; u32 set; if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) { - int ret = of_property_read_u32(dev->of_node, - "marvell,pwm-offset", &offset); + int ret = device_property_read_u32(dev, "marvell,pwm-offset", + &offset); if (ret < 0) return 0; } else { @@ -813,9 +813,11 @@ static int mvebu_pwm_probe(struct platform_device *pdev, if (IS_ERR(mvchip->clk)) return PTR_ERR(mvchip->clk); - mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); - if (!mvpwm) - return -ENOMEM; + chip = devm_pwmchip_alloc(dev, mvchip->chip.ngpio, sizeof(*mvpwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + mvpwm = to_mvebu_pwm(chip); + mvchip->mvpwm = mvpwm; mvpwm->mvchip = mvchip; mvpwm->offset = offset; @@ -868,13 +870,11 @@ static int mvebu_pwm_probe(struct platform_device *pdev, return -EINVAL; } - mvpwm->chip.dev = dev; - mvpwm->chip.ops = &mvebu_pwm_ops; - mvpwm->chip.npwm = mvchip->chip.ngpio; + chip->ops = &mvebu_pwm_ops; spin_lock_init(&mvpwm->lock); - return devm_pwmchip_add(dev, &mvpwm->chip); + return devm_pwmchip_add(dev, chip); } #ifdef CONFIG_DEBUG_FS @@ -1106,7 +1106,7 @@ static int mvebu_gpio_probe_syscon(struct platform_device *pdev, if (IS_ERR(mvchip->regs)) return PTR_ERR(mvchip->regs); - if (of_property_read_u32(pdev->dev.of_node, "offset", &mvchip->offset)) + if (device_property_read_u32(&pdev->dev, "offset", &mvchip->offset)) return -EINVAL; return 0; @@ -1147,7 +1147,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mvchip); - if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) { + if (device_property_read_u32(&pdev->dev, "ngpios", &ngpios)) { dev_err(&pdev->dev, "Missing ngpios OF property\n"); return -ENODEV; } diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 4cb455b2bdee..619b6fb9d833 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -490,8 +490,7 @@ static int mxc_gpio_probe(struct platform_device *pdev) port->gc.request = mxc_gpio_request; port->gc.free = mxc_gpio_free; port->gc.to_irq = mxc_gpio_to_irq; - port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 : - pdev->id * 32; + port->gc.base = of_alias_get_id(np, "gpio") * 32; err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port); if (err) diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c new file mode 100644 index 000000000000..836f1cc760c2 --- /dev/null +++ b/drivers/gpio/gpio-nomadik.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driver for the IP block found in the Nomadik SoC; it is an AMBA device, + * managing 32 pins with alternate functions. It can also handle the STA2X11 + * block from ST. + * + * The GPIO chips are shared with pinctrl-nomadik if used; it needs access for + * pinmuxing functionality and others. + * + * This driver also handles the mobileye,eyeq5-gpio compatible. It is an STA2X11 + * but with only data, direction and interrupts register active. We want to + * avoid touching SLPM, RWIMSC, FWIMSC, AFSLA and AFSLB registers; that is, + * wake and alternate function registers. It is NOT compatible with + * pinctrl-nomadik. + * + * Copyright (C) 2008,2009 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> + * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com> + * Copyright (C) 2011-2013 Linus Walleij <linus.walleij@linaro.org> + */ +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <linux/gpio/gpio-nomadik.h> + +#ifndef CONFIG_PINCTRL_NOMADIK +static DEFINE_SPINLOCK(nmk_gpio_slpm_lock); +#endif + +void __nmk_gpio_set_slpm(struct nmk_gpio_chip *nmk_chip, unsigned int offset, + enum nmk_gpio_slpm mode) +{ + u32 slpm; + + /* We should NOT have been called. */ + if (WARN_ON(nmk_chip->is_mobileye_soc)) + return; + + slpm = readl(nmk_chip->addr + NMK_GPIO_SLPC); + if (mode == NMK_GPIO_SLPM_NOCHANGE) + slpm |= BIT(offset); + else + slpm &= ~BIT(offset); + writel(slpm, nmk_chip->addr + NMK_GPIO_SLPC); +} + +static void __nmk_gpio_set_output(struct nmk_gpio_chip *nmk_chip, + unsigned int offset, int val) +{ + if (val) + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DATS); + else + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DATC); +} + +void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip, + unsigned int offset, int val) +{ + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DIRS); + __nmk_gpio_set_output(nmk_chip, offset, val); +} + +/* IRQ functions */ + +static void nmk_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + clk_enable(nmk_chip->clk); + writel(BIT(d->hwirq), nmk_chip->addr + NMK_GPIO_IC); + clk_disable(nmk_chip->clk); +} + +enum nmk_gpio_irq_type { + NORMAL, + WAKE, +}; + +static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, + int offset, enum nmk_gpio_irq_type which, + bool enable) +{ + u32 *rimscval; + u32 *fimscval; + u32 rimscreg; + u32 fimscreg; + + if (which == NORMAL) { + rimscreg = NMK_GPIO_RIMSC; + fimscreg = NMK_GPIO_FIMSC; + rimscval = &nmk_chip->rimsc; + fimscval = &nmk_chip->fimsc; + } else { + /* We should NOT have been called. */ + if (WARN_ON(nmk_chip->is_mobileye_soc)) + return; + rimscreg = NMK_GPIO_RWIMSC; + fimscreg = NMK_GPIO_FWIMSC; + rimscval = &nmk_chip->rwimsc; + fimscval = &nmk_chip->fwimsc; + } + + /* we must individually set/clear the two edges */ + if (nmk_chip->edge_rising & BIT(offset)) { + if (enable) + *rimscval |= BIT(offset); + else + *rimscval &= ~BIT(offset); + writel(*rimscval, nmk_chip->addr + rimscreg); + } + if (nmk_chip->edge_falling & BIT(offset)) { + if (enable) + *fimscval |= BIT(offset); + else + *fimscval &= ~BIT(offset); + writel(*fimscval, nmk_chip->addr + fimscreg); + } +} + +static void __nmk_gpio_set_wake(struct nmk_gpio_chip *nmk_chip, + int offset, bool on) +{ + /* We should NOT have been called. */ + if (WARN_ON(nmk_chip->is_mobileye_soc)) + return; + + /* + * Ensure WAKEUP_ENABLE is on. No need to disable it if wakeup is + * disabled, since setting SLPM to 1 increases power consumption, and + * wakeup is anyhow controlled by the RIMSC and FIMSC registers. + */ + if (nmk_chip->sleepmode && on) { + __nmk_gpio_set_slpm(nmk_chip, offset, + NMK_GPIO_SLPM_WAKEUP_ENABLE); + } + + __nmk_gpio_irq_modify(nmk_chip, offset, WAKE, on); +} + +static void nmk_gpio_irq_maskunmask(struct nmk_gpio_chip *nmk_chip, + struct irq_data *d, bool enable) +{ + unsigned long flags; + + clk_enable(nmk_chip->clk); + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + spin_lock(&nmk_chip->lock); + + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, NORMAL, enable); + + if (!nmk_chip->is_mobileye_soc && !(nmk_chip->real_wake & BIT(d->hwirq))) + __nmk_gpio_set_wake(nmk_chip, d->hwirq, enable); + + spin_unlock(&nmk_chip->lock); + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); +} + +static void nmk_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + nmk_gpio_irq_maskunmask(nmk_chip, d, false); + gpiochip_disable_irq(gc, irqd_to_hwirq(d)); +} + +static void nmk_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + gpiochip_enable_irq(gc, irqd_to_hwirq(d)); + nmk_gpio_irq_maskunmask(nmk_chip, d, true); +} + +static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + unsigned long flags; + + /* Handler is registered in all cases. */ + if (nmk_chip->is_mobileye_soc) + return -ENXIO; + + clk_enable(nmk_chip->clk); + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + spin_lock(&nmk_chip->lock); + + if (irqd_irq_disabled(d)) + __nmk_gpio_set_wake(nmk_chip, d->hwirq, on); + + if (on) + nmk_chip->real_wake |= BIT(d->hwirq); + else + nmk_chip->real_wake &= ~BIT(d->hwirq); + + spin_unlock(&nmk_chip->lock); + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); + + return 0; +} + +static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + bool enabled = !irqd_irq_disabled(d); + bool wake = irqd_is_wakeup_set(d); + unsigned long flags; + + if (type & IRQ_TYPE_LEVEL_HIGH) + return -EINVAL; + if (type & IRQ_TYPE_LEVEL_LOW) + return -EINVAL; + + clk_enable(nmk_chip->clk); + spin_lock_irqsave(&nmk_chip->lock, flags); + + if (enabled) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, NORMAL, false); + + if (!nmk_chip->is_mobileye_soc && (enabled || wake)) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, WAKE, false); + + nmk_chip->edge_rising &= ~BIT(d->hwirq); + if (type & IRQ_TYPE_EDGE_RISING) + nmk_chip->edge_rising |= BIT(d->hwirq); + + nmk_chip->edge_falling &= ~BIT(d->hwirq); + if (type & IRQ_TYPE_EDGE_FALLING) + nmk_chip->edge_falling |= BIT(d->hwirq); + + if (enabled) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, NORMAL, true); + + if (!nmk_chip->is_mobileye_soc && (enabled || wake)) + __nmk_gpio_irq_modify(nmk_chip, d->hwirq, WAKE, true); + + spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); + + return 0; +} + +static unsigned int nmk_gpio_irq_startup(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + clk_enable(nmk_chip->clk); + nmk_gpio_irq_unmask(d); + return 0; +} + +static void nmk_gpio_irq_shutdown(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + nmk_gpio_irq_mask(d); + clk_disable(nmk_chip->clk); +} + +static irqreturn_t nmk_gpio_irq_handler(int irq, void *dev_id) +{ + struct nmk_gpio_chip *nmk_chip = dev_id; + struct gpio_chip *chip = &nmk_chip->chip; + unsigned long mask = GENMASK(chip->ngpio - 1, 0); + unsigned long status; + int bit; + + clk_enable(nmk_chip->clk); + + status = readl(nmk_chip->addr + NMK_GPIO_IS); + + /* Ensure we cannot leave pending bits; this should never occur. */ + if (unlikely(status & ~mask)) + writel(status & ~mask, nmk_chip->addr + NMK_GPIO_IC); + + clk_disable(nmk_chip->clk); + + for_each_set_bit(bit, &status, chip->ngpio) + generic_handle_domain_irq_safe(chip->irq.domain, bit); + + return IRQ_RETVAL((status & mask) != 0); +} + +/* I/O Functions */ + +static int nmk_gpio_get_dir(struct gpio_chip *chip, unsigned int offset) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + int dir; + + clk_enable(nmk_chip->clk); + + dir = readl(nmk_chip->addr + NMK_GPIO_DIR) & BIT(offset); + + clk_disable(nmk_chip->clk); + + if (dir) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned int offset) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + + clk_enable(nmk_chip->clk); + + writel(BIT(offset), nmk_chip->addr + NMK_GPIO_DIRC); + + clk_disable(nmk_chip->clk); + + return 0; +} + +static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned int offset) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + int value; + + clk_enable(nmk_chip->clk); + + value = !!(readl(nmk_chip->addr + NMK_GPIO_DAT) & BIT(offset)); + + clk_disable(nmk_chip->clk); + + return value; +} + +static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, + int val) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + + clk_enable(nmk_chip->clk); + + __nmk_gpio_set_output(nmk_chip, offset, val); + + clk_disable(nmk_chip->clk); +} + +static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned int offset, + int val) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + + clk_enable(nmk_chip->clk); + + __nmk_gpio_make_output(nmk_chip, offset, val); + + clk_disable(nmk_chip->clk); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS + +static int nmk_gpio_get_mode(struct nmk_gpio_chip *nmk_chip, int offset) +{ + u32 afunc, bfunc; + + /* We don't support modes. */ + if (nmk_chip->is_mobileye_soc) + return NMK_GPIO_ALT_GPIO; + + clk_enable(nmk_chip->clk); + + afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & BIT(offset); + bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & BIT(offset); + + clk_disable(nmk_chip->clk); + + return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); +} + +void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev, + struct gpio_chip *chip, unsigned int offset, + unsigned int gpio) +{ + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); + int mode; + bool is_out; + bool data_out; + bool pull; + static const char * const modes[] = { + [NMK_GPIO_ALT_GPIO] = "gpio", + [NMK_GPIO_ALT_A] = "altA", + [NMK_GPIO_ALT_B] = "altB", + [NMK_GPIO_ALT_C] = "altC", + [NMK_GPIO_ALT_C + 1] = "altC1", + [NMK_GPIO_ALT_C + 2] = "altC2", + [NMK_GPIO_ALT_C + 3] = "altC3", + [NMK_GPIO_ALT_C + 4] = "altC4", + }; + + char *label = gpiochip_dup_line_label(chip, offset); + if (IS_ERR(label)) + return; + + clk_enable(nmk_chip->clk); + is_out = !!(readl(nmk_chip->addr + NMK_GPIO_DIR) & BIT(offset)); + pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & BIT(offset)); + data_out = !!(readl(nmk_chip->addr + NMK_GPIO_DAT) & BIT(offset)); + mode = nmk_gpio_get_mode(nmk_chip, offset); +#ifdef CONFIG_PINCTRL_NOMADIK + if (mode == NMK_GPIO_ALT_C && pctldev) + mode = nmk_prcm_gpiocr_get_mode(pctldev, gpio); +#endif + + if (is_out) { + seq_printf(s, " gpio-%-3d (%-20.20s) out %s %s", + gpio, + label ?: "(none)", + data_out ? "hi" : "lo", + (mode < 0) ? "unknown" : modes[mode]); + } else { + int irq = chip->to_irq(chip, offset); + const int pullidx = pull ? 1 : 0; + int val; + static const char * const pulls[] = { + "none ", + "pull enabled", + }; + + seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s", + gpio, + label ?: "(none)", + pulls[pullidx], + (mode < 0) ? "unknown" : modes[mode]); + + val = nmk_gpio_get_input(chip, offset); + seq_printf(s, " VAL %d", val); + + /* + * This races with request_irq(), set_irq_type(), + * and set_irq_wake() ... but those are "rare". + */ + if (irq > 0 && irq_has_action(irq)) { + char *trigger; + bool wake; + + if (nmk_chip->edge_rising & BIT(offset)) + trigger = "edge-rising"; + else if (nmk_chip->edge_falling & BIT(offset)) + trigger = "edge-falling"; + else + trigger = "edge-undefined"; + + wake = !!(nmk_chip->real_wake & BIT(offset)); + + seq_printf(s, " irq-%d %s%s", + irq, trigger, wake ? " wakeup" : ""); + } + } + clk_disable(nmk_chip->clk); +} + +static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + unsigned int i, gpio = chip->base; + + for (i = 0; i < chip->ngpio; i++, gpio++) { + nmk_gpio_dbg_show_one(s, NULL, chip, i, gpio); + seq_puts(s, "\n"); + } +} + +#else + +#define nmk_gpio_dbg_show NULL + +#endif + +/* + * We will allocate memory for the state container using devm* allocators + * binding to the first device reaching this point, it doesn't matter if + * it is the pin controller or GPIO driver. However we need to use the right + * platform device when looking up resources so pay attention to pdev. + */ +struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode, + struct platform_device *pdev) +{ + struct nmk_gpio_chip *nmk_chip; + struct platform_device *gpio_pdev; + struct device *dev = &pdev->dev; + struct reset_control *reset; + struct device *gpio_dev; + struct gpio_chip *chip; + struct resource *res; + struct clk *clk; + void __iomem *base; + u32 id, ngpio; + int ret; + + gpio_dev = bus_find_device_by_fwnode(&platform_bus_type, fwnode); + if (!gpio_dev) { + dev_err(dev, "populate \"%pfwP\": device not found\n", fwnode); + return ERR_PTR(-ENODEV); + } + gpio_pdev = to_platform_device(gpio_dev); + + if (device_property_read_u32(gpio_dev, "gpio-bank", &id)) { + dev_err(dev, "populate: gpio-bank property not found\n"); + platform_device_put(gpio_pdev); + return ERR_PTR(-EINVAL); + } + +#ifdef CONFIG_PINCTRL_NOMADIK + if (id >= ARRAY_SIZE(nmk_gpio_chips)) { + dev_err(dev, "populate: invalid id: %u\n", id); + platform_device_put(gpio_pdev); + return ERR_PTR(-EINVAL); + } + /* Already populated? */ + nmk_chip = nmk_gpio_chips[id]; + if (nmk_chip) { + platform_device_put(gpio_pdev); + return nmk_chip; + } +#endif + + nmk_chip = devm_kzalloc(dev, sizeof(*nmk_chip), GFP_KERNEL); + if (!nmk_chip) { + platform_device_put(gpio_pdev); + return ERR_PTR(-ENOMEM); + } + + if (device_property_read_u32(gpio_dev, "ngpios", &ngpio)) { + ngpio = NMK_GPIO_PER_CHIP; + dev_dbg(dev, "populate: using default ngpio (%u)\n", ngpio); + } + + nmk_chip->is_mobileye_soc = device_is_compatible(gpio_dev, + "mobileye,eyeq5-gpio"); + nmk_chip->bank = id; + chip = &nmk_chip->chip; + chip->base = -1; + chip->ngpio = ngpio; + chip->label = dev_name(gpio_dev); + chip->parent = gpio_dev; + + /* NOTE: different devices! No devm_platform_ioremap_resource() here! */ + res = platform_get_resource(gpio_pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) { + platform_device_put(gpio_pdev); + return ERR_CAST(base); + } + nmk_chip->addr = base; + + /* NOTE: do not use devm_ here! */ + clk = clk_get_optional(gpio_dev, NULL); + if (IS_ERR(clk)) { + platform_device_put(gpio_pdev); + return ERR_CAST(clk); + } + clk_prepare(clk); + nmk_chip->clk = clk; + + /* NOTE: do not use devm_ here! */ + reset = reset_control_get_optional_shared(gpio_dev, NULL); + if (IS_ERR(reset)) { + clk_unprepare(clk); + clk_put(clk); + platform_device_put(gpio_pdev); + dev_err(dev, "failed getting reset control: %pe\n", + reset); + return ERR_CAST(reset); + } + + /* + * Reset might be shared and asserts/deasserts calls are unbalanced. We + * only support sharing this reset with other gpio-nomadik devices that + * use this reset to ensure deassertion at probe. + */ + ret = reset_control_deassert(reset); + if (ret) { + reset_control_put(reset); + clk_unprepare(clk); + clk_put(clk); + platform_device_put(gpio_pdev); + dev_err(dev, "failed reset deassert: %d\n", ret); + return ERR_PTR(ret); + } + +#ifdef CONFIG_PINCTRL_NOMADIK + nmk_gpio_chips[id] = nmk_chip; +#endif + return nmk_chip; +} + +static void nmk_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(gc); + + seq_printf(p, "nmk%u-%u-%u", nmk_chip->bank, + gc->base, gc->base + gc->ngpio - 1); +} + +static const struct irq_chip nmk_irq_chip = { + .irq_ack = nmk_gpio_irq_ack, + .irq_mask = nmk_gpio_irq_mask, + .irq_unmask = nmk_gpio_irq_unmask, + .irq_set_type = nmk_gpio_irq_set_type, + .irq_set_wake = nmk_gpio_irq_set_wake, + .irq_startup = nmk_gpio_irq_startup, + .irq_shutdown = nmk_gpio_irq_shutdown, + .irq_print_chip = nmk_gpio_irq_print_chip, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static int nmk_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nmk_gpio_chip *nmk_chip; + struct gpio_irq_chip *girq; + bool supports_sleepmode; + struct gpio_chip *chip; + int irq; + int ret; + + nmk_chip = nmk_gpio_populate_chip(dev_fwnode(dev), pdev); + if (IS_ERR(nmk_chip)) { + dev_err(dev, "could not populate nmk chip struct\n"); + return PTR_ERR(nmk_chip); + } + + supports_sleepmode = + device_property_read_bool(dev, "st,supports-sleepmode"); + + /* Correct platform device ID */ + pdev->id = nmk_chip->bank; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + /* + * The virt address in nmk_chip->addr is in the nomadik register space, + * so we can simply convert the resource address, without remapping + */ + nmk_chip->sleepmode = supports_sleepmode; + spin_lock_init(&nmk_chip->lock); + + chip = &nmk_chip->chip; + chip->parent = dev; + chip->request = gpiochip_generic_request; + chip->free = gpiochip_generic_free; + chip->get_direction = nmk_gpio_get_dir; + chip->direction_input = nmk_gpio_make_input; + chip->get = nmk_gpio_get_input; + chip->direction_output = nmk_gpio_make_output; + chip->set = nmk_gpio_set_output; + chip->dbg_show = nmk_gpio_dbg_show; + chip->can_sleep = false; + chip->owner = THIS_MODULE; + + girq = &chip->irq; + gpio_irq_chip_set_chip(girq, &nmk_irq_chip); + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + + ret = devm_request_irq(dev, irq, nmk_gpio_irq_handler, IRQF_SHARED, + dev_name(dev), nmk_chip); + if (ret) { + dev_err(dev, "failed requesting IRQ\n"); + return ret; + } + + if (!nmk_chip->is_mobileye_soc) { + clk_enable(nmk_chip->clk); + nmk_chip->lowemi = readl_relaxed(nmk_chip->addr + NMK_GPIO_LOWEMI); + clk_disable(nmk_chip->clk); + } + + ret = gpiochip_add_data(chip, nmk_chip); + if (ret) + return ret; + + platform_set_drvdata(pdev, nmk_chip); + + dev_info(dev, "chip registered\n"); + + return 0; +} + +static const struct of_device_id nmk_gpio_match[] = { + { .compatible = "st,nomadik-gpio", }, + { .compatible = "mobileye,eyeq5-gpio", }, + {} +}; + +static struct platform_driver nmk_gpio_driver = { + .driver = { + .name = "nomadik-gpio", + .of_match_table = nmk_gpio_match, + .suppress_bind_attrs = true, + }, + .probe = nmk_gpio_probe, +}; + +static int __init nmk_gpio_init(void) +{ + return platform_driver_register(&nmk_gpio_driver); +} +subsys_initcall(nmk_gpio_init); diff --git a/drivers/gpio/gpio-npcm-sgpio.c b/drivers/gpio/gpio-npcm-sgpio.c index d31788b43abc..260570614543 100644 --- a/drivers/gpio/gpio-npcm-sgpio.c +++ b/drivers/gpio/gpio-npcm-sgpio.c @@ -434,7 +434,7 @@ static void npcm_sgpio_irq_handler(struct irq_desc *desc) struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct irq_chip *ic = irq_desc_get_chip(desc); struct npcm_sgpio *gpio = gpiochip_get_data(gc); - unsigned int i, j, girq; + unsigned int i, j; unsigned long reg; chained_irq_enter(ic, desc); @@ -443,11 +443,9 @@ static void npcm_sgpio_irq_handler(struct irq_desc *desc) const struct npcm_sgpio_bank *bank = &npcm_sgpio_banks[i]; reg = ioread8(bank_reg(gpio, bank, EVENT_STS)); - for_each_set_bit(j, ®, 8) { - girq = irq_find_mapping(gc->irq.domain, - i * 8 + gpio->nout_sgpio + j); - generic_handle_domain_irq(gc->irq.domain, girq); - } + for_each_set_bit(j, ®, 8) + generic_handle_domain_irq(gc->irq.domain, + i * 8 + gpio->nout_sgpio + j); } chained_irq_exit(ic, desc); diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 76d5d87e9681..54c4bfdccf56 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -715,7 +715,7 @@ static void omap_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct gpio_bank *bank = omap_irq_data_get_bank(d); - seq_printf(p, dev_name(bank->dev)); + seq_puts(p, dev_name(bank->dev)); } static const struct irq_chip omap_gpio_irq_chip = { @@ -1557,7 +1557,7 @@ static const struct dev_pm_ops gpio_pm_ops = { static struct platform_driver omap_gpio_driver = { .probe = omap_gpio_probe, - .remove_new = omap_gpio_remove, + .remove = omap_gpio_remove, .driver = { .name = "omap_gpio", .pm = &gpio_pm_ops, diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 00ffa168e405..d63c1030e6ac 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -144,7 +144,7 @@ static int pca953x_acpi_get_irq(struct device *dev) if (ret) dev_warn(dev, "can't add GPIO ACPI mapping\n"); - ret = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev), "irq-gpios", 0); + ret = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev), "irq", 0); if (ret < 0) return ret; @@ -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); @@ -813,7 +815,7 @@ static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip pca953x_irq_chip = { @@ -839,25 +841,6 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin DECLARE_BITMAP(trigger, MAX_LINE); int ret; - if (chip->driver_data & PCA_PCAL) { - /* Read the current interrupt status from the device */ - ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, trigger); - if (ret) - return false; - - /* Check latched inputs and clear interrupt status */ - ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); - if (ret) - return false; - - /* Apply filter for rising/falling edge selection */ - bitmap_replace(new_stat, chip->irq_trig_fall, chip->irq_trig_raise, cur_stat, gc->ngpio); - - bitmap_and(pending, new_stat, trigger, gc->ngpio); - - return !bitmap_empty(pending, gc->ngpio); - } - ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); if (ret) return false; @@ -1086,7 +1069,8 @@ static int pca953x_probe(struct i2c_client *client) */ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(reset_gpio)) - return PTR_ERR(reset_gpio); + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to get reset gpio\n"); } chip->client = client; @@ -1313,6 +1297,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-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 53b69abe6787..7c57eaeb0afe 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -438,5 +438,6 @@ static void __exit pcf857x_exit(void) } module_exit(pcf857x_exit); +MODULE_DESCRIPTION("Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); 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-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 44c0a21b1d1d..476cea1b5ed7 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -70,24 +70,17 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct device *const dev = &pdev->dev; int err; const size_t pci_bar_index = 2; - const char *const name = pci_name(pdev); struct idio_16_regmap_config config = {}; void __iomem *regs; struct regmap *map; err = pcim_enable_device(pdev); - if (err) { - dev_err(dev, "Failed to enable PCI device (%d)\n", err); - return err; - } - - err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name); - if (err) { - dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, "Failed to enable PCI device\n"); - regs = pcim_iomap_table(pdev)[pci_bar_index]; + regs = pcim_iomap_region(pdev, pci_bar_index, pci_name(pdev)); + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), "Unable to map PCI I/O addresses\n"); map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config); if (IS_ERR(map)) @@ -119,4 +112,4 @@ module_pci_driver(idio_16_driver); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(GPIO_IDIO_16); +MODULE_IMPORT_NS("GPIO_IDIO_16"); diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index 2efd1b1a0805..80c0ba0afa67 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -267,7 +267,7 @@ static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned case IDIO_24_CONTROL_REG: /* We can only set direction for TTL/CMOS lines */ if (offset < 48) - return -EOPNOTSUPP; + return -ENOTSUPP; *reg = IDIO_24_CONTROL_REG; *mask = CONTROL_REG_OUT_MODE; @@ -305,19 +305,16 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct regmap_irq_chip_data *chip_data; err = pcim_enable_device(pdev); - if (err) { - dev_err(dev, "Failed to enable PCI device (%d)\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, "Failed to enable PCI device\n"); - err = pcim_iomap_regions(pdev, BIT(pci_plx_bar_index) | BIT(pci_bar_index), name); - if (err) { - dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err); - return err; - } + pex8311_regs = pcim_iomap_region(pdev, pci_plx_bar_index, "pex8311"); + if (IS_ERR(pex8311_regs)) + return dev_err_probe(dev, PTR_ERR(pex8311_regs), "Unable to map PEX 8311 I/O addresses\n"); - pex8311_regs = pcim_iomap_table(pdev)[pci_plx_bar_index]; - idio_24_regs = pcim_iomap_table(pdev)[pci_bar_index]; + idio_24_regs = pcim_iomap_region(pdev, pci_bar_index, name); + if (IS_ERR(idio_24_regs)) + return dev_err_probe(dev, PTR_ERR(idio_24_regs), "Unable to map PCIe-IDIO-24 I/O addresses\n"); intcsr_map = devm_regmap_init_mmio(dev, pex8311_regs, &pex8311_intcsr_regmap_config); if (IS_ERR(intcsr_map)) diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 9fc1f3dd4190..1c273727ffa3 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -291,7 +291,7 @@ static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip pl061_irq_chip = { @@ -438,4 +438,5 @@ static struct amba_driver pl061_gpio_driver = { }; module_amba_driver(pl061_gpio_driver); +MODULE_DESCRIPTION("Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 6159fda38d5d..a7a1cdf7ac66 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -40,7 +40,7 @@ struct gpio_rcar_info { struct gpio_rcar_priv { void __iomem *base; - spinlock_t lock; + raw_spinlock_t lock; struct device *dev; struct gpio_chip gpio_chip; unsigned int irq_parent; @@ -123,7 +123,7 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, * "Setting Level-Sensitive Interrupt Input Mode" */ - spin_lock_irqsave(&p->lock, flags); + raw_spin_lock_irqsave(&p->lock, flags); /* Configure positive or negative logic in POSNEG */ gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge); @@ -142,7 +142,7 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, if (!level_trigger) gpio_rcar_write(p, INTCLR, BIT(hwirq)); - spin_unlock_irqrestore(&p->lock, flags); + raw_spin_unlock_irqrestore(&p->lock, flags); } static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) @@ -246,7 +246,7 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, * "Setting General Input Mode" */ - spin_lock_irqsave(&p->lock, flags); + raw_spin_lock_irqsave(&p->lock, flags); /* Configure positive logic in POSNEG */ gpio_rcar_modify_bit(p, POSNEG, gpio, false); @@ -261,7 +261,7 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, if (p->info.has_outdtsel && output) gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false); - spin_unlock_irqrestore(&p->lock, flags); + raw_spin_unlock_irqrestore(&p->lock, flags); } static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) @@ -347,7 +347,7 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, return 0; } - spin_lock_irqsave(&p->lock, flags); + raw_spin_lock_irqsave(&p->lock, flags); outputs = gpio_rcar_read(p, INOUTSEL); m = outputs & bankmask; if (m) @@ -356,7 +356,7 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, m = ~outputs & bankmask; if (m) val |= gpio_rcar_read(p, INDT) & m; - spin_unlock_irqrestore(&p->lock, flags); + raw_spin_unlock_irqrestore(&p->lock, flags); bits[0] = val; return 0; @@ -367,9 +367,9 @@ static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) struct gpio_rcar_priv *p = gpiochip_get_data(chip); unsigned long flags; - spin_lock_irqsave(&p->lock, flags); + raw_spin_lock_irqsave(&p->lock, flags); gpio_rcar_modify_bit(p, OUTDT, offset, value); - spin_unlock_irqrestore(&p->lock, flags); + raw_spin_unlock_irqrestore(&p->lock, flags); } static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, @@ -386,12 +386,12 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, if (!bankmask) return; - spin_lock_irqsave(&p->lock, flags); + raw_spin_lock_irqsave(&p->lock, flags); val = gpio_rcar_read(p, OUTDT); val &= ~bankmask; val |= (bankmask & bits[0]); gpio_rcar_write(p, OUTDT, val); - spin_unlock_irqrestore(&p->lock, flags); + raw_spin_unlock_irqrestore(&p->lock, flags); } static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, @@ -468,7 +468,12 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) p->info = *info; ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); - *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; + if (ret) { + *npins = RCAR_MAX_GPIO_PER_BANK; + } else { + *npins = args.args[2]; + of_node_put(args.np); + } if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n", @@ -505,7 +510,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) return -ENOMEM; p->dev = dev; - spin_lock_init(&p->lock); + raw_spin_lock_init(&p->lock); /* Get device configuration from DT node */ ret = gpio_rcar_parse_dt(p, &npins); @@ -657,7 +662,7 @@ static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume); static struct platform_driver gpio_rcar_device_driver = { .probe = gpio_rcar_probe, - .remove_new = gpio_rcar_remove, + .remove = gpio_rcar_remove, .driver = { .name = "gpio_rcar", .pm = &gpio_rcar_pm_ops, 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-regmap.c b/drivers/gpio/gpio-regmap.c index c08c8e528867..05f8781b5204 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -129,7 +129,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, base = gpio_regmap_addr(gpio->reg_dir_in_base); invert = 1; } else { - return -EOPNOTSUPP; + return -ENOTSUPP; } ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); @@ -160,7 +160,7 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip, base = gpio_regmap_addr(gpio->reg_dir_in_base); invert = 1; } else { - return -EOPNOTSUPP; + return -ENOTSUPP; } ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); @@ -262,6 +262,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->label = config->label ?: dev_name(config->parent); chip->can_sleep = regmap_might_sleep(config->regmap); + chip->request = gpiochip_generic_request; + chip->free = gpiochip_generic_free; chip->get = gpio_regmap_get; if (gpio->reg_set_base && gpio->reg_clr_base) chip->set = gpio_regmap_set_with_clear; diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 0bd339813110..01a3b3dac58b 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -26,9 +26,16 @@ #include "../pinctrl/core.h" #include "../pinctrl/pinctrl-rockchip.h" +/* + * Version ID Register + * Bits [31:24] - Major Version + * Bits [23:16] - Minor Version + * Bits [15:0] - Revision Number + */ #define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ -#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ -#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ +#define GPIO_TYPE_V2 (0x01000C2B) +#define GPIO_TYPE_V2_1 (0x0101157C) +#define GPIO_TYPE_V2_2 (0x010219C8) static const struct rockchip_gpio_regs gpio_regs_v1 = { .port_dr = 0x00, @@ -602,7 +609,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) * files which don't set the "gpio-ranges" property or systems that * utilize ACPI the driver has to call gpiochip_add_pin_range(). */ - if (!of_property_read_bool(bank->of_node, "gpio-ranges")) { + if (!of_property_present(bank->of_node, "gpio-ranges")) { struct device_node *pctlnp = of_get_parent(bank->of_node); struct pinctrl_dev *pctldev = NULL; @@ -661,8 +668,10 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) clk_prepare_enable(bank->clk); id = readl(bank->reg_base + gpio_regs_v2.version_id); - /* If not gpio v2, that is default to v1. */ - if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) { + switch (id) { + case GPIO_TYPE_V2: + case GPIO_TYPE_V2_1: + case GPIO_TYPE_V2_2: bank->gpio_regs = &gpio_regs_v2; bank->gpio_type = GPIO_TYPE_V2; bank->db_clk = of_clk_get(bank->of_node, 1); @@ -671,9 +680,14 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) clk_disable_unprepare(bank->clk); return -EINVAL; } - } else { + break; + case GPIO_TYPE_V1: bank->gpio_regs = &gpio_regs_v1; bank->gpio_type = GPIO_TYPE_V1; + break; + default: + dev_err(bank->dev, "unsupported version ID: 0x%08x\n", id); + return -ENODEV; } return 0; @@ -713,6 +727,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; @@ -794,7 +809,7 @@ static const struct of_device_id rockchip_gpio_match[] = { static struct platform_driver rockchip_gpio_driver = { .probe = rockchip_gpio_probe, - .remove_new = rockchip_gpio_remove, + .remove = rockchip_gpio_remove, .driver = { .name = "rockchip-gpio", .of_match_table = rockchip_gpio_match, 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-sch.c b/drivers/gpio/gpio-sch.c index e48392074e4b..ff0341b1222f 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -38,8 +38,8 @@ struct sch_gpio { struct gpio_chip chip; + void __iomem *regs; spinlock_t lock; - unsigned short iobase; unsigned short resume_base; /* GPE handling */ @@ -75,7 +75,7 @@ static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned in offset = sch_gpio_offset(sch, gpio, reg); bit = sch_gpio_bit(sch, gpio); - reg_val = !!(inb(sch->iobase + offset) & BIT(bit)); + reg_val = !!(ioread8(sch->regs + offset) & BIT(bit)); return reg_val; } @@ -89,12 +89,14 @@ static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned i offset = sch_gpio_offset(sch, gpio, reg); bit = sch_gpio_bit(sch, gpio); - reg_val = inb(sch->iobase + offset); + reg_val = ioread8(sch->regs + offset); if (val) - outb(reg_val | BIT(bit), sch->iobase + offset); + reg_val |= BIT(bit); else - outb((reg_val & ~BIT(bit)), sch->iobase + offset); + reg_val &= ~BIT(bit); + + iowrite8(reg_val, sch->regs + offset); } static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) @@ -267,8 +269,8 @@ static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) spin_lock_irqsave(&sch->lock, flags); - core_status = inl(sch->iobase + CORE_BANK_OFFSET + GTS); - resume_status = inl(sch->iobase + RESUME_BANK_OFFSET + GTS); + core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); + resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); spin_unlock_irqrestore(&sch->lock, flags); @@ -319,12 +321,14 @@ static int sch_gpio_install_gpe_handler(struct sch_gpio *sch) static int sch_gpio_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct gpio_irq_chip *girq; struct sch_gpio *sch; struct resource *res; + void __iomem *regs; int ret; - sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL); + sch = devm_kzalloc(dev, sizeof(*sch), GFP_KERNEL); if (!sch) return -ENOMEM; @@ -332,15 +336,16 @@ static int sch_gpio_probe(struct platform_device *pdev) if (!res) return -EBUSY; - if (!devm_request_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) + regs = devm_ioport_map(dev, res->start, resource_size(res)); + if (!regs) return -EBUSY; + sch->regs = regs; + spin_lock_init(&sch->lock); - sch->iobase = res->start; sch->chip = sch_gpio_chip; - sch->chip.label = dev_name(&pdev->dev); - sch->chip.parent = &pdev->dev; + sch->chip.label = dev_name(dev); + sch->chip.parent = dev; switch (pdev->id) { case PCI_DEVICE_ID_INTEL_SCH_LPC: @@ -394,9 +399,9 @@ static int sch_gpio_probe(struct platform_device *pdev) ret = sch_gpio_install_gpe_handler(sch); if (ret) - dev_warn(&pdev->dev, "Can't setup GPE, no IRQ support\n"); + dev_warn(dev, "Can't setup GPE, no IRQ support\n"); - return devm_gpiochip_add_data(&pdev->dev, &sch->chip, sch); + return devm_gpiochip_add_data(dev, &sch->chip, sch); } static struct platform_driver sch_gpio_driver = { diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index c4106e37e6db..b6c230fab840 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,8 +21,8 @@ #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> #include <linux/mod_devicetable.h> #include <linux/module.h> @@ -226,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); @@ -234,10 +256,10 @@ static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc) guard(mutex)(&chip->lock); - for_each_requested_gpio(gc, i, label) + for_each_hwgpio(gc, i, label) seq_printf(seq, " gpio-%-3d (%s) %s,%s\n", gc->base + i, - label, + label ?: "<unused>", test_bit(i, chip->direction_map) ? "input" : test_bit(i, chip->value_map) ? "output-high" : "output-low", @@ -307,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; @@ -398,11 +413,6 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip); } -static int gpio_sim_dev_match_fwnode(struct device *dev, void *data) -{ - return device_match_fwnode(dev, data); -} - static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) { struct gpio_sim_chip *chip; @@ -420,7 +430,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) ret = fwnode_property_read_string(swnode, "gpio-sim,label", &label); if (ret) { - label = devm_kasprintf(dev, GFP_KERNEL, "%s-%pfwP", + label = devm_kasprintf(dev, GFP_KERNEL, "%s:%pfwP", dev_name(dev), swnode); if (!label) return -ENOMEM; @@ -449,7 +459,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); @@ -457,9 +469,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; @@ -488,7 +498,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) if (ret) return ret; - chip->dev = device_find_child(dev, swnode, gpio_sim_dev_match_fwnode); + chip->dev = device_find_child(dev, swnode, device_match_fwnode); if (!chip->dev) return -ENODEV; @@ -505,15 +515,12 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) static int gpio_sim_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct fwnode_handle *swnode; int ret; - device_for_each_child_node(dev, swnode) { + device_for_each_child_node_scoped(dev, swnode) { ret = gpio_sim_add_bank(swnode, dev); - if (ret) { - fwnode_handle_put(swnode); + if (ret) return ret; - } } return 0; @@ -580,19 +587,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) @@ -697,8 +704,10 @@ static struct gpio_sim_device *gpio_sim_hog_get_device(struct gpio_sim_hog *hog) return gpio_sim_line_get_device(line); } -static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev) +static bool gpio_sim_device_is_live(struct gpio_sim_device *dev) { + lockdep_assert_held(&dev->lock); + return !!dev->pdev; } @@ -737,7 +746,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page) bool live; scoped_guard(mutex, &dev->lock) - live = gpio_sim_device_is_live_unlocked(dev); + live = gpio_sim_device_is_live(dev); return sprintf(page, "%c\n", live ? '1' : '0'); } @@ -833,7 +842,7 @@ static int gpio_sim_add_hogs(struct gpio_sim_device *dev) GFP_KERNEL); else hog->chip_label = kasprintf(GFP_KERNEL, - "gpio-sim.%u-%pfwP", + "gpio-sim.%u:%pfwP", dev->id, bank->swnode); if (!hog->chip_label) { @@ -926,7 +935,7 @@ static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev) return false; } -static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) +static int gpio_sim_device_activate(struct gpio_sim_device *dev) { struct platform_device_info pdevinfo; struct fwnode_handle *swnode; @@ -934,6 +943,8 @@ static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) struct gpio_sim_bank *bank; int ret; + lockdep_assert_held(&dev->lock); + if (list_empty(&dev->bank_list)) return -ENODATA; @@ -998,10 +1009,12 @@ static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) return 0; } -static void gpio_sim_device_deactivate_unlocked(struct gpio_sim_device *dev) +static void gpio_sim_device_deactivate(struct gpio_sim_device *dev) { struct fwnode_handle *swnode; + lockdep_assert_held(&dev->lock); + swnode = dev_fwnode(&dev->pdev->dev); platform_device_unregister(dev->pdev); gpio_sim_remove_hogs(dev); @@ -1009,6 +1022,33 @@ static void gpio_sim_device_deactivate_unlocked(struct gpio_sim_device *dev) dev->pdev = NULL; } +static void +gpio_sim_device_lockup_configfs(struct gpio_sim_device *dev, bool lock) +{ + struct configfs_subsystem *subsys = dev->group.cg_subsys; + struct gpio_sim_bank *bank; + struct gpio_sim_line *line; + struct config_item *item; + + /* + * The device only needs to depend on leaf entries. This is + * sufficient to lock up all the configfs entries that the + * instantiated, alive device depends on. + */ + list_for_each_entry(bank, &dev->bank_list, siblings) { + list_for_each_entry(line, &bank->line_list, siblings) { + item = line->hog ? &line->hog->item + : &line->group.cg_item; + + if (lock) + WARN_ON(configfs_depend_item_unlocked(subsys, + item)); + else + configfs_undepend_item_unlocked(item); + } + } +} + static ssize_t gpio_sim_device_config_live_store(struct config_item *item, const char *page, size_t count) @@ -1021,14 +1061,24 @@ gpio_sim_device_config_live_store(struct config_item *item, if (ret) return ret; - guard(mutex)(&dev->lock); + if (live) + gpio_sim_device_lockup_configfs(dev, true); - if (live == gpio_sim_device_is_live_unlocked(dev)) - ret = -EPERM; - else if (live) - ret = gpio_sim_device_activate_unlocked(dev); - else - gpio_sim_device_deactivate_unlocked(dev); + scoped_guard(mutex, &dev->lock) { + if (live == gpio_sim_device_is_live(dev)) + ret = -EPERM; + else if (live) + ret = gpio_sim_device_activate(dev); + else + gpio_sim_device_deactivate(dev); + } + + /* + * Undepend is required only if device disablement (live == 0) + * succeeds or if device enablement (live == 1) fails. + */ + if (live == !!ret) + gpio_sim_device_lockup_configfs(dev, false); return ret ?: count; } @@ -1069,7 +1119,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return device_for_each_child(&dev->pdev->dev, &ctx, gpio_sim_emit_chip_name); @@ -1098,7 +1148,7 @@ static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; trimmed = gpio_sim_strdup_trimmed(page, count); @@ -1142,7 +1192,7 @@ gpio_sim_bank_config_num_lines_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; bank->num_lines = num_lines; @@ -1179,7 +1229,7 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; trimmed = gpio_sim_strdup_trimmed(page, count); @@ -1219,7 +1269,7 @@ static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; trimmed = gpio_sim_strdup_trimmed(page, count); @@ -1274,7 +1324,7 @@ gpio_sim_hog_config_direction_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; if (sysfs_streq(page, "input")) @@ -1392,7 +1442,7 @@ gpio_sim_bank_config_make_line_group(struct config_group *group, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return ERR_PTR(-EBUSY); line = kzalloc(sizeof(*line), GFP_KERNEL); @@ -1445,7 +1495,7 @@ gpio_sim_device_config_make_bank_group(struct config_group *group, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return ERR_PTR(-EBUSY); bank = kzalloc(sizeof(*bank), GFP_KERNEL); @@ -1467,8 +1517,8 @@ static void gpio_sim_device_config_group_release(struct config_item *item) struct gpio_sim_device *dev = to_gpio_sim_device(item); scoped_guard(mutex, &dev->lock) { - if (gpio_sim_device_is_live_unlocked(dev)) - gpio_sim_device_deactivate_unlocked(dev); + if (gpio_sim_device_is_live(dev)) + gpio_sim_device_deactivate(dev); } mutex_destroy(&dev->lock); diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c new file mode 100644 index 000000000000..8cf3b171c599 --- /dev/null +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -0,0 +1,345 @@ +// 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, + .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; + + ret = devm_mutex_init(dev, &priv->blob_lock); + if (ret) + return ret; + + 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 = 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..222279a9d82b 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 */ @@ -192,7 +191,7 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) [REG_IE][CSB] = STMPE_IDX_IEGPIOR_CSB, [REG_IE][MSB] = STMPE_IDX_IEGPIOR_MSB, }; - int i, j; + int ret, i, j; /* * STMPE1600: to be able to get IRQ from pins, @@ -200,8 +199,16 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) * GPSR or GPCR registers */ if (stmpe->partnum == STMPE1600) { - stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_LSB]); - stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_CSB]); + ret = stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_LSB]); + if (ret < 0) { + dev_err(stmpe->dev, "Failed to read GPMR_LSB: %d\n", ret); + goto err; + } + ret = stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_CSB]); + if (ret < 0) { + dev_err(stmpe->dev, "Failed to read GPMR_CSB: %d\n", ret); + goto err; + } } for (i = 0; i < CACHE_NR_REGS; i++) { @@ -223,6 +230,7 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) } } +err: mutex_unlock(&stmpe_gpio->irq_lock); } @@ -464,59 +472,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 +528,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-tangier.c b/drivers/gpio/gpio-tangier.c index b75e0b12087a..a415e6d36173 100644 --- a/drivers/gpio/gpio-tangier.c +++ b/drivers/gpio/gpio-tangier.c @@ -195,7 +195,8 @@ static int tng_gpio_set_config(struct gpio_chip *chip, unsigned int offset, static void tng_irq_ack(struct irq_data *d) { - struct tng_gpio *priv = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tng_gpio *priv = gpiochip_get_data(gc); irq_hw_number_t gpio = irqd_to_hwirq(d); void __iomem *gisr; u8 shift; @@ -227,7 +228,8 @@ static void tng_irq_unmask_mask(struct tng_gpio *priv, u32 gpio, bool unmask) static void tng_irq_mask(struct irq_data *d) { - struct tng_gpio *priv = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tng_gpio *priv = gpiochip_get_data(gc); irq_hw_number_t gpio = irqd_to_hwirq(d); tng_irq_unmask_mask(priv, gpio, false); @@ -236,7 +238,8 @@ static void tng_irq_mask(struct irq_data *d) static void tng_irq_unmask(struct irq_data *d) { - struct tng_gpio *priv = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tng_gpio *priv = gpiochip_get_data(gc); irq_hw_number_t gpio = irqd_to_hwirq(d); gpiochip_enable_irq(&priv->chip, gpio); @@ -456,7 +459,7 @@ int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio) return 0; } -EXPORT_SYMBOL_NS_GPL(devm_tng_gpio_probe, GPIO_TANGIER); +EXPORT_SYMBOL_NS_GPL(devm_tng_gpio_probe, "GPIO_TANGIER"); static int tng_gpio_suspend(struct device *dev) { diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index e8c1485b9c73..b6335cde455f 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -235,7 +235,7 @@ MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids); static struct platform_driver tb10x_gpio_driver = { .probe = tb10x_gpio_probe, - .remove_new = tb10x_gpio_remove, + .remove = tb10x_gpio_remove, .driver = { .name = "tb10x-gpio", .of_match_table = tb10x_gpio_dt_ids, diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index ea5f9cc14bc4..9ad286adf263 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) @@ -599,7 +600,7 @@ static void tegra_gpio_irq_print_chip(struct irq_data *d, struct seq_file *s) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); - seq_printf(s, dev_name(chip->parent)); + seq_puts(s, dev_name(chip->parent)); } static const struct irq_chip tegra_gpio_irq_chip = { @@ -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 d87dd06db40d..6895b65c86af 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> @@ -36,12 +37,6 @@ #define TEGRA186_GPIO_SCR_SEC_REN BIT(27) #define TEGRA186_GPIO_SCR_SEC_G1W BIT(9) #define TEGRA186_GPIO_SCR_SEC_G1R BIT(1) -#define TEGRA186_GPIO_FULL_ACCESS (TEGRA186_GPIO_SCR_SEC_WEN | \ - TEGRA186_GPIO_SCR_SEC_REN | \ - TEGRA186_GPIO_SCR_SEC_G1R | \ - TEGRA186_GPIO_SCR_SEC_G1W) -#define TEGRA186_GPIO_SCR_SEC_ENABLE (TEGRA186_GPIO_SCR_SEC_WEN | \ - TEGRA186_GPIO_SCR_SEC_REN) /* control registers */ #define TEGRA186_GPIO_ENABLE_CONFIG 0x00 @@ -177,10 +172,18 @@ static inline bool tegra186_gpio_is_accessible(struct tegra_gpio *gpio, unsigned value = __raw_readl(secure + TEGRA186_GPIO_SCR); - if ((value & TEGRA186_GPIO_SCR_SEC_ENABLE) == 0) - return true; + /* + * When SCR_SEC_[R|W]EN is unset, then we have full read/write access to all the + * registers for given GPIO pin. + * When SCR_SEC[R|W]EN is set, then there is need to further check the accompanying + * SCR_SEC_G1[R|W] bit to determine read/write access to all the registers for given + * GPIO pin. + */ - if ((value & TEGRA186_GPIO_FULL_ACCESS) == TEGRA186_GPIO_FULL_ACCESS) + if (((value & TEGRA186_GPIO_SCR_SEC_REN) == 0 || + ((value & TEGRA186_GPIO_SCR_SEC_REN) && (value & TEGRA186_GPIO_SCR_SEC_G1R))) && + ((value & TEGRA186_GPIO_SCR_SEC_WEN) == 0 || + ((value & TEGRA186_GPIO_SCR_SEC_WEN) && (value & TEGRA186_GPIO_SCR_SEC_G1W)))) return true; return false; @@ -607,7 +610,7 @@ static void tegra186_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip tegra186_gpio_irq_chip = { @@ -926,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-tps65219.c b/drivers/gpio/gpio-tps65219.c index cd1f17041f8c..526640c39a11 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -15,8 +15,6 @@ #define TPS65219_GPIO0_DIR_MASK BIT(3) #define TPS65219_GPIO0_OFFSET 2 #define TPS65219_GPIO0_IDX 0 -#define TPS65219_GPIO_DIR_IN 1 -#define TPS65219_GPIO_DIR_OUT 0 struct tps65219_gpio { struct gpio_chip gpio_chip; @@ -61,7 +59,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) * status bit. */ - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return -ENOTSUPP; return ret; @@ -124,10 +122,10 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs return -ENOTSUPP; } - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_IN) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN) return 0; - return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_IN); + return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_IN); } static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) @@ -136,10 +134,10 @@ static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int off if (offset != TPS65219_GPIO0_IDX) return 0; - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return 0; - return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_OUT); + return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_OUT); } static const struct gpio_chip tps65219_template_chip = { diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 3a28c1f273c3..18f523a15b3c 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -6,6 +6,7 @@ * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru> */ +#include <linux/bitmap.h> #include <linux/bitops.h> #include <linux/errno.h> #include <linux/gpio/driver.h> @@ -28,17 +29,30 @@ #define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ #define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ -#define TQMX86_GPII_FALLING BIT(0) -#define TQMX86_GPII_RISING BIT(1) -#define TQMX86_GPII_MASK (BIT(0) | BIT(1)) -#define TQMX86_GPII_BITS 2 +/* + * NONE, FALLING and RISING use the same bit patterns that can be programmed to + * the GPII register (after passing them to the TQMX86_GPII_ macros to shift + * them to the right position) + */ +#define TQMX86_INT_TRIG_NONE 0 +#define TQMX86_INT_TRIG_FALLING BIT(0) +#define TQMX86_INT_TRIG_RISING BIT(1) +#define TQMX86_INT_TRIG_BOTH (BIT(0) | BIT(1)) +#define TQMX86_INT_TRIG_MASK (BIT(0) | BIT(1)) +/* Stored in irq_type with GPII bits */ +#define TQMX86_INT_UNMASKED BIT(2) + +#define TQMX86_GPIIC_CONFIG(i, v) ((v) << (2 * (i))) +#define TQMX86_GPIIC_MASK(i) TQMX86_GPIIC_CONFIG(i, TQMX86_INT_TRIG_MASK) struct tqmx86_gpio_data { struct gpio_chip chip; void __iomem *io_base; int irq; + /* Lock must be held for accessing output and irq_type fields */ raw_spinlock_t spinlock; - u8 irq_type[TQMX86_NGPI]; + DECLARE_BITMAP(output, TQMX86_NGPIO); + u8 irq_type[TQMX86_NGPIO]; }; static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg) @@ -52,6 +66,18 @@ static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val, iowrite8(val, gd->io_base + reg); } +static void tqmx86_gpio_clrsetbits(struct tqmx86_gpio_data *gpio, + u8 clr, u8 set, unsigned int reg) + __must_hold(&gpio->spinlock) +{ + u8 val = tqmx86_gpio_read(gpio, reg); + + val &= ~clr; + val |= set; + + tqmx86_gpio_write(gpio, val, reg); +} + static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); @@ -59,122 +85,137 @@ static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset)); } +static void _tqmx86_gpio_set(struct tqmx86_gpio_data *gpio, unsigned int offset, + int value) + __must_hold(&gpio->spinlock) +{ + __assign_bit(offset, gpio->output, value); + tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); +} + static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); - unsigned long flags; - u8 val; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - val = tqmx86_gpio_read(gpio, TQMX86_GPIOD); - if (value) - val |= BIT(offset); - else - val &= ~BIT(offset); - tqmx86_gpio_write(gpio, val, TQMX86_GPIOD); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + _tqmx86_gpio_set(gpio, offset, value); } static int tqmx86_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { - /* Direction cannot be changed. Validate is an input. */ - if (BIT(offset) & TQMX86_DIR_INPUT_MASK) - return 0; - else - return -EINVAL; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + tqmx86_gpio_clrsetbits(gpio, BIT(offset), 0, TQMX86_GPIODD); + + return 0; } static int tqmx86_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { - /* Direction cannot be changed, validate is an output */ - if (BIT(offset) & TQMX86_DIR_INPUT_MASK) - return -EINVAL; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + _tqmx86_gpio_set(gpio, offset, value); + tqmx86_gpio_clrsetbits(gpio, 0, BIT(offset), TQMX86_GPIODD); - tqmx86_gpio_set(chip, offset, value); return 0; } static int tqmx86_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - if (TQMX86_DIR_INPUT_MASK & BIT(offset)) - return GPIO_LINE_DIRECTION_IN; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + u8 val; + + val = tqmx86_gpio_read(gpio, TQMX86_GPIODD); + + if (val & BIT(offset)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int hwirq) + __must_hold(&gpio->spinlock) +{ + u8 type = TQMX86_INT_TRIG_NONE; + int gpiic_irq = hwirq - TQMX86_NGPO; + + if (gpio->irq_type[hwirq] & TQMX86_INT_UNMASKED) { + type = gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK; + + if (type == TQMX86_INT_TRIG_BOTH) + type = tqmx86_gpio_get(&gpio->chip, hwirq) + ? TQMX86_INT_TRIG_FALLING + : TQMX86_INT_TRIG_RISING; + } - return GPIO_LINE_DIRECTION_OUT; + tqmx86_gpio_clrsetbits(gpio, + TQMX86_GPIIC_MASK(gpiic_irq), + TQMX86_GPIIC_CONFIG(gpiic_irq, type), + TQMX86_GPIIC); } static void tqmx86_gpio_irq_mask(struct irq_data *data) { - unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned long flags; - u8 gpiic, mask; - mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + scoped_guard(raw_spinlock_irqsave, &gpio->spinlock) { + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, data->hwirq); + } - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~mask; - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); } static void tqmx86_gpio_irq_unmask(struct irq_data *data) { - unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned long flags; - u8 gpiic, mask; - - mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~mask; - gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + gpio->irq_type[data->hwirq] |= TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, data->hwirq); } static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) { struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned int offset = (data->hwirq - TQMX86_NGPO); unsigned int edge_type = type & IRQF_TRIGGER_MASK; - unsigned long flags; - u8 new_type, gpiic; + u8 new_type; switch (edge_type) { case IRQ_TYPE_EDGE_RISING: - new_type = TQMX86_GPII_RISING; + new_type = TQMX86_INT_TRIG_RISING; break; case IRQ_TYPE_EDGE_FALLING: - new_type = TQMX86_GPII_FALLING; + new_type = TQMX86_INT_TRIG_FALLING; break; case IRQ_TYPE_EDGE_BOTH: - new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; + new_type = TQMX86_INT_TRIG_BOTH; break; default: return -EINVAL; /* not supported */ } - gpio->irq_type[offset] = new_type; + guard(raw_spinlock_irqsave)(&gpio->spinlock); - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS)); - gpiic |= new_type << (offset * TQMX86_GPII_BITS); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_TRIG_MASK; + gpio->irq_type[data->hwirq] |= new_type; + tqmx86_gpio_irq_config(gpio, data->hwirq); return 0; } @@ -185,7 +226,7 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); struct irq_chip *irq_chip = irq_desc_get_chip(desc); unsigned long irq_bits; - int i = 0; + int i, hwirq; u8 irq_status; chained_irq_enter(irq_chip, desc); @@ -194,6 +235,40 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); irq_bits = irq_status; + + scoped_guard(raw_spinlock_irqsave, &gpio->spinlock) { + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + hwirq = i + TQMX86_NGPO; + + /* + * Edge-both triggers are implemented by flipping the + * edge trigger after each interrupt, as the controller + * only supports either rising or falling edge triggers, + * but not both. + * + * Internally, the TQMx86 GPIO controller has separate + * status registers for rising and falling edge + * interrupts. GPIIC configures which bits from which + * register are visible in the interrupt status register + * GPIIS and defines what triggers the parent IRQ line. + * Writing to GPIIS always clears both rising and + * falling interrupt flags internally, regardless of the + * currently configured trigger. + * + * In consequence, we can cleanly implement the + * edge-both trigger in software by first clearing the + * interrupt and then setting the new trigger based on + * the current GPIO input in tqmx86_gpio_irq_config() - + * even if an edge arrives between reading the input and + * setting the trigger, we will have a new interrupt + * pending. + */ + if ((gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK) == + TQMX86_INT_TRIG_BOTH) + tqmx86_gpio_irq_config(gpio, hwirq); + } + } + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) generic_handle_domain_irq(gpio->chip.irq.domain, i + TQMX86_NGPO); @@ -232,7 +307,7 @@ static void tqmx86_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - seq_printf(p, gc->label); + seq_puts(p, gc->label); } static const struct irq_chip tqmx86_gpio_irq_chip = { @@ -277,6 +352,13 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + /* + * Reading the previous output state is not possible with TQMx86 hardware. + * Initialize all outputs to 0 to have a defined state that matches the + * shadow register. + */ + tqmx86_gpio_write(gpio, 0, TQMX86_GPIOD); + chip = &gpio->chip; chip->label = "gpio-tqmx86"; chip->owner = THIS_MODULE; diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index 0f6397b77c9d..5c806140fdf0 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -8,8 +8,8 @@ #include <linux/gpio/driver.h> #include <linux/i2c.h> -#include <linux/of.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/regmap.h> #define DEFAULT_PIN_NUMBER 32 @@ -142,7 +142,7 @@ static int ts4900_gpio_probe(struct i2c_client *client) u32 ngpio; int ret; - if (of_property_read_u32(client->dev.of_node, "ngpios", &ngpio)) + if (device_property_read_u32(&client->dev, "ngpios", &ngpio)) ngpio = DEFAULT_PIN_NUMBER; priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); @@ -153,7 +153,7 @@ static int ts4900_gpio_probe(struct i2c_client *client) priv->gpio_chip.label = "ts4900-gpio"; priv->gpio_chip.ngpio = ngpio; priv->gpio_chip.parent = &client->dev; - priv->input_bit = (uintptr_t)of_device_get_match_data(&client->dev); + priv->input_bit = (uintptr_t)device_get_match_data(&client->dev); priv->regmap = devm_regmap_init_i2c(client, &ts4900_regmap_config); if (IS_ERR(priv->regmap)) { diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index 90f8e9e9915e..61cbec5c06a7 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -433,7 +433,7 @@ static struct platform_driver ts5500_dio_driver = { .name = "ts5500-dio", }, .probe = ts5500_dio_probe, - .remove_new = ts5500_dio_remove, + .remove = ts5500_dio_remove, .id_table = ts5500_dio_ids, }; diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index 6c3fbf382dba..b9171bf66168 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -22,7 +22,7 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset) { - struct twl6040 *twl6040 = dev_get_drvdata(chip->parent->parent); + struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret = 0; ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL); @@ -46,7 +46,7 @@ static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) { - struct twl6040 *twl6040 = dev_get_drvdata(chip->parent->parent); + struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret; u8 gpoctl; @@ -91,7 +91,7 @@ static int gpo_twl6040_probe(struct platform_device *pdev) twl6040gpo_chip.parent = &pdev->dev; - ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, NULL); + ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, twl6040); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); twl6040gpo_chip.ngpio = 0; diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 1f440707f8f4..d738da8718f9 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; @@ -480,7 +481,7 @@ MODULE_DEVICE_TABLE(of, uniphier_gpio_match); static struct platform_driver uniphier_gpio_driver = { .probe = uniphier_gpio_probe, - .remove_new = uniphier_gpio_remove, + .remove = uniphier_gpio_remove, .driver = { .name = "uniphier-gpio", .of_match_table = uniphier_gpio_match, diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 07e5e6323e86..c36a9dbccd4d 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -15,10 +15,9 @@ #include <linux/io.h> #include <linux/ioport.h> #include <linux/irq.h> -#include <linux/platform_device.h> -#include <linux/of.h> -#include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/property.h> #define VF610_GPIO_PER_PORT 32 @@ -37,6 +36,7 @@ struct vf610_gpio_port { struct clk *clk_port; struct clk *clk_gpio; int irq; + spinlock_t lock; /* protect gpio direction registers */ }; #define GPIO_PDOR 0x00 @@ -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,19 +112,20 @@ 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) { + guard(spinlock_irqsave)(&port->lock); val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR); val &= ~mask; vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR); @@ -133,16 +134,17 @@ 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); if (port->sdata->have_paddr) { + guard(spinlock_irqsave)(&port->lock); val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR); val |= mask; vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR); @@ -151,6 +153,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 = @@ -284,7 +299,8 @@ static int vf610_gpio_probe(struct platform_device *pdev) if (!port) return -ENOMEM; - port->sdata = of_device_get_match_data(dev); + port->sdata = device_get_match_data(dev); + spin_lock_init(&port->lock); dual_base = port->sdata->have_dual_base; @@ -362,6 +378,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 fcc5e8c08973..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; @@ -653,7 +653,6 @@ static struct virtio_driver virtio_gpio_driver = { .remove = virtio_gpio_remove, .driver = { .name = KBUILD_MODNAME, - .owner = THIS_MODULE, }, }; module_virtio_driver(virtio_gpio_driver); diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c new file mode 100644 index 000000000000..e89f299f2140 --- /dev/null +++ b/drivers/gpio/gpio-virtuser.c @@ -0,0 +1,1849 @@ +// 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, idx; + + 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) { + idx = 0; + 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, idx++, entry->flags); + } + } + + gpiod_add_lookup_table(table); + dev->lookup_table = no_free_ptr(table); + + return 0; +} + +static void +gpio_virtuser_remove_lookup_table(struct gpio_virtuser_device *dev) +{ + gpiod_remove_lookup_table(dev->lookup_table); + kfree(dev->lookup_table->dev_id); + kfree(dev->lookup_table); + dev->lookup_table = NULL; +} + +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) + goto err_remove_swnode; + + 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)) { + ret = PTR_ERR(pdev); + bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); + goto err_remove_lookup_table; + } + + wait_for_completion(&dev->probe_completion); + bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); + + if (!dev->driver_bound) { + ret = -ENXIO; + goto err_unregister_pdev; + } + + dev->pdev = pdev; + + return 0; + +err_unregister_pdev: + platform_device_unregister(pdev); +err_remove_lookup_table: + gpio_virtuser_remove_lookup_table(dev); +err_remove_swnode: + fwnode_remove_software_node(swnode); + + return ret; +} + +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); + gpio_virtuser_remove_lookup_table(dev); + fwnode_remove_software_node(swnode); + dev->pdev = NULL; +} + +static void +gpio_virtuser_device_lockup_configfs(struct gpio_virtuser_device *dev, bool lock) +{ + struct configfs_subsystem *subsys = dev->group.cg_subsys; + struct gpio_virtuser_lookup_entry *entry; + struct gpio_virtuser_lookup *lookup; + + /* + * The device only needs to depend on leaf lookup entries. This is + * sufficient to lock up all the configfs entries that the + * instantiated, alive device depends on. + */ + list_for_each_entry(lookup, &dev->lookup_list, siblings) { + list_for_each_entry(entry, &lookup->entry_list, siblings) { + if (lock) + WARN_ON(configfs_depend_item_unlocked( + subsys, &entry->group.cg_item)); + else + configfs_undepend_item_unlocked( + &entry->group.cg_item); + } + } +} + +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; + + if (live) + gpio_virtuser_device_lockup_configfs(dev, true); + + scoped_guard(mutex, &dev->lock) { + if (live == gpio_virtuser_device_is_live(dev)) + ret = -EPERM; + else if (live) + ret = gpio_virtuser_device_activate(dev); + else + gpio_virtuser_device_deactivate(dev); + } + + /* + * Undepend is required only if device disablement (live == 0) + * succeeds or if device enablement (live == 1) fails. + */ + if (live == !!ret) + gpio_virtuser_device_lockup_configfs(dev, false); + + 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..5bd965c18a46 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 @@ -141,7 +142,7 @@ static void visconti_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct visconti_gpio *priv = gpiochip_get_data(gc); - seq_printf(p, dev_name(priv->dev)); + seq_puts(p, dev_name(priv->dev)); } static const struct irq_chip visconti_gpio_irq_chip = { @@ -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-wcove.c b/drivers/gpio/gpio-wcove.c index c18b6b47384f..94ca9d03c094 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -104,7 +104,7 @@ static inline int to_reg(int gpio, enum ctrl_register type) unsigned int reg = type == CTRL_IN ? GPIO_IN_CTRL_BASE : GPIO_OUT_CTRL_BASE; if (gpio >= WCOVE_GPIO_NUM) - return -EOPNOTSUPP; + return -ENOTSUPP; return reg + gpio; } diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index bd5befa807c3..48b829733b15 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -8,20 +8,22 @@ * Quan Nguyen <qnguyen@apm.com>. */ -#include <linux/module.h> +#include <linux/device.h> +#include <linux/err.h> #include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/types.h> + #include <linux/gpio/driver.h> -#include <linux/acpi.h> #include "gpiolib-acpi.h" -/* Common property names */ -#define XGENE_NIRQ_PROPERTY "apm,nr-irqs" -#define XGENE_NGPIO_PROPERTY "apm,nr-gpios" -#define XGENE_IRQ_START_PROPERTY "apm,irq-start" - #define XGENE_DFLT_MAX_NGPIO 22 #define XGENE_DFLT_MAX_NIRQ 6 #define XGENE_DFLT_IRQ_START_PIN 8 @@ -252,18 +254,17 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev) /* Retrieve start irq pin, use default if property not found */ priv->irq_start = XGENE_DFLT_IRQ_START_PIN; - if (!device_property_read_u32(&pdev->dev, - XGENE_IRQ_START_PROPERTY, &val32)) + if (!device_property_read_u32(&pdev->dev, "apm,irq-start", &val32)) priv->irq_start = val32; /* Retrieve number irqs, use default if property not found */ priv->nirq = XGENE_DFLT_MAX_NIRQ; - if (!device_property_read_u32(&pdev->dev, XGENE_NIRQ_PROPERTY, &val32)) + if (!device_property_read_u32(&pdev->dev, "apm,nr-irqs", &val32)) priv->nirq = val32; /* Retrieve number gpio, use default if property not found */ priv->gc.ngpio = XGENE_DFLT_MAX_NGPIO; - if (!device_property_read_u32(&pdev->dev, XGENE_NGPIO_PROPERTY, &val32)) + if (!device_property_read_u32(&pdev->dev, "apm,nr-gpios", &val32)) priv->gc.ngpio = val32; dev_info(&pdev->dev, "Support %d gpios, %d irqs start from pin %d\n", @@ -305,27 +306,25 @@ static void xgene_gpio_sb_remove(struct platform_device *pdev) } static const struct of_device_id xgene_gpio_sb_of_match[] = { - {.compatible = "apm,xgene-gpio-sb", }, - {}, + { .compatible = "apm,xgene-gpio-sb" }, + {} }; MODULE_DEVICE_TABLE(of, xgene_gpio_sb_of_match); -#ifdef CONFIG_ACPI static const struct acpi_device_id xgene_gpio_sb_acpi_match[] = { - {"APMC0D15", 0}, - {}, + { "APMC0D15" }, + {} }; MODULE_DEVICE_TABLE(acpi, xgene_gpio_sb_acpi_match); -#endif static struct platform_driver xgene_gpio_sb_driver = { .driver = { .name = "xgene-gpio-sb", .of_match_table = xgene_gpio_sb_of_match, - .acpi_match_table = ACPI_PTR(xgene_gpio_sb_acpi_match), - }, + .acpi_match_table = xgene_gpio_sb_acpi_match, + }, .probe = xgene_gpio_sb_probe, - .remove_new = xgene_gpio_sb_remove, + .remove = xgene_gpio_sb_remove, }; module_platform_driver(xgene_gpio_sb_driver); diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c index d445eea03687..93544e98ccbd 100644 --- a/drivers/gpio/gpio-xgs-iproc.c +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -198,7 +198,7 @@ static void iproc_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct iproc_gpio_chip *chip = to_iproc_gpio(gc); - seq_printf(p, dev_name(chip->dev)); + seq_puts(p, dev_name(chip->dev)); } static const struct irq_chip iproc_gpio_irq_chip = { @@ -316,7 +316,7 @@ static struct platform_driver bcm_iproc_gpio_driver = { .of_match_table = bcm_iproc_gpio_of_match, }, .probe = iproc_gpio_probe, - .remove_new = iproc_gpio_remove, + .remove = iproc_gpio_remove, }; module_platform_driver(bcm_iproc_gpio_driver); diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 7348df385198..792d94c49077 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -15,9 +15,9 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> -#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/slab.h> /* Register Offset Definitions */ @@ -65,7 +65,7 @@ struct xgpio_instance { DECLARE_BITMAP(state, 64); DECLARE_BITMAP(last_irq_read, 64); DECLARE_BITMAP(dir, 64); - spinlock_t gpio_lock; /* For serializing operations */ + raw_spinlock_t gpio_lock; /* For serializing operations */ int irq; DECLARE_BITMAP(enable, 64); DECLARE_BITMAP(rising_edge, 64); @@ -179,14 +179,14 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) struct xgpio_instance *chip = gpiochip_get_data(gc); int bit = xgpio_to_bit(chip, gpio); - spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&chip->gpio_lock, flags); /* Write to GPIO signal and set its direction to output */ __assign_bit(bit, chip->state, val); xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state); - spin_unlock_irqrestore(&chip->gpio_lock, flags); + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); } /** @@ -210,7 +210,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, bitmap_remap(hw_mask, mask, chip->sw_map, chip->hw_map, 64); bitmap_remap(hw_bits, bits, chip->sw_map, chip->hw_map, 64); - spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&chip->gpio_lock, flags); bitmap_replace(state, chip->state, hw_bits, hw_mask, 64); @@ -218,7 +218,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, bitmap_copy(chip->state, state, 64); - spin_unlock_irqrestore(&chip->gpio_lock, flags); + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); } /** @@ -236,13 +236,13 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) struct xgpio_instance *chip = gpiochip_get_data(gc); int bit = xgpio_to_bit(chip, gpio); - spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&chip->gpio_lock, flags); /* Set the GPIO bit in shadow register and set direction as input */ __set_bit(bit, chip->dir); xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir); - spin_unlock_irqrestore(&chip->gpio_lock, flags); + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); return 0; } @@ -265,7 +265,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) struct xgpio_instance *chip = gpiochip_get_data(gc); int bit = xgpio_to_bit(chip, gpio); - spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&chip->gpio_lock, flags); /* Write state of GPIO signal */ __assign_bit(bit, chip->state, val); @@ -275,7 +275,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) __clear_bit(bit, chip->dir); xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir); - spin_unlock_irqrestore(&chip->gpio_lock, flags); + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); return 0; } @@ -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); } /** @@ -401,7 +398,7 @@ static void xgpio_irq_mask(struct irq_data *irq_data) int bit = xgpio_to_bit(chip, irq_offset); u32 mask = BIT(bit / 32), temp; - spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&chip->gpio_lock, flags); __clear_bit(bit, chip->enable); @@ -411,7 +408,7 @@ static void xgpio_irq_mask(struct irq_data *irq_data) temp &= ~mask; xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, temp); } - spin_unlock_irqrestore(&chip->gpio_lock, flags); + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); gpiochip_disable_irq(&chip->gc, irq_offset); } @@ -431,7 +428,7 @@ static void xgpio_irq_unmask(struct irq_data *irq_data) gpiochip_enable_irq(&chip->gc, irq_offset); - spin_lock_irqsave(&chip->gpio_lock, flags); + raw_spin_lock_irqsave(&chip->gpio_lock, flags); __set_bit(bit, chip->enable); @@ -450,7 +447,7 @@ static void xgpio_irq_unmask(struct irq_data *irq_data) xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val); } - spin_unlock_irqrestore(&chip->gpio_lock, flags); + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); } /** @@ -515,7 +512,7 @@ static void xgpio_irqhandler(struct irq_desc *desc) chained_irq_enter(irqchip, desc); - spin_lock(&chip->gpio_lock); + raw_spin_lock(&chip->gpio_lock); xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, all); @@ -532,7 +529,7 @@ static void xgpio_irqhandler(struct irq_desc *desc) bitmap_copy(chip->last_irq_read, all, 64); bitmap_or(all, rising, falling, 64); - spin_unlock(&chip->gpio_lock); + raw_spin_unlock(&chip->gpio_lock); dev_dbg(gc->parent, "IRQ rising %*pb falling %*pb\n", 64, rising, 64, falling); @@ -564,9 +561,9 @@ static const struct irq_chip xgpio_irq_chip = { */ static int xgpio_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct xgpio_instance *chip; int status = 0; - struct device_node *np = pdev->dev.of_node; u32 is_dual = 0; u32 width[2]; u32 state[2]; @@ -574,14 +571,14 @@ static int xgpio_probe(struct platform_device *pdev) struct gpio_irq_chip *girq; u32 temp; - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; platform_set_drvdata(pdev, chip); /* First, check if the device is dual-channel */ - of_property_read_u32(np, "xlnx,is-dual", &is_dual); + device_property_read_u32(dev, "xlnx,is-dual", &is_dual); /* Setup defaults */ memset32(width, 0, ARRAY_SIZE(width)); @@ -589,14 +586,14 @@ static int xgpio_probe(struct platform_device *pdev) memset32(dir, 0xFFFFFFFF, ARRAY_SIZE(dir)); /* Update GPIO state shadow register with default value */ - of_property_read_u32(np, "xlnx,dout-default", &state[0]); - of_property_read_u32(np, "xlnx,dout-default-2", &state[1]); + device_property_read_u32(dev, "xlnx,dout-default", &state[0]); + device_property_read_u32(dev, "xlnx,dout-default-2", &state[1]); bitmap_from_arr32(chip->state, state, 64); /* Update GPIO direction shadow register with default value */ - of_property_read_u32(np, "xlnx,tri-default", &dir[0]); - of_property_read_u32(np, "xlnx,tri-default-2", &dir[1]); + device_property_read_u32(dev, "xlnx,tri-default", &dir[0]); + device_property_read_u32(dev, "xlnx,tri-default-2", &dir[1]); bitmap_from_arr32(chip->dir, dir, 64); @@ -604,13 +601,13 @@ static int xgpio_probe(struct platform_device *pdev) * Check device node and parent device node for device width * and assume default width of 32 */ - if (of_property_read_u32(np, "xlnx,gpio-width", &width[0])) + if (device_property_read_u32(dev, "xlnx,gpio-width", &width[0])) width[0] = 32; if (width[0] > 32) return -EINVAL; - if (is_dual && of_property_read_u32(np, "xlnx,gpio2-width", &width[1])) + if (is_dual && device_property_read_u32(dev, "xlnx,gpio2-width", &width[1])) width[1] = 32; if (width[1] > 32) @@ -623,11 +620,11 @@ static int xgpio_probe(struct platform_device *pdev) bitmap_set(chip->hw_map, 0, width[0]); bitmap_set(chip->hw_map, 32, width[1]); - spin_lock_init(&chip->gpio_lock); + raw_spin_lock_init(&chip->gpio_lock); chip->gc.base = -1; chip->gc.ngpio = bitmap_weight(chip->hw_map, 64); - chip->gc.parent = &pdev->dev; + chip->gc.parent = dev; chip->gc.direction_input = xgpio_dir_in; chip->gc.direction_output = xgpio_dir_out; chip->gc.get = xgpio_get; @@ -636,26 +633,21 @@ static int xgpio_probe(struct platform_device *pdev) chip->gc.free = xgpio_free; chip->gc.set_multiple = xgpio_set_multiple; - chip->gc.label = dev_name(&pdev->dev); + chip->gc.label = dev_name(dev); chip->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->regs)) { - dev_err(&pdev->dev, "failed to ioremap memory resource\n"); + dev_err(dev, "failed to ioremap memory resource\n"); return PTR_ERR(chip->regs); } - chip->clk = devm_clk_get_optional(&pdev->dev, NULL); + chip->clk = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(chip->clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk), "input clock not found.\n"); + return dev_err_probe(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); + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); xgpio_save_regs(chip); @@ -675,8 +667,7 @@ static int xgpio_probe(struct platform_device *pdev) gpio_irq_chip_set_chip(girq, &xgpio_irq_chip); girq->parent_handler = xgpio_irqhandler; girq->num_parents = 1; - girq->parents = devm_kcalloc(&pdev->dev, 1, - sizeof(*girq->parents), + girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), GFP_KERNEL); if (!girq->parents) { status = -ENOMEM; @@ -687,19 +678,18 @@ static int xgpio_probe(struct platform_device *pdev) girq->handler = handle_bad_irq; skip_irq: - status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); + status = devm_gpiochip_add_data(dev, &chip->gc, chip); if (status) { - dev_err(&pdev->dev, "failed to add GPIO chip\n"); + dev_err(dev, "failed to add GPIO chip\n"); goto err_pm_put; } - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); return 0; err_pm_put: - pm_runtime_disable(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); - clk_disable_unprepare(chip->clk); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); return status; } @@ -712,7 +702,7 @@ MODULE_DEVICE_TABLE(of, xgpio_of_match); static struct platform_driver xgpio_plat_driver = { .probe = xgpio_probe, - .remove_new = xgpio_remove, + .remove = xgpio_remove, .driver = { .name = "gpio-xilinx", .of_match_table = xgpio_of_match, diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index 2de61337ad3b..d7230fd83f5d 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -11,6 +11,7 @@ #include <linux/io.h> #include <linux/mod_devicetable.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -169,6 +170,7 @@ static const struct gpio_chip zevio_gpio_chip = { /* Initialization */ static int zevio_gpio_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct zevio_gpio *controller; int status, i; @@ -180,6 +182,10 @@ static int zevio_gpio_probe(struct platform_device *pdev) controller->chip = zevio_gpio_chip; controller->chip.parent = &pdev->dev; + controller->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!controller->chip.label) + return -ENOMEM; + controller->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(controller->regs)) return dev_err_probe(&pdev->dev, PTR_ERR(controller->regs), diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 466e23031afc..be81fa2b17ab 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); } @@ -1031,7 +1023,7 @@ static struct platform_driver zynq_gpio_driver = { .of_match_table = zynq_gpio_of_match, }, .probe = zynq_gpio_probe, - .remove_new = zynq_gpio_remove, + .remove = zynq_gpio_remove, }; module_platform_driver(zynq_gpio_driver); 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 cd3e9657cc36..f7746c57ba76 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -126,9 +126,26 @@ static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); static LIST_HEAD(acpi_gpio_deferred_req_irqs_list); static bool acpi_gpio_deferred_req_irqs_done; -static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) +static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data) { - return device_match_acpi_handle(&gc->gpiodev->dev, data); + /* First check the actual GPIO device */ + if (device_match_acpi_handle(&gc->gpiodev->dev, data)) + return true; + + /* + * When the ACPI device is artificially split to the banks of GPIOs, + * where each of them is represented by a separate GPIO device, + * the firmware node of the physical device may not be shared among + * the banks as they may require different values for the same property, + * e.g., number of GPIOs in a certain bank. In such case the ACPI handle + * of a GPIO device is NULL and can not be used. Hence we have to check + * the parent device to be sure that there is no match before bailing + * out. + */ + if (gc->parent) + return device_match_acpi_handle(gc->parent, data); + + return false; } /** @@ -136,8 +153,12 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, 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. */ @@ -207,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) @@ -859,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 @@ -873,9 +899,6 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, struct acpi_gpio_lookup lookup; int ret; - if (!adev) - return ERR_PTR(-ENODEV); - memset(&lookup, 0, sizeof(lookup)); lookup.index = index; @@ -910,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. */ @@ -941,6 +966,10 @@ static struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, static bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id) { + /* If there is no ACPI device, there is no _CRS to fall back to */ + if (!adev) + return false; + /* Never allow fallback if the device has properties */ if (acpi_dev_has_props(adev) || adev->driver_gpios) return false; @@ -948,49 +977,50 @@ static bool acpi_can_fallback_to_crs(struct acpi_device *adev, return con_id == NULL; } -struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, - const char *con_id, - unsigned int idx, - enum gpiod_flags *dflags, - unsigned long *lookupflags) +static struct gpio_desc * +__acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int idx, + bool can_fallback, struct acpi_gpio_info *info) { struct acpi_device *adev = to_acpi_device_node(fwnode); - struct acpi_gpio_info info; struct gpio_desc *desc; char propname[32]; - int i; /* 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); + propname, idx, info); else desc = acpi_get_gpiod_from_data(fwnode, - propname, idx, &info); - if (!IS_ERR(desc)) - break; + propname, idx, info); if (PTR_ERR(desc) == -EPROBE_DEFER) return ERR_CAST(desc); + + if (!IS_ERR(desc)) + return desc; } /* Then from plain _CRS GPIOs */ - if (IS_ERR(desc)) { - if (!adev || !acpi_can_fallback_to_crs(adev, con_id)) - return ERR_PTR(-ENOENT); + if (can_fallback) + return acpi_get_gpiod_by_index(adev, NULL, idx, info); - desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info); - if (IS_ERR(desc)) - return desc; - } + return ERR_PTR(-ENOENT); +} + +struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, + const char *con_id, + unsigned int idx, + enum gpiod_flags *dflags, + unsigned long *lookupflags) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + bool can_fallback = acpi_can_fallback_to_crs(adev, con_id); + struct acpi_gpio_info info; + struct gpio_desc *desc; + + desc = __acpi_find_gpio(fwnode, con_id, idx, can_fallback, &info); + if (IS_ERR(desc)) + return desc; if (info.gpioint && (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { @@ -1006,7 +1036,7 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, /** * acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number * @adev: pointer to a ACPI device to get IRQ from - * @name: optional name of GpioInt resource + * @con_id: optional name of GpioInt resource * @index: index of GpioInt resource (starting from %0) * @wake_capable: Set to true if the IRQ is wake capable * @@ -1017,17 +1047,19 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, * The function is idempotent, though each time it runs it will configure GPIO * pin direction according to the flags in GpioInt resource. * - * The function takes optional @name parameter. If the resource has a property - * name, then only those will be taken into account. + * The function takes optional @con_id parameter. If the resource has + * a @con_id in a property, then only those will be taken into account. * * The GPIO is considered wake capable if the GpioInt resource specifies * SharedAndWake or ExclusiveAndWake. * - * Return: Linux IRQ number (> %0) on success, negative errno on failure. + * 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 *name, int index, +int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *con_id, int index, bool *wake_capable) { + struct fwnode_handle *fwnode = acpi_fwnode_handle(adev); int idx, i; unsigned int irq_flags; int ret; @@ -1036,9 +1068,8 @@ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, in struct acpi_gpio_info info; struct gpio_desc *desc; - desc = acpi_get_gpiod_by_index(adev, name, i, &info); - /* Ignore -EPROBE_DEFER, it only matters if idx matches */ + desc = __acpi_find_gpio(fwnode, con_id, i, true, &info); if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) return PTR_ERR(desc); @@ -1058,7 +1089,11 @@ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, in acpi_gpio_update_gpiod_flags(&dflags, &info); acpi_gpio_update_gpiod_lookup_flags(&lflags, &info); - snprintf(label, sizeof(label), "GpioInt() %d", index); + snprintf(label, sizeof(label), "%pfwP GpioInt(%d)", fwnode, index); + ret = gpiod_set_consumer_name(desc, con_id ?: label); + if (ret) + return ret; + ret = gpiod_configure_flags(desc, label, lflags, dflags); if (ret < 0) return ret; @@ -1280,9 +1315,8 @@ acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip, static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip) { struct gpio_chip *chip = achip->chip; - struct fwnode_handle *fwnode; - device_for_each_child_node(chip->parent, fwnode) { + device_for_each_child_node_scoped(chip->parent, fwnode) { unsigned long lflags; enum gpiod_flags dflags; struct gpio_desc *desc; @@ -1300,7 +1334,6 @@ static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip) ret = gpiod_hog(desc, name, lflags, dflags); if (ret) { dev_err(chip->parent, "Failed to hog GPIO\n"); - fwnode_handle_put(fwnode); return; } } @@ -1402,35 +1435,26 @@ static int acpi_find_gpio_count(struct acpi_resource *ares, void *data) } /** - * acpi_gpio_count - count the GPIOs associated with a device / function - * @dev: GPIO consumer, can be %NULL for system-global GPIOs + * acpi_gpio_count - count the GPIOs associated with a firmware node / function + * @fwnode: firmware node of the GPIO consumer * @con_id: function within the GPIO consumer * - * Return: - * The number of GPIOs associated with a device / function or %-ENOENT, + * Returns: + * The number of GPIOs associated with a firmware node / function or %-ENOENT, * if no GPIO has been assigned to the requested function. */ -int acpi_gpio_count(struct device *dev, const char *con_id) +int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) { - struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *adev = to_acpi_device_node(fwnode); const union acpi_object *obj; const struct acpi_gpio_mapping *gm; 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]); - - ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, - &obj); + 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) count = 1; @@ -1665,6 +1689,20 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { .ignore_wake = "PNP0C50:00@8", }, }, + { + /* + * Spurious wakeups from GPIO 11 + * Found in BIOS 1.04 + * https://gitlab.freedesktop.org/drm/amd/-/issues/3954 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 14"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "AMDI0030:00@11", + }, + }, {} /* Terminating entry */ }; diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h index 0fcd7e14d7f9..7e1c51d04040 100644 --- a/drivers/gpio/gpiolib-acpi.h +++ b/drivers/gpio/gpiolib-acpi.h @@ -33,7 +33,7 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, enum gpiod_flags *dflags, unsigned long *lookupflags); -int acpi_gpio_count(struct device *dev, const char *con_id); +int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id); #else static inline void acpi_gpiochip_add(struct gpio_chip *chip) { } static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { } @@ -51,7 +51,8 @@ acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, { return ERR_PTR(-ENOENT); } -static inline int acpi_gpio_count(struct device *dev, const char *con_id) +static inline int acpi_gpio_count(const struct fwnode_handle *fwnode, + const char *con_id) { return -ENODEV; } diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 2a88736629ef..107d75558b5a 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -16,17 +16,15 @@ #include <linux/hte.h> #include <linux/interrupt.h> #include <linux/irqreturn.h> -#include <linux/kernel.h> #include <linux/kfifo.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/overflow.h> #include <linux/pinctrl/consumer.h> #include <linux/poll.h> -#include <linux/rbtree.h> -#include <linux/rwsem.h> #include <linux/seq_file.h> #include <linux/spinlock.h> +#include <linux/string.h> #include <linux/timekeeping.h> #include <linux/uaccess.h> #include <linux/workqueue.h> @@ -61,11 +59,6 @@ static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8)); * interface to gpiolib GPIOs via ioctl()s. */ -typedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *); -typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long); -typedef ssize_t (*read_fn)(struct file *, char __user *, - size_t count, loff_t *); - /* * GPIO line handle management */ @@ -95,6 +88,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 */ @@ -145,18 +142,22 @@ static int linehandle_validate_flags(u32 flags) static void linehandle_flags_to_desc_flags(u32 lflags, unsigned long *flagsp) { - assign_bit(FLAG_ACTIVE_LOW, flagsp, + unsigned long flags = READ_ONCE(*flagsp); + + assign_bit(FLAG_ACTIVE_LOW, &flags, lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); - assign_bit(FLAG_OPEN_DRAIN, flagsp, + assign_bit(FLAG_OPEN_DRAIN, &flags, lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); - assign_bit(FLAG_OPEN_SOURCE, flagsp, + assign_bit(FLAG_OPEN_SOURCE, &flags, lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); - assign_bit(FLAG_PULL_UP, flagsp, + assign_bit(FLAG_PULL_UP, &flags, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); - assign_bit(FLAG_PULL_DOWN, flagsp, + assign_bit(FLAG_PULL_DOWN, &flags, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); - assign_bit(FLAG_BIAS_DISABLE, flagsp, + assign_bit(FLAG_BIAS_DISABLE, &flags, lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); + + WRITE_ONCE(*flagsp, flags); } static long linehandle_set_config(struct linehandle_state *lh, @@ -175,22 +176,22 @@ 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); + ret = gpiod_direction_output_nonotify(desc, val); if (ret) return ret; - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); + } else { + ret = gpiod_direction_input_nonotify(desc); if (ret) return ret; } @@ -210,9 +211,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, unsigned int i; int ret; - guard(rwsem_read)(&lh->gdev->sem); + guard(srcu)(&lh->gdev->srcu); - if (!lh->gdev->chip) + if (!rcu_access_pointer(lh->gdev->chip)) return -ENODEV; switch (cmd) { @@ -337,7 +338,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < handlereq.lines; i++) { u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) { ret = PTR_ERR(desc); @@ -361,11 +362,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { int val = !!handlereq.default_values[i]; - ret = gpiod_direction_output(desc, val); + ret = gpiod_direction_output_nonotify(desc, val); if (ret) goto out_free_lh; } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); + ret = gpiod_direction_input_nonotify(desc); if (ret) goto out_free_lh; } @@ -419,7 +420,6 @@ out_free_lh: /** * struct line - contains the state of a requested line - * @node: to store the object in supinfo_tree if supplemental * @desc: the GPIO descriptor for this line. * @req: the corresponding line request * @irq: the interrupt triggered in response to events on this GPIO @@ -432,7 +432,6 @@ out_free_lh: * @line_seqno: the seqno for the current edge event in the sequence of * events for this line. * @work: the worker that implements software debouncing - * @debounce_period_us: the debounce period in microseconds * @sw_debounced: flag indicating if the software debouncer is active * @level: the current debounced physical level of the line * @hdesc: the Hardware Timestamp Engine (HTE) descriptor @@ -441,7 +440,6 @@ out_free_lh: * @last_seqno: the last sequence number before debounce period expires */ struct line { - struct rb_node node; struct gpio_desc *desc; /* * -- edge detector specific fields -- @@ -452,7 +450,7 @@ struct line { * The flags for the active edge detector configuration. * * edflags is set by linereq_create(), linereq_free(), and - * linereq_set_config_unlocked(), which are themselves mutually + * linereq_set_config(), which are themselves mutually * exclusive, and is accessed by edge_irq_thread(), * process_hw_ts_thread() and debounce_work_func(), * which can all live with a slightly stale value. @@ -476,15 +474,6 @@ struct line { */ struct delayed_work work; /* - * debounce_period_us is accessed by debounce_irq_handler() and - * process_hw_ts() which are disabled when modified by - * debounce_setup(), edge_detector_setup() or edge_detector_stop() - * or can live with a stale version when updated by - * edge_detector_update(). - * The modifying functions are themselves mutually exclusive. - */ - unsigned int debounce_period_us; - /* * sw_debounce is accessed by linereq_set_config(), which is the * only setter, and linereq_get_values(), which can live with a * slightly stale value. @@ -516,17 +505,6 @@ struct line { #endif /* CONFIG_HTE */ }; -/* - * a rbtree of the struct lines containing supplemental info. - * Used to populate gpio_v2_line_info with cdev specific fields not contained - * in the struct gpio_desc. - * A line is determined to contain supplemental information by - * line_has_supinfo(). - */ -static struct rb_root supinfo_tree = RB_ROOT; -/* covers supinfo_tree */ -static DEFINE_SPINLOCK(supinfo_lock); - /** * struct linereq - contains the state of a userspace line request * @gdev: the GPIO device the line request pertains to @@ -540,8 +518,7 @@ static DEFINE_SPINLOCK(supinfo_lock); * this line request. Note that this is not used when @num_lines is 1, as * the line_seqno is then the same and is cheaper to calculate. * @config_mutex: mutex for serializing ioctl() calls to ensure consistency - * of configuration, particularly multi-step accesses to desc flags and - * changes to supinfo status. + * of configuration, particularly multi-step accesses to desc flags. * @lines: the lines held by this line request, with @num_lines elements. */ struct linereq { @@ -557,103 +534,6 @@ struct linereq { struct line lines[] __counted_by(num_lines); }; -static void supinfo_insert(struct line *line) -{ - struct rb_node **new = &(supinfo_tree.rb_node), *parent = NULL; - struct line *entry; - - guard(spinlock)(&supinfo_lock); - - while (*new) { - entry = container_of(*new, struct line, node); - - parent = *new; - if (line->desc < entry->desc) { - new = &((*new)->rb_left); - } else if (line->desc > entry->desc) { - new = &((*new)->rb_right); - } else { - /* this should never happen */ - WARN(1, "duplicate line inserted"); - return; - } - } - - rb_link_node(&line->node, parent, new); - rb_insert_color(&line->node, &supinfo_tree); -} - -static void supinfo_erase(struct line *line) -{ - guard(spinlock)(&supinfo_lock); - - rb_erase(&line->node, &supinfo_tree); -} - -static struct line *supinfo_find(struct gpio_desc *desc) -{ - struct rb_node *node = supinfo_tree.rb_node; - struct line *line; - - while (node) { - line = container_of(node, struct line, node); - if (desc < line->desc) - node = node->rb_left; - else if (desc > line->desc) - node = node->rb_right; - else - return line; - } - return NULL; -} - -static void supinfo_to_lineinfo(struct gpio_desc *desc, - struct gpio_v2_line_info *info) -{ - struct gpio_v2_line_attribute *attr; - struct line *line; - - guard(spinlock)(&supinfo_lock); - - line = supinfo_find(desc); - if (!line) - return; - - attr = &info->attrs[info->num_attrs]; - attr->id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; - attr->debounce_period_us = READ_ONCE(line->debounce_period_us); - info->num_attrs++; -} - -static inline bool line_has_supinfo(struct line *line) -{ - return READ_ONCE(line->debounce_period_us); -} - -/* - * Checks line_has_supinfo() before and after the change to avoid unnecessary - * supinfo_tree access. - * Called indirectly by linereq_create() or linereq_set_config() so line - * is already protected from concurrent changes. - */ -static void line_set_debounce_period(struct line *line, - unsigned int debounce_period_us) -{ - bool was_suppl = line_has_supinfo(line); - - WRITE_ONCE(line->debounce_period_us, debounce_period_us); - - /* if supinfo status is unchanged then we're done */ - if (line_has_supinfo(line) == was_suppl) - return; - - /* supinfo status has changed, so update the tree */ - if (was_suppl) - supinfo_erase(line); - else - supinfo_insert(line); -} - #define GPIO_V2_LINE_BIAS_FLAGS \ (GPIO_V2_LINE_FLAG_BIAS_PULL_UP | \ GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN | \ @@ -734,6 +614,25 @@ static u32 line_event_id(int level) GPIO_V2_LINE_EVENT_FALLING_EDGE; } +static inline char *make_irq_label(const char *orig) +{ + char *new; + + if (!orig) + return NULL; + + new = kstrdup_and_replace(orig, '/', ':', GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + return new; +} + +static inline void free_irq_label(const char *label) +{ + kfree(label); +} + #ifdef CONFIG_HTE static enum hte_return process_hw_ts_thread(void *p) @@ -802,7 +701,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) line->total_discard_seq++; line->last_seqno = ts->seq; mod_delayed_work(system_wq, &line->work, - usecs_to_jiffies(READ_ONCE(line->debounce_period_us))); + usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); } else { if (unlikely(ts->seq < line->line_seqno)) return HTE_CB_HANDLED; @@ -943,7 +842,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *p) struct line *line = p; mod_delayed_work(system_wq, &line->work, - usecs_to_jiffies(READ_ONCE(line->debounce_period_us))); + usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); return IRQ_HANDLED; } @@ -1021,13 +920,15 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us) { unsigned long irqflags; int ret, level, irq; + char *label; - /* try hardware */ - ret = gpiod_set_debounce(line->desc, debounce_period_us); - if (!ret) { - line_set_debounce_period(line, debounce_period_us); - return ret; - } + /* + * Try hardware. Skip gpiod_set_config() to avoid emitting two + * CHANGED_CONFIG line state events. + */ + ret = gpio_do_set_config(line->desc, + pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, + debounce_period_us)); if (ret != -ENOTSUPP) return ret; @@ -1043,11 +944,17 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us) if (irq < 0) return -ENXIO; + label = make_irq_label(line->req->label); + if (IS_ERR(label)) + return -ENOMEM; + irqflags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING; ret = request_irq(irq, debounce_irq_handler, irqflags, - line->req->label, line); - if (ret) + label, line); + if (ret) { + free_irq_label(label); return ret; + } line->irq = irq; } else { ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH); @@ -1092,7 +999,7 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc, static void edge_detector_stop(struct line *line) { if (line->irq) { - free_irq(line->irq, line); + free_irq_label(free_irq(line->irq, line)); line->irq = 0; } @@ -1104,10 +1011,19 @@ static void edge_detector_stop(struct line *line) cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); WRITE_ONCE(line->edflags, 0); - line_set_debounce_period(line, 0); + if (line->desc) + WRITE_ONCE(line->desc->debounce_period_us, 0); /* 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) @@ -1116,11 +1032,11 @@ static int edge_detector_setup(struct line *line, unsigned long irqflags = 0; u64 eflags; int irq, ret; + char *label; eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; - if (eflags && !kfifo_initialized(&line->req->events)) { - 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; } @@ -1129,7 +1045,7 @@ static int edge_detector_setup(struct line *line, ret = debounce_setup(line, debounce_period_us); if (ret) return ret; - line_set_debounce_period(line, debounce_period_us); + WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); } /* detection disabled or sw debouncer will provide edge detection */ @@ -1152,11 +1068,17 @@ static int edge_detector_setup(struct line *line, IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; irqflags |= IRQF_ONESHOT; + label = make_irq_label(line->req->label); + if (IS_ERR(label)) + return PTR_ERR(label); + /* Request a thread to read the events */ ret = request_threaded_irq(irq, edge_irq_handler, edge_irq_thread, - irqflags, line->req->label, line); - if (ret) + irqflags, label, line); + if (ret) { + free_irq_label(label); return ret; + } line->irq = irq; return 0; @@ -1171,12 +1093,19 @@ static int edge_detector_update(struct line *line, gpio_v2_line_config_debounce_period(lc, line_idx); if ((active_edflags == edflags) && - (READ_ONCE(line->debounce_period_us) == debounce_period_us)) + (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) return 0; /* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) { - line_set_debounce_period(line, debounce_period_us); + WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); + /* + * ensure event fifo is initialised if edge detection + * is now enabled. + */ + if (edflags & GPIO_V2_LINE_EDGE_FLAGS) + return edge_detector_fifo_init(line->req); + return 0; } @@ -1286,7 +1215,7 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) return -EINVAL; - if (memchr_inv(lc->padding, 0, sizeof(lc->padding))) + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) return -EINVAL; for (i = 0; i < num_lines; i++) { @@ -1303,38 +1232,42 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, return 0; } -static void gpio_v2_line_config_flags_to_desc_flags(u64 flags, +static void gpio_v2_line_config_flags_to_desc_flags(u64 lflags, unsigned long *flagsp) { - assign_bit(FLAG_ACTIVE_LOW, flagsp, - flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW); + unsigned long flags = READ_ONCE(*flagsp); + + assign_bit(FLAG_ACTIVE_LOW, &flags, + lflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW); - if (flags & GPIO_V2_LINE_FLAG_OUTPUT) - set_bit(FLAG_IS_OUT, flagsp); - else if (flags & GPIO_V2_LINE_FLAG_INPUT) - clear_bit(FLAG_IS_OUT, flagsp); + if (lflags & GPIO_V2_LINE_FLAG_OUTPUT) + set_bit(FLAG_IS_OUT, &flags); + else if (lflags & GPIO_V2_LINE_FLAG_INPUT) + clear_bit(FLAG_IS_OUT, &flags); - assign_bit(FLAG_EDGE_RISING, flagsp, - flags & GPIO_V2_LINE_FLAG_EDGE_RISING); - assign_bit(FLAG_EDGE_FALLING, flagsp, - flags & GPIO_V2_LINE_FLAG_EDGE_FALLING); + assign_bit(FLAG_EDGE_RISING, &flags, + lflags & GPIO_V2_LINE_FLAG_EDGE_RISING); + assign_bit(FLAG_EDGE_FALLING, &flags, + lflags & GPIO_V2_LINE_FLAG_EDGE_FALLING); - assign_bit(FLAG_OPEN_DRAIN, flagsp, - flags & GPIO_V2_LINE_FLAG_OPEN_DRAIN); - assign_bit(FLAG_OPEN_SOURCE, flagsp, - flags & GPIO_V2_LINE_FLAG_OPEN_SOURCE); + assign_bit(FLAG_OPEN_DRAIN, &flags, + lflags & GPIO_V2_LINE_FLAG_OPEN_DRAIN); + assign_bit(FLAG_OPEN_SOURCE, &flags, + lflags & GPIO_V2_LINE_FLAG_OPEN_SOURCE); - assign_bit(FLAG_PULL_UP, flagsp, - flags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP); - assign_bit(FLAG_PULL_DOWN, flagsp, - flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN); - assign_bit(FLAG_BIAS_DISABLE, flagsp, - flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED); + assign_bit(FLAG_PULL_UP, &flags, + lflags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP); + assign_bit(FLAG_PULL_DOWN, &flags, + lflags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN); + assign_bit(FLAG_BIAS_DISABLE, &flags, + lflags & GPIO_V2_LINE_FLAG_BIAS_DISABLED); - assign_bit(FLAG_EVENT_CLOCK_REALTIME, flagsp, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME); - assign_bit(FLAG_EVENT_CLOCK_HTE, flagsp, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + assign_bit(FLAG_EVENT_CLOCK_REALTIME, &flags, + lflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME); + assign_bit(FLAG_EVENT_CLOCK_HTE, &flags, + lflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + + WRITE_ONCE(*flagsp, flags); } static long linereq_get_values(struct linereq *lr, void __user *ip) @@ -1489,21 +1422,23 @@ 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); edge_detector_stop(line); - ret = gpiod_direction_output(desc, val); + ret = gpiod_direction_output_nonotify(desc, val); if (ret) return ret; - } else if (flags & GPIO_V2_LINE_FLAG_INPUT) { - ret = gpiod_direction_input(desc); + } else { + ret = gpiod_direction_input_nonotify(desc); if (ret) return ret; @@ -1525,9 +1460,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, struct linereq *lr = file->private_data; void __user *ip = (void __user *)arg; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_access_pointer(lr->gdev->chip)) return -ENODEV; switch (cmd) { @@ -1556,9 +1491,9 @@ static __poll_t linereq_poll(struct file *file, struct linereq *lr = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_access_pointer(lr->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &lr->wait, wait); @@ -1578,9 +1513,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf, ssize_t bytes_read = 0; int ret; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_access_pointer(lr->gdev->chip)) return -ENODEV; if (count < sizeof(le)) @@ -1601,16 +1536,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))) @@ -1623,7 +1557,6 @@ static ssize_t linereq_read(struct file *file, char __user *buf, static void linereq_free(struct linereq *lr) { - struct line *line; unsigned int i; if (lr->device_unregistered_nb.notifier_call) @@ -1631,14 +1564,10 @@ static void linereq_free(struct linereq *lr) &lr->device_unregistered_nb); for (i = 0; i < lr->num_lines; i++) { - line = &lr->lines[i]; - if (!line->desc) - continue; - - edge_detector_stop(line); - if (line_has_supinfo(line)) - supinfo_erase(line); - gpiod_free(line->desc); + if (lr->lines[i].desc) { + edge_detector_stop(&lr->lines[i]); + gpiod_free(lr->lines[i].desc); + } } kfifo_free(&lr->events); kfree(lr->label); @@ -1700,7 +1629,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX)) return -EINVAL; - if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding))) + if (!mem_is_zero(ulr.padding, sizeof(ulr.padding))) return -EINVAL; lc = &ulr.config; @@ -1733,6 +1662,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; @@ -1744,7 +1674,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < ulr.num_lines; i++) { u32 offset = ulr.offsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) { ret = PTR_ERR(desc); @@ -1771,11 +1701,11 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { int val = gpio_v2_line_config_output_value(lc, i); - ret = gpiod_direction_output(desc, val); + ret = gpiod_direction_output_nonotify(desc, val); if (ret) goto out_free_linereq; } else if (flags & GPIO_V2_LINE_FLAG_INPUT) { - ret = gpiod_direction_input(desc); + ret = gpiod_direction_input_nonotify(desc); if (ret) goto out_free_linereq; @@ -1879,9 +1809,9 @@ static __poll_t lineevent_poll(struct file *file, struct lineevent_state *le = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_access_pointer(le->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &le->wait, wait); @@ -1917,9 +1847,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf, ssize_t ge_size; int ret; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV; /* @@ -1953,16 +1883,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)) @@ -1979,7 +1908,7 @@ static void lineevent_free(struct lineevent_state *le) blocking_notifier_chain_unregister(&le->gdev->device_notifier, &le->device_unregistered_nb); if (le->irq) - free_irq(le->irq, le); + free_irq_label(free_irq(le->irq, le)); if (le->desc) gpiod_free(le->desc); kfree(le->label); @@ -2000,9 +1929,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd, void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV; /* @@ -2120,6 +2049,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) int fd; int ret; int irq, irqflags = 0; + char *label; if (copy_from_user(&eventreq, ip, sizeof(eventreq))) return -EFAULT; @@ -2128,7 +2058,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) lflags = eventreq.handleflags; eflags = eventreq.eventflags; - desc = gpiochip_get_desc(gdev->chip, offset); + desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2204,15 +2134,23 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (ret) goto out_free_le; + label = make_irq_label(le->label); + if (IS_ERR(label)) { + ret = PTR_ERR(label); + goto out_free_le; + } + /* Request a thread to read the events */ ret = request_threaded_irq(irq, lineevent_irq_handler, lineevent_irq_thread, irqflags, - le->label, + label, le); - if (ret) + if (ret) { + free_irq_label(label); goto out_free_le; + } le->irq = irq; @@ -2298,23 +2236,29 @@ static void gpio_v2_line_info_changed_to_v1( #endif /* CONFIG_GPIO_CDEV_V1 */ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, - struct gpio_v2_line_info *info) + struct gpio_v2_line_info *info, bool atomic) { - struct gpio_chip *gc = desc->gdev->chip; + u32 debounce_period_us; unsigned long dflags; + const char *label; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; memset(info, 0, sizeof(*info)); info->offset = gpio_chip_hwgpio(desc); - scoped_guard(spinlock_irqsave, &gpio_lock) { - if (desc->name) - strscpy(info->name, desc->name, sizeof(info->name)); + if (desc->name) + strscpy(info->name, desc->name, sizeof(info->name)); - if (desc->label) - strscpy(info->consumer, desc->label, - sizeof(info->consumer)); + dflags = READ_ONCE(desc->flags); - dflags = READ_ONCE(desc->flags); + scoped_guard(srcu, &desc->gdev->desc_srcu) { + label = gpiod_get_label(desc); + if (label && test_bit(FLAG_REQUESTED, &dflags)) + strscpy(info->consumer, label, + sizeof(info->consumer)); } /* @@ -2331,12 +2275,14 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, */ if (test_bit(FLAG_REQUESTED, &dflags) || test_bit(FLAG_IS_HOGGED, &dflags) || - test_bit(FLAG_USED_AS_IRQ, &dflags) || test_bit(FLAG_EXPORT, &dflags) || test_bit(FLAG_SYSFS, &dflags) || - !gpiochip_line_is_valid(gc, info->offset) || - !pinctrl_gpio_can_use_line(gc, info->offset)) + !gpiochip_line_is_valid(guard.gc, info->offset)) { info->flags |= GPIO_V2_LINE_FLAG_USED; + } else if (!atomic) { + if (!pinctrl_gpio_can_use_line(guard.gc, info->offset)) + info->flags |= GPIO_V2_LINE_FLAG_USED; + } if (test_bit(FLAG_IS_OUT, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_OUTPUT; @@ -2367,6 +2313,14 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME; else if (test_bit(FLAG_EVENT_CLOCK_HTE, &dflags)) info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE; + + debounce_period_us = READ_ONCE(desc->debounce_period_us); + if (debounce_period_us) { + info->attrs[info->num_attrs].id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; + info->attrs[info->num_attrs].debounce_period_us = + debounce_period_us; + info->num_attrs++; + } } struct gpio_chardev_data { @@ -2379,6 +2333,7 @@ struct gpio_chardev_data { #ifdef CONFIG_GPIO_CDEV_V1 atomic_t watch_abi_version; #endif + struct file *fp; }; static int chipinfo_get(struct gpio_chardev_data *cdev, void __user *ip) @@ -2422,7 +2377,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip, return -EFAULT; /* this doubles as a range check on line_offset */ - desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset); + desc = gpio_device_get_desc(cdev->gdev, lineinfo.line_offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2434,7 +2389,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip, return -EBUSY; } - gpio_desc_to_lineinfo(desc, &lineinfo_v2); + gpio_desc_to_lineinfo(desc, &lineinfo_v2, false); gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo); if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) { @@ -2456,10 +2411,10 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; - if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) + if (!mem_is_zero(lineinfo.padding, sizeof(lineinfo.padding))) return -EINVAL; - desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); + desc = gpio_device_get_desc(cdev->gdev, lineinfo.offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2471,8 +2426,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, if (test_and_set_bit(lineinfo.offset, cdev->watched_lines)) return -EBUSY; } - gpio_desc_to_lineinfo(desc, &lineinfo); - supinfo_to_lineinfo(desc, &lineinfo); + gpio_desc_to_lineinfo(desc, &lineinfo, false); if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) { if (watch) @@ -2508,10 +2462,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct gpio_device *gdev = cdev->gdev; void __user *ip = (void __user *)arg; - guard(rwsem_read)(&gdev->sem); + guard(srcu)(&gdev->srcu); /* We fail any subsequent ioctl():s when the chip is gone */ - if (!gdev->chip) + if (!rcu_access_pointer(gdev->chip)) return -ENODEV; /* Fill in the struct and pass to userspace */ @@ -2549,29 +2503,86 @@ static long gpio_ioctl_compat(struct file *file, unsigned int cmd, } #endif +struct lineinfo_changed_ctx { + struct work_struct work; + struct gpio_v2_line_info_changed chg; + struct gpio_device *gdev; + struct gpio_chardev_data *cdev; +}; + +static void lineinfo_changed_func(struct work_struct *work) +{ + struct lineinfo_changed_ctx *ctx = + container_of(work, struct lineinfo_changed_ctx, work); + struct gpio_chip *gc; + int ret; + + if (!(ctx->chg.info.flags & GPIO_V2_LINE_FLAG_USED)) { + /* + * If nobody set the USED flag earlier, let's see with pinctrl + * now. We're doing this late because it's a sleeping function. + * Pin functions are in general much more static and while it's + * not 100% bullet-proof, it's good enough for most cases. + */ + scoped_guard(srcu, &ctx->gdev->srcu) { + gc = srcu_dereference(ctx->gdev->chip, &ctx->gdev->srcu); + if (gc && + !pinctrl_gpio_can_use_line(gc, ctx->chg.info.offset)) + ctx->chg.info.flags |= GPIO_V2_LINE_FLAG_USED; + } + } + + ret = kfifo_in_spinlocked(&ctx->cdev->events, &ctx->chg, 1, + &ctx->cdev->wait.lock); + if (ret) + wake_up_poll(&ctx->cdev->wait, EPOLLIN); + else + pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); + + gpio_device_put(ctx->gdev); + fput(ctx->cdev->fp); + kfree(ctx); +} + static int lineinfo_changed_notify(struct notifier_block *nb, unsigned long action, void *data) { struct gpio_chardev_data *cdev = container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); - struct gpio_v2_line_info_changed chg; + struct lineinfo_changed_ctx *ctx; struct gpio_desc *desc = data; - int ret; if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines)) return NOTIFY_DONE; - memset(&chg, 0, sizeof(chg)); - chg.event_type = action; - chg.timestamp_ns = ktime_get_ns(); - gpio_desc_to_lineinfo(desc, &chg.info); - supinfo_to_lineinfo(desc, &chg.info); + /* + * If this is called from atomic context (for instance: with a spinlock + * taken by the atomic notifier chain), any sleeping calls must be done + * outside of this function in process context of the dedicated + * workqueue. + * + * Let's gather as much info as possible from the descriptor and + * postpone just the call to pinctrl_gpio_can_use_line() until the work + * is executed. + */ - ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock); - if (ret) - wake_up_poll(&cdev->wait, EPOLLIN); - else - pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); + ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); + if (!ctx) { + pr_err("Failed to allocate memory for line info notification\n"); + return NOTIFY_DONE; + } + + ctx->chg.event_type = action; + ctx->chg.timestamp_ns = ktime_get_ns(); + gpio_desc_to_lineinfo(desc, &ctx->chg.info, true); + /* Keep the GPIO device alive until we emit the event. */ + ctx->gdev = gpio_device_get(desc->gdev); + ctx->cdev = cdev; + /* Keep the file descriptor alive too. */ + get_file(ctx->cdev->fp); + + INIT_WORK(&ctx->work, lineinfo_changed_func); + queue_work(ctx->gdev->line_state_wq, &ctx->work); return NOTIFY_OK; } @@ -2594,9 +2605,9 @@ static __poll_t lineinfo_watch_poll(struct file *file, struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&cdev->gdev->sem); + guard(srcu)(&cdev->gdev->srcu); - if (!cdev->gdev->chip) + if (!rcu_access_pointer(cdev->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &cdev->wait, pollt); @@ -2617,9 +2628,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, int ret; size_t event_size; - guard(rwsem_read)(&cdev->gdev->sem); + guard(srcu)(&cdev->gdev->srcu); - if (!cdev->gdev->chip) + if (!rcu_access_pointer(cdev->gdev->chip)) return -ENODEV; #ifndef CONFIG_GPIO_CDEV_V1 @@ -2651,12 +2662,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 @@ -2685,7 +2699,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) { @@ -2694,17 +2710,17 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) struct gpio_chardev_data *cdev; int ret = -ENOMEM; - guard(rwsem_read)(&gdev->sem); + guard(srcu)(&gdev->srcu); /* Fail on open if the backing gpiochip is gone */ - if (!gdev->chip) + if (!rcu_access_pointer(gdev->chip)) return -ENODEV; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return -ENODEV; - cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + cdev->watched_lines = bitmap_zalloc(gdev->ngpio, GFP_KERNEL); if (!cdev->watched_lines) goto out_free_cdev; @@ -2713,8 +2729,9 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) cdev->gdev = gpio_device_get(gdev); cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = blocking_notifier_chain_register(&gdev->line_state_notifier, - &cdev->lineinfo_changed_nb); + scoped_guard(write_lock_irqsave, &gdev->line_state_lock) + ret = raw_notifier_chain_register(&gdev->line_state_notifier, + &cdev->lineinfo_changed_nb); if (ret) goto out_free_bitmap; @@ -2726,6 +2743,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) goto out_unregister_line_notifier; file->private_data = cdev; + cdev->fp = file; ret = nonseekable_open(inode, file); if (ret) @@ -2737,8 +2755,9 @@ out_unregister_device_notifier: blocking_notifier_chain_unregister(&gdev->device_notifier, &cdev->device_unregistered_nb); out_unregister_line_notifier: - blocking_notifier_chain_unregister(&gdev->line_state_notifier, - &cdev->lineinfo_changed_nb); + scoped_guard(write_lock_irqsave, &gdev->line_state_lock) + raw_notifier_chain_unregister(&gdev->line_state_notifier, + &cdev->lineinfo_changed_nb); out_free_bitmap: gpio_device_put(gdev); bitmap_free(cdev->watched_lines); @@ -2751,18 +2770,21 @@ 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) { struct gpio_chardev_data *cdev = file->private_data; struct gpio_device *gdev = cdev->gdev; - bitmap_free(cdev->watched_lines); blocking_notifier_chain_unregister(&gdev->device_notifier, &cdev->device_unregistered_nb); - blocking_notifier_chain_unregister(&gdev->line_state_notifier, - &cdev->lineinfo_changed_nb); + scoped_guard(write_lock_irqsave, &gdev->line_state_lock) + raw_notifier_chain_unregister(&gdev->line_state_notifier, + &cdev->lineinfo_changed_nb); + bitmap_free(cdev->watched_lines); gpio_device_put(gdev); kfree(cdev); @@ -2775,7 +2797,6 @@ static const struct file_operations gpio_fileops = { .poll = lineinfo_watch_poll, .read = lineinfo_watch_read, .owner = THIS_MODULE, - .llseek = no_llseek, .unlocked_ioctl = gpio_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = gpio_ioctl_compat, @@ -2784,24 +2805,35 @@ static const struct file_operations gpio_fileops = { int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) { + struct gpio_chip *gc; int ret; cdev_init(&gdev->chrdev, &gpio_fileops); gdev->chrdev.owner = THIS_MODULE; gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id); + gdev->line_state_wq = alloc_ordered_workqueue("%s", WQ_HIGHPRI, + dev_name(&gdev->dev)); + if (!gdev->line_state_wq) + return -ENOMEM; + ret = cdev_device_add(&gdev->chrdev, &gdev->dev); if (ret) return ret; - chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", - MAJOR(devt), gdev->id); + guard(srcu)(&gdev->srcu); + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) + return -ENODEV; + + chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); return 0; } void gpiolib_cdev_unregister(struct gpio_device *gdev) { + destroy_workqueue(gdev->line_state_wq); cdev_device_del(&gdev->chrdev, &gdev->dev); blocking_notifier_call_chain(&gdev->device_notifier, 0, NULL); } diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index fe9ce6b19f15..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, @@ -158,7 +179,7 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, if (!dr) return ERR_PTR(-ENOMEM); - desc = fwnode_gpiod_get_index(fwnode, con_id, index, flags, label); + desc = gpiod_find_and_request(dev, fwnode, con_id, index, flags, label, false); if (IS_ERR(desc)) { devres_free(dr); return desc; @@ -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 97f4b498e343..aeae6df8bec9 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> @@ -6,6 +12,9 @@ #include "gpiolib.h" +/* + * **DEPRECATED** This function is deprecated and must not be used in new code. + */ void gpio_free(unsigned gpio) { gpiod_free(gpio_to_desc(gpio)); @@ -17,30 +26,30 @@ EXPORT_SYMBOL_GPL(gpio_free); * @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 gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { struct gpio_desc *desc; int err; - desc = gpio_to_desc(gpio); - /* Compatibility: assume unavailable "valid" GPIOs will appear later */ - if (!desc && gpio_is_valid(gpio)) + desc = gpio_to_desc(gpio); + if (!desc) return -EPROBE_DEFER; err = gpiod_request(desc, label); if (err) return err; - 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; @@ -53,49 +62,98 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) } EXPORT_SYMBOL_GPL(gpio_request_one); +/* + * **DEPRECATED** This function is deprecated and must not be used in new code. + */ int gpio_request(unsigned gpio, const char *label) { - struct gpio_desc *desc = gpio_to_desc(gpio); + struct gpio_desc *desc; /* Compatibility: assume unavailable "valid" GPIOs will appear later */ - if (!desc && gpio_is_valid(gpio)) + desc = gpio_to_desc(gpio); + if (!desc) return -EPROBE_DEFER; return gpiod_request(desc, label); } EXPORT_SYMBOL_GPL(gpio_request); +static void devm_gpio_release(struct device *dev, void *res) +{ + unsigned *gpio = res; + + gpio_free(*gpio); +} + /** - * gpio_request_array - request multiple GPIOs in a single call - * @array: array of the 'struct gpio' - * @num: how many GPIOs in the array + * 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 gpio_request_array(const struct gpio *array, size_t num) +int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) { - int i, err; + unsigned *dr; + int rc; + + dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); + if (!dr) + return -ENOMEM; - for (i = 0; i < num; i++, array++) { - err = gpio_request_one(array->gpio, array->flags, array->label); - if (err) - goto err_free; + rc = gpio_request(gpio, label); + if (rc) { + devres_free(dr); + return rc; } - return 0; -err_free: - while (i--) - gpio_free((--array)->gpio); - return err; + *dr = gpio; + devres_add(dev, dr); + + return 0; } -EXPORT_SYMBOL_GPL(gpio_request_array); +EXPORT_SYMBOL_GPL(devm_gpio_request); /** - * gpio_free_array - release multiple GPIOs in a single call - * @array: array of the 'struct gpio' - * @num: how many GPIOs in the array + * 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. */ -void gpio_free_array(const struct gpio *array, size_t num) +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label) { - while (num--) - gpio_free((array++)->gpio); + 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(gpio_free_array); +EXPORT_SYMBOL_GPL(devm_gpio_request_one); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index e7770eedd146..2e537ee979f3 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' @@ -68,7 +71,7 @@ static int of_gpio_named_count(const struct device_node *np, /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI - * @dev: Consuming device + * @np: Consuming device node * @con_id: Function within the GPIO consumer * * Some elder GPIO controllers need special quirks. Currently we handle @@ -77,11 +80,15 @@ 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(struct device *dev, const char *con_id) +static int of_gpio_spi_cs_get_count(const struct device_node *np, + const char *con_id) { - struct device_node *np = dev->of_node; - if (!IS_ENABLED(CONFIG_SPI_MASTER)) return 0; if (!con_id || strcmp(con_id, "cs")) @@ -93,34 +100,28 @@ static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id) return of_gpio_named_count(np, "gpios"); } -int of_gpio_get_count(struct device *dev, const char *con_id) +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(dev, con_id); + 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]); - - ret = of_gpio_named_count(dev->of_node, propname); + for_each_gpio_property_name(propname, con_id) { + ret = of_gpio_named_count(np, propname); if (ret > 0) break; } return ret ? ret : -ENOENT; } -static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data) +static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, + const void *data) { - struct of_phandle_args *gpiospec = data; + const struct of_phandle_args *gpiospec = data; return device_match_of_node(&chip->gpiodev->dev, gpiospec->np) && chip->of_xlate && @@ -128,7 +129,7 @@ static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data) } static struct gpio_device * -of_find_gpio_device_by_xlate(struct of_phandle_args *gpiospec) +of_find_gpio_device_by_xlate(const struct of_phandle_args *gpiospec) { return gpio_device_find(gpiospec, of_gpiochip_match_node_and_xlate); } @@ -202,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; @@ -318,12 +337,11 @@ static void of_gpio_flags_quirks(const struct device_node *np, * to determine if the flags should have inverted semantics. */ if (IS_ENABLED(CONFIG_SPI_MASTER) && !strcmp(propname, "cs-gpios") && - of_property_read_bool(np, "cs-gpios")) { - struct device_node *child; + of_property_present(np, "cs-gpios")) { 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; @@ -344,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; } } @@ -364,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. */ @@ -414,7 +432,10 @@ out: * @propname: Name of property containing gpio specifier(s) * @index: index of the GPIO * - * Returns GPIO number to use with Linux generic GPIO API, or one of the errno + * **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 * value on the error condition. */ int of_get_named_gpio(const struct device_node *np, const char *propname, @@ -501,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 /* @@ -666,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; } @@ -709,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, @@ -777,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) { @@ -798,7 +812,7 @@ static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog) return ret; #ifdef CONFIG_OF_DYNAMIC - desc->hog = hog; + WRITE_ONCE(desc->hog, hog); #endif } @@ -811,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); } @@ -846,11 +859,11 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip, struct gpio_desc *desc; for_each_gpio_desc_with_flag(chip, desc, FLAG_IS_HOGGED) - if (desc->hog == hog) + if (READ_ONCE(desc->hog) == hog) gpiochip_free_own_desc(desc); } -static int of_gpiochip_match_node(struct gpio_chip *chip, void *data) +static int of_gpiochip_match_node(struct gpio_chip *chip, const void *data) { return device_match_of_node(&chip->gpiodev->dev, data); } @@ -924,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, @@ -973,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, @@ -1034,16 +1053,16 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) struct of_phandle_args pinspec; struct pinctrl_dev *pctldev; struct device_node *np; - int index = 0, ret; + int index = 0, ret, trim; const char *name; static const char group_names_propname[] = "gpio-ranges-group-names"; - struct property *group_names; + 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, @@ -1056,8 +1075,15 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) return -EPROBE_DEFER; + /* Ignore ranges outside of this GPIO chip */ + if (pinspec.args[0] >= (chip->offset + chip->ngpio)) + continue; + if (pinspec.args[0] + pinspec.args[2] <= chip->offset) + continue; + if (pinspec.args[2]) { - if (group_names) { + /* npins != 0: linear range */ + if (has_group_names) { of_property_read_string_index(np, group_names_propname, index, &name); @@ -1067,7 +1093,19 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) break; } } - /* npins != 0: linear range */ + + /* Trim the range to fit this GPIO chip */ + if (chip->offset > pinspec.args[0]) { + trim = chip->offset - pinspec.args[0]; + pinspec.args[2] -= trim; + pinspec.args[1] += trim; + pinspec.args[0] = 0; + } else { + pinspec.args[0] -= chip->offset; + } + if ((pinspec.args[0] + pinspec.args[2]) > chip->ngpio) + pinspec.args[2] = chip->ngpio - pinspec.args[0]; + ret = gpiochip_add_pin_range(chip, pinctrl_dev_get_devname(pctldev), pinspec.args[0], @@ -1083,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-of.h b/drivers/gpio/gpiolib-of.h index 6b3a5347c5d9..16d6ac8cb156 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -9,6 +9,7 @@ #include <linux/notifier.h> struct device; +struct fwnode_handle; struct gpio_chip; struct gpio_desc; @@ -21,7 +22,7 @@ struct gpio_desc *of_find_gpio(struct device_node *np, unsigned long *lookupflags); int of_gpiochip_add(struct gpio_chip *gc); void of_gpiochip_remove(struct gpio_chip *gc); -int of_gpio_get_count(struct device *dev, const char *con_id); +int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id); #else static inline struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id, @@ -32,7 +33,8 @@ static inline struct gpio_desc *of_find_gpio(struct device_node *np, } static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } static inline void of_gpiochip_remove(struct gpio_chip *gc) { } -static inline int of_gpio_get_count(struct device *dev, const char *con_id) +static inline int of_gpio_count(const struct fwnode_handle *fwnode, + const char *con_id) { return 0; } diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index fa52bdb1a29a..f21dbc28cf2c 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -4,8 +4,13 @@ * * Copyright 2022 Google LLC */ + +#define pr_fmt(fmt) "gpiolib: swnode: " fmt + #include <linux/err.h> #include <linux/errno.h> +#include <linux/export.h> +#include <linux/init.h> #include <linux/kernel.h> #include <linux/printk.h> #include <linux/property.h> @@ -17,19 +22,7 @@ #include "gpiolib.h" #include "gpiolib-swnode.h" -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); -} +#define GPIOLIB_SWNODE_UNDEFINED_NAME "swnode-gpio-undefined" static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) { @@ -40,10 +33,29 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) if (!gdev_node || !gdev_node->name) return ERR_PTR(-EINVAL); + /* + * Check for a special node that identifies undefined GPIOs, this is + * primarily used as a key for internal chip selects in SPI bindings. + */ + if (IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED) && + !strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME)) + return ERR_PTR(-ENOENT); + gdev = gpio_device_find_by_label(gdev_node->name); return gdev ?: ERR_PTR(-EPROBE_DEFER); } +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) @@ -52,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 = 0; 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) = @@ -96,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. */ @@ -106,18 +116,48 @@ 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) +/* + * A special node that identifies undefined GPIOs, this is primarily used as + * a key for internal chip selects in SPI bindings. + */ +const struct software_node swnode_gpio_undefined = { + .name = GPIOLIB_SWNODE_UNDEFINED_NAME, +}; +EXPORT_SYMBOL_NS_GPL(swnode_gpio_undefined, "GPIO_SWNODE"); + +static int __init swnode_gpio_init(void) +{ + int ret; + + ret = software_node_register(&swnode_gpio_undefined); + if (ret < 0) + pr_err("failed to register swnode: %d\n", ret); + + return ret; +} +subsys_initcall(swnode_gpio_init); + +static void __exit swnode_gpio_cleanup(void) +{ + software_node_unregister(&swnode_gpio_undefined); } +__exitcall(swnode_gpio_cleanup); +#endif diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 6bf5332136e5..1acfa43bf1ab 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/idr.h> #include <linux/init.h> @@ -13,12 +14,15 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string.h> +#include <linux/srcu.h> #include <linux/sysfs.h> #include <linux/types.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> +#include <uapi/linux/gpio.h> + #include "gpiolib.h" #include "gpiolib-sysfs.h" @@ -75,12 +79,10 @@ static ssize_t direction_show(struct device *dev, struct gpio_desc *desc = data->desc; int value; - mutex_lock(&data->mutex); - - gpiod_get_direction(desc); - value = !!test_bit(FLAG_IS_OUT, &desc->flags); - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) { + gpiod_get_direction(desc); + value = !!test_bit(FLAG_IS_OUT, &desc->flags); + } return sysfs_emit(buf, "%s\n", value ? "out" : "in"); } @@ -92,7 +94,7 @@ static ssize_t direction_store(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (sysfs_streq(buf, "high")) status = gpiod_direction_output_raw(desc, 1); @@ -103,8 +105,6 @@ static ssize_t direction_store(struct device *dev, else status = -EINVAL; - mutex_unlock(&data->mutex); - return status ? : size; } static DEVICE_ATTR_RW(direction); @@ -116,11 +116,8 @@ static ssize_t value_show(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&data->mutex); - - status = gpiod_get_value_cansleep(desc); - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) + status = gpiod_get_value_cansleep(desc); if (status < 0) return status; @@ -138,18 +135,17 @@ static ssize_t value_store(struct device *dev, status = kstrtol(buf, 0, &value); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); - if (!test_bit(FLAG_IS_OUT, &desc->flags)) { - status = -EPERM; - } else if (status == 0) { - gpiod_set_value_cansleep(desc, value); - status = size; - } + if (!test_bit(FLAG_IS_OUT, &desc->flags)) + return -EPERM; + + if (status) + return status; - mutex_unlock(&data->mutex); + gpiod_set_value_cansleep(desc, value); - return status; + return size; } static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); @@ -170,6 +166,10 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) unsigned long irq_flags; int ret; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + data->irq = gpiod_to_irq(desc); if (data->irq < 0) return -EIO; @@ -179,12 +179,16 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) return -ENODEV; irq_flags = IRQF_SHARED; - if (flags & GPIO_IRQF_TRIGGER_FALLING) + if (flags & GPIO_IRQF_TRIGGER_FALLING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; - if (flags & GPIO_IRQF_TRIGGER_RISING) + set_bit(FLAG_EDGE_FALLING, &desc->flags); + } + if (flags & GPIO_IRQF_TRIGGER_RISING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + set_bit(FLAG_EDGE_RISING, &desc->flags); + } /* * FIXME: This should be done in the irq_request_resources callback @@ -194,7 +198,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) * Remove this redundant call (along with the corresponding * unlock) when those drivers have been fixed. */ - ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); + ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); if (ret < 0) goto err_put_kn; @@ -208,8 +212,10 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) return 0; err_unlock: - gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); err_put_kn: + clear_bit(FLAG_EDGE_RISING, &desc->flags); + clear_bit(FLAG_EDGE_FALLING, &desc->flags); sysfs_put(data->value_kn); return ret; @@ -224,9 +230,15 @@ static void gpio_sysfs_free_irq(struct device *dev) struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; + data->irq_flags = 0; free_irq(data->irq, data); - gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); + clear_bit(FLAG_EDGE_RISING, &desc->flags); + clear_bit(FLAG_EDGE_FALLING, &desc->flags); sysfs_put(data->value_kn); } @@ -243,11 +255,8 @@ static ssize_t edge_show(struct device *dev, struct gpiod_data *data = dev_get_drvdata(dev); int flags; - mutex_lock(&data->mutex); - - flags = data->irq_flags; - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) + flags = data->irq_flags; if (flags >= ARRAY_SIZE(trigger_names)) return 0; @@ -266,26 +275,24 @@ static ssize_t edge_store(struct device *dev, if (flags < 0) return flags; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); - if (flags == data->irq_flags) { - status = size; - goto out_unlock; - } + if (flags == data->irq_flags) + return size; if (data->irq_flags) gpio_sysfs_free_irq(dev); - if (flags) { - status = gpio_sysfs_request_irq(dev, flags); - if (!status) - status = size; - } + if (!flags) + return size; + + status = gpio_sysfs_request_irq(dev, flags); + if (status) + return status; -out_unlock: - mutex_unlock(&data->mutex); + gpiod_line_state_notify(data->desc, GPIO_V2_LINE_CHANGED_CONFIG); - return status; + return size; } static DEVICE_ATTR_RW(edge); @@ -310,6 +317,8 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) status = gpio_sysfs_request_irq(dev, flags); } + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); + return status; } @@ -320,11 +329,8 @@ static ssize_t active_low_show(struct device *dev, struct gpio_desc *desc = data->desc; int value; - mutex_lock(&data->mutex); - - value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags); - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) + value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags); return sysfs_emit(buf, "%d\n", value); } @@ -340,13 +346,9 @@ static ssize_t active_low_store(struct device *dev, if (status) return status; - mutex_lock(&data->mutex); - - status = gpio_sysfs_set_active_low(dev, value); - - mutex_unlock(&data->mutex); + guard(mutex)(&data->mutex); - return status ? : size; + return gpio_sysfs_set_active_low(dev, value) ?: size; } static DEVICE_ATTR_RW(active_low); @@ -400,27 +402,27 @@ static const struct attribute_group *gpio_groups[] = { static ssize_t base_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%d\n", chip->base); + return sysfs_emit(buf, "%u\n", gdev->base); } static DEVICE_ATTR_RO(base); static ssize_t label_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%s\n", chip->label ?: ""); + return sysfs_emit(buf, "%s\n", gdev->label); } static DEVICE_ATTR_RO(label); static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%u\n", chip->ngpio); + return sysfs_emit(buf, "%u\n", gdev->ngpio); } static DEVICE_ATTR_RO(ngpio); @@ -443,24 +445,27 @@ static ssize_t export_store(const struct class *class, const char *buf, size_t len) { struct gpio_desc *desc; - struct gpio_chip *gc; int status, offset; long gpio; status = kstrtol(buf, 0, &gpio); - if (status < 0) - goto done; + if (status) + return status; desc = gpio_to_desc(gpio); /* reject invalid GPIOs */ if (!desc) { - pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); + pr_debug_ratelimited("%s: invalid GPIO %ld\n", __func__, gpio); return -EINVAL; } - gc = desc->gdev->chip; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + offset = gpio_chip_hwgpio(desc); - if (!gpiochip_line_is_valid(gc, offset)) { - pr_warn("%s: GPIO %ld masked\n", __func__, gpio); + if (!gpiochip_line_is_valid(guard.gc, offset)) { + pr_debug_ratelimited("%s: GPIO %ld masked\n", __func__, gpio); return -EINVAL; } @@ -480,10 +485,12 @@ static ssize_t export_store(const struct class *class, } status = gpiod_export(desc, true); - if (status < 0) + if (status < 0) { gpiod_free(desc); - else + } else { set_bit(FLAG_SYSFS, &desc->flags); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); + } done: if (status) @@ -507,7 +514,7 @@ static ssize_t unexport_store(const struct class *class, desc = gpio_to_desc(gpio); /* reject bogus commands (gpiod_unexport() ignores them) */ if (!desc) { - pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); + pr_debug_ratelimited("%s: invalid GPIO %ld\n", __func__, gpio); return -EINVAL; } @@ -536,12 +543,11 @@ static struct attribute *gpio_class_attrs[] = { }; ATTRIBUTE_GROUPS(gpio_class); -static struct class gpio_class = { +static const struct class gpio_class = { .name = "gpio", - .class_groups = gpio_class_groups, + .class_groups = gpio_class_groups, }; - /** * gpiod_export - export a GPIO through sysfs * @desc: GPIO to make available, already requested @@ -555,17 +561,15 @@ 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) { - const char *ioname = NULL; struct gpio_device *gdev; struct gpiod_data *data; - struct gpio_chip *chip; - unsigned long flags; struct device *dev; - int status, offset; + int status; /* can't export until sysfs is available ... */ if (!class_is_registered(&gpio_class)) { @@ -578,64 +582,56 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) return -EINVAL; } + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + if (test_and_set_bit(FLAG_EXPORT, &desc->flags)) + return -EPERM; + gdev = desc->gdev; - chip = gdev->chip; - mutex_lock(&sysfs_lock); + guard(mutex)(&sysfs_lock); /* check if chip is being removed */ - if (!chip || !gdev->mockdev) { + if (!gdev->mockdev) { status = -ENODEV; - goto err_unlock; + goto err_clear_bit; } - spin_lock_irqsave(&gpio_lock, flags); - if (!test_bit(FLAG_REQUESTED, &desc->flags) || - test_bit(FLAG_EXPORT, &desc->flags)) { - spin_unlock_irqrestore(&gpio_lock, flags); - gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n", - __func__, - test_bit(FLAG_REQUESTED, &desc->flags), - test_bit(FLAG_EXPORT, &desc->flags)); + if (!test_bit(FLAG_REQUESTED, &desc->flags)) { + gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__); status = -EPERM; - goto err_unlock; + goto err_clear_bit; } - spin_unlock_irqrestore(&gpio_lock, flags); data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { status = -ENOMEM; - goto err_unlock; + goto err_clear_bit; } data->desc = desc; mutex_init(&data->mutex); - if (chip->direction_input && chip->direction_output) + if (guard.gc->direction_input && guard.gc->direction_output) data->direction_can_change = direction_may_change; else data->direction_can_change = false; - offset = gpio_chip_hwgpio(desc); - if (chip->names && chip->names[offset]) - ioname = chip->names[offset]; - dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, - ioname ? ioname : "gpio%u", - desc_to_gpio(desc)); + "gpio%u", desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto err_free_data; } - set_bit(FLAG_EXPORT, &desc->flags); - mutex_unlock(&sysfs_lock); return 0; err_free_data: kfree(data); -err_unlock: - mutex_unlock(&sysfs_lock); +err_clear_bit: + clear_bit(FLAG_EXPORT, &desc->flags); gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } @@ -657,7 +653,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) @@ -697,42 +694,34 @@ void gpiod_unexport(struct gpio_desc *desc) return; } - mutex_lock(&sysfs_lock); - - if (!test_bit(FLAG_EXPORT, &desc->flags)) - goto err_unlock; - - dev = class_find_device(&gpio_class, NULL, desc, match_export); - if (!dev) - goto err_unlock; - - data = dev_get_drvdata(dev); - - clear_bit(FLAG_EXPORT, &desc->flags); + scoped_guard(mutex, &sysfs_lock) { + if (!test_bit(FLAG_EXPORT, &desc->flags)) + return; - device_unregister(dev); + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (!dev) + return; - /* - * Release irq after deregistration to prevent race with edge_store. - */ - if (data->irq_flags) - gpio_sysfs_free_irq(dev); + data = dev_get_drvdata(dev); + clear_bit(FLAG_EXPORT, &desc->flags); + device_unregister(dev); - mutex_unlock(&sysfs_lock); + /* + * Release irq after deregistration to prevent race with + * edge_store. + */ + if (data->irq_flags) + gpio_sysfs_free_irq(dev); + } put_device(dev); kfree(data); - - return; - -err_unlock: - mutex_unlock(&sysfs_lock); } EXPORT_SYMBOL_GPL(gpiod_unexport); int gpiochip_sysfs_register(struct gpio_device *gdev) { - struct gpio_chip *chip = gdev->chip; + struct gpio_chip *chip; struct device *parent; struct device *dev; @@ -745,6 +734,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) if (!class_is_registered(&gpio_class)) return 0; + guard(srcu)(&gdev->srcu); + + chip = srcu_dereference(gdev->chip, &gdev->srcu); + if (!chip) + return -ENODEV; + /* * For sysfs backward compatibility we need to preserve this * preferred parenting to the gpio_chip parent field, if set. @@ -755,15 +750,14 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) parent = &gdev->dev; /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip, + dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), gdev, gpiochip_groups, GPIOCHIP_NAME "%d", chip->base); if (IS_ERR(dev)) return PTR_ERR(dev); - mutex_lock(&sysfs_lock); + guard(mutex)(&sysfs_lock); gdev->mockdev = dev; - mutex_unlock(&sysfs_lock); return 0; } @@ -771,17 +765,23 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) void gpiochip_sysfs_unregister(struct gpio_device *gdev) { struct gpio_desc *desc; - struct gpio_chip *chip = gdev->chip; + struct gpio_chip *chip; - if (!gdev->mockdev) - return; + scoped_guard(mutex, &sysfs_lock) { + if (!gdev->mockdev) + return; - device_unregister(gdev->mockdev); + device_unregister(gdev->mockdev); - /* prevent further gpiod exports */ - mutex_lock(&sysfs_lock); - gdev->mockdev = NULL; - mutex_unlock(&sysfs_lock); + /* prevent further gpiod exports */ + gdev->mockdev = NULL; + } + + guard(srcu)(&gdev->srcu); + + chip = srcu_dereference(gdev->chip, &gdev->srcu); + if (!chip) + return; /* unregister gpiod class devices owned by sysfs */ for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) { @@ -790,11 +790,29 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) } } +/* + * We're not really looking for a device - we just want to iterate over the + * list and call this callback for each GPIO device. This is why this function + * always returns 0. + */ +static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data) +{ + struct gpio_device *gdev = gc->gpiodev; + int ret; + + if (gdev->mockdev) + return 0; + + ret = gpiochip_sysfs_register(gdev); + if (ret) + chip_err(gc, "failed to register the sysfs entry: %d\n", ret); + + return 0; +} + static int __init gpiolib_sysfs_init(void) { - int status; - unsigned long flags; - struct gpio_device *gdev; + int status; status = class_register(&gpio_class); if (status < 0) @@ -806,26 +824,8 @@ static int __init gpiolib_sysfs_init(void) * We run before arch_initcall() so chip->dev nodes can have * registered, and so arch_initcall() can always gpiod_export(). */ - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->mockdev) - continue; + (void)gpio_device_find(NULL, gpiofind_sysfs_register); - /* - * TODO we yield gpio_lock here because - * gpiochip_sysfs_register() acquires a mutex. This is unsafe - * and needs to be fixed. - * - * Also it would be nice to use gpio_device_find() here so we - * can keep gpio_chips local to gpiolib.c, but the yield of - * gpio_lock prevents us from doing this. - */ - spin_unlock_irqrestore(&gpio_lock, flags); - status = gpiochip_sysfs_register(gdev); - spin_lock_irqsave(&gpio_lock, flags); - } - spin_unlock_irqrestore(&gpio_lock, flags); - - return status; + return 0; } postcore_initcall(gpiolib_sysfs_init); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 75be4a3ca7f8..0c00ed2ab431 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,7 +1,9 @@ // 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> #include <linux/debugfs.h> #include <linux/device.h> @@ -12,14 +14,17 @@ #include <linux/idr.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/irqdesc.h> #include <linux/kernel.h> #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> #include <linux/slab.h> -#include <linux/spinlock.h> +#include <linux/srcu.h> #include <linux/string.h> #include <linux/gpio.h> @@ -50,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); @@ -63,7 +68,7 @@ static int gpio_bus_match(struct device *dev, struct device_driver *drv) return 1; } -static struct bus_type gpio_bus_type = { +static const struct bus_type gpio_bus_type = { .name = "gpio", .match = gpio_bus_match, }; @@ -73,19 +78,20 @@ static struct bus_type gpio_bus_type = { */ #define FASTPATH_NGPIO CONFIG_GPIOLIB_FASTPATH_LIMIT -/* gpio_lock prevents conflicts during gpio_desc[] table updates. - * While any GPIO is requested, its gpio_chip is not removable; - * each GPIO's "requested" flag serves as a lock and refcount. - */ -DEFINE_SPINLOCK(gpio_lock); - static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); -LIST_HEAD(gpio_devices); + +static LIST_HEAD(gpio_devices); +/* Protects the GPIO device list against concurrent modifications. */ +static DEFINE_MUTEX(gpio_devices_lock); +/* Ensures coherence during read-only accesses to the list of GPIO devices. */ +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, @@ -97,9 +103,48 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc); static bool gpiolib_initialized; -static inline void desc_set_label(struct gpio_desc *d, const char *label) +const char *gpiod_get_label(struct gpio_desc *desc) { - d->label = label; + struct gpio_desc_label *label; + unsigned long flags; + + flags = READ_ONCE(desc->flags); + + 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 ? label->str : "interrupt"; + + if (!test_bit(FLAG_REQUESTED, &flags)) + return NULL; + + return label ? label->str : NULL; +} + +static void desc_free_label(struct rcu_head *rh) +{ + kfree(container_of(rh, struct gpio_desc_label, rh)); +} + +static int desc_set_label(struct gpio_desc *desc, const char *label) +{ + struct gpio_desc_label *new = NULL, *old; + + if (label) { + new = kzalloc(struct_size(new, str, strlen(label) + 1), + GFP_KERNEL); + if (!new) + return -ENOMEM; + + strcpy(new->str, label); + } + + old = rcu_replace_pointer(desc->label, new, 1); + if (old) + call_srcu(&desc->gdev->desc_srcu, &old->rh, desc_free_label); + + return 0; } /** @@ -113,23 +158,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->base <= gpio && - gdev->base + gdev->ngpio > gpio) { - spin_unlock_irqrestore(&gpio_lock, flags); - return &gdev->descs[gpio - gdev->base]; + scoped_guard(srcu, &gpio_devices_srcu) { + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { + if (gdev->base <= gpio && + gdev->base + gdev->ngpio > gpio) + return &gdev->descs[gpio - gdev->base]; } } - spin_unlock_irqrestore(&gpio_lock, flags); - - if (!gpio_is_valid(gpio)) - pr_warn("invalid GPIO %d\n", gpio); - return NULL; } EXPORT_SYMBOL_GPL(gpio_to_desc); @@ -140,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 @@ -161,20 +198,10 @@ EXPORT_SYMBOL_GPL(gpiochip_get_desc); struct gpio_desc * gpio_device_get_desc(struct gpio_device *gdev, unsigned int hwnum) { - struct gpio_chip *gc; - - /* - * FIXME: This will be locked once we protect gdev->chip everywhere - * with SRCU. - */ - gc = gdev->chip; - if (!gc) - return ERR_PTR(-ENODEV); - 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); @@ -198,12 +225,21 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); /** * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs * @desc: descriptor to return the chip of + * + * *DEPRECATED* + * 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) { - if (!desc || !desc->gdev) + if (!desc) return NULL; - return desc->gdev->chip; + + return gpio_device_get_chip(desc->gdev); } EXPORT_SYMBOL_GPL(gpiod_to_chip); @@ -262,6 +298,7 @@ EXPORT_SYMBOL(gpio_device_get_label); * Returns: * Address of the GPIO chip backing this device. * + * *DEPRECATED* * Until we can get rid of all non-driver users of struct gpio_chip, we must * provide a way of retrieving the pointer to it from struct gpio_device. This * is *NOT* safe as the GPIO API is considered to be hot-unpluggable and the @@ -272,17 +309,18 @@ EXPORT_SYMBOL(gpio_device_get_label); */ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev) { - return gdev->chip; + return rcu_dereference_check(gdev->chip, 1); } EXPORT_SYMBOL_GPL(gpio_device_get_chip); /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ -static int gpiochip_find_base_unlocked(int ngpio) +static int gpiochip_find_base_unlocked(u16 ngpio) { + unsigned int base = GPIO_DYNAMIC_BASE; struct gpio_device *gdev; - int base = GPIO_DYNAMIC_BASE; - list_for_each_entry(gdev, &gpio_devices, list) { + list_for_each_entry_srcu(gdev, &gpio_devices, list, + lockdep_is_held(&gpio_devices_lock)) { /* found a free space? */ if (gdev->base >= base + ngpio) break; @@ -290,9 +328,11 @@ static int gpiochip_find_base_unlocked(int ngpio) base = gdev->base + gdev->ngpio; if (base < GPIO_DYNAMIC_BASE) base = GPIO_DYNAMIC_BASE; + if (base > GPIO_DYNAMIC_MAX - ngpio) + break; } - if (gpio_is_valid(base)) { + if (base <= GPIO_DYNAMIC_MAX - ngpio) { pr_debug("%s: found new base at %d\n", __func__, base); return base; } else { @@ -305,39 +345,55 @@ static int gpiochip_find_base_unlocked(int 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. */ int gpiod_get_direction(struct gpio_desc *desc) { - struct gpio_chip *gc; + unsigned long flags; unsigned int offset; int ret; - gc = gpiod_to_chip(desc); + /* + * We cannot use VALIDATE_DESC() as we must not return 0 for a NULL + * descriptor like we usually do. + */ + if (IS_ERR_OR_NULL(desc)) + return -EINVAL; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + offset = gpio_chip_hwgpio(desc); + flags = READ_ONCE(desc->flags); /* * Open drain emulation using input mode may incorrectly report * input here, fix that up. */ - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) && - test_bit(FLAG_IS_OUT, &desc->flags)) + if (test_bit(FLAG_OPEN_DRAIN, &flags) && + test_bit(FLAG_IS_OUT, &flags)) return 0; - if (!gc->get_direction) + if (!guard.gc->get_direction) return -ENOTSUPP; - ret = gc->get_direction(gc, offset); + ret = guard.gc->get_direction(guard.gc, offset); if (ret < 0) return ret; - /* GPIOF_DIR_IN or other positive, otherwise GPIOF_DIR_OUT */ + /* + * GPIO_LINE_DIRECTION_IN or other positive, + * otherwise GPIO_LINE_DIRECTION_OUT. + */ if (ret > 0) ret = 1; - assign_bit(FLAG_IS_OUT, &desc->flags, !ret); + assign_bit(FLAG_IS_OUT, &flags, !ret); + WRITE_ONCE(desc->flags, flags); return ret; } @@ -347,30 +403,32 @@ 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) { struct gpio_device *prev, *next; + lockdep_assert_held(&gpio_devices_lock); + if (list_empty(&gpio_devices)) { /* initial entry in list */ - list_add_tail(&gdev->list, &gpio_devices); + list_add_tail_rcu(&gdev->list, &gpio_devices); return 0; } next = list_first_entry(&gpio_devices, struct gpio_device, list); if (gdev->base + gdev->ngpio <= next->base) { /* add before first entry */ - list_add(&gdev->list, &gpio_devices); + list_add_rcu(&gdev->list, &gpio_devices); return 0; } prev = list_last_entry(&gpio_devices, struct gpio_device, list); if (prev->base + prev->ngpio <= gdev->base) { /* add behind last entry */ - list_add_tail(&gdev->list, &gpio_devices); + list_add_tail_rcu(&gdev->list, &gpio_devices); return 0; } @@ -382,11 +440,13 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) /* add between prev and next */ if (prev->base + prev->ngpio <= gdev->base && gdev->base + gdev->ngpio <= next->base) { - list_add(&gdev->list, &prev->list); + list_add_rcu(&gdev->list, &prev->list); return 0; } } + synchronize_srcu(&gpio_devices_srcu); + return -EBUSY; } @@ -399,26 +459,28 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) static struct gpio_desc *gpio_name_to_desc(const char * const name) { struct gpio_device *gdev; - unsigned long flags; + struct gpio_desc *desc; + struct gpio_chip *gc; if (!name) return NULL; - spin_lock_irqsave(&gpio_lock, flags); + guard(srcu)(&gpio_devices_srcu); + + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { + guard(srcu)(&gdev->srcu); - list_for_each_entry(gdev, &gpio_devices, list) { - struct gpio_desc *desc; + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) + continue; - for_each_gpio_desc(gdev->chip, desc) { - if (desc->name && !strcmp(desc->name, name)) { - spin_unlock_irqrestore(&gpio_lock, flags); + for_each_gpio_desc(gc, desc) { + if (desc->name && !strcmp(desc->name, name)) return desc; - } } } - spin_unlock_irqrestore(&gpio_lock, flags); - return NULL; } @@ -430,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; @@ -449,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; } /* @@ -653,16 +713,65 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); +static void gpiod_free_irqs(struct gpio_desc *desc) +{ + int irq = gpiod_to_irq(desc); + struct irq_desc *irqd = irq_to_desc(irq); + void *cookie; + + for (;;) { + /* + * Make sure the action doesn't go away while we're + * dereferencing it. Retrieve and store the cookie value. + * If the irq is freed after we release the lock, that's + * alright - the underlying maple tree lookup will return NULL + * and nothing will happen in free_irq(). + */ + scoped_guard(mutex, &irqd->request_mutex) { + if (!irq_desc_has_action(irqd)) + return; + + cookie = irqd->action->dev_id; + } + + free_irq(irq, cookie); + } +} + +/* + * The chip is going away but there may be users who had requested interrupts + * on its GPIO lines who have no idea about its removal and have no way of + * being notified about it. We need to free any interrupts still in use here or + * we'll leak memory and resources (like procfs files). + */ +static void gpiochip_free_remaining_irqs(struct gpio_chip *gc) +{ + struct gpio_desc *desc; + + for_each_gpio_desc_with_flag(gc, desc, FLAG_USED_AS_IRQ) + gpiod_free_irqs(desc); +} + static void gpiodev_release(struct device *dev) { struct gpio_device *gdev = to_gpio_device(dev); + /* Call pending kfree()s for descriptor labels. */ + synchronize_srcu(&gdev->desc_srcu); + cleanup_srcu_struct(&gdev->desc_srcu); + ida_free(&gpio_ida, gdev->id); kfree_const(gdev->label); kfree(gdev->descs); + cleanup_srcu_struct(&gdev->srcu); kfree(gdev); } +static const struct device_type gpio_dev_type = { + .name = "gpio_chip", + .release = gpiodev_release, +}; + #ifdef CONFIG_GPIO_CDEV #define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) @@ -680,6 +789,8 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev); int ret; + device_initialize(&gdev->dev); + /* * If fwnode doesn't belong to another device, it's safe to clear its * initialized flag. @@ -691,15 +802,12 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) if (ret) return ret; - /* From this point, the .release() function cleans up gpio_device */ - gdev->dev.release = gpiodev_release; - ret = gpiochip_sysfs_register(gdev); if (ret) goto err_remove_device; - dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base, - gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic"); + dev_dbg(&gdev->dev, "registered GPIOs %u to %u on %s\n", gdev->base, + gdev->base + gdev->ngpio - 1, gdev->label); return 0; @@ -720,9 +828,6 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) return; } - if (test_bit(FLAG_IS_HOGGED, &desc->flags)) - return; - rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags); if (rv) gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n", @@ -748,7 +853,10 @@ static void gpiochip_setup_devs(void) struct gpio_device *gdev; int ret; - list_for_each_entry(gdev, &gpio_devices, list) { + guard(srcu)(&gpio_devices_srcu); + + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { ret = gpiochip_setup_dev(gdev); if (ret) dev_err(&gdev->dev, @@ -796,13 +904,13 @@ int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev) } if (gc->ngpio == 0) { - chip_err(gc, "tried to insert a GPIO chip with zero lines\n"); + dev_err(dev, "tried to insert a GPIO chip with zero lines\n"); return -EINVAL; } if (gc->ngpio > FASTPATH_NGPIO) - chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n", - gc->ngpio, FASTPATH_NGPIO); + dev_warn(dev, "line cnt %u is greater than fast path cnt %u\n", + gc->ngpio, FASTPATH_NGPIO); return 0; } @@ -813,8 +921,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *request_key) { struct gpio_device *gdev; - unsigned long flags; - unsigned int i; + unsigned int desc_index; int base = 0; int ret = 0; @@ -825,9 +932,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); if (!gdev) return -ENOMEM; + + gdev->dev.type = &gpio_dev_type; gdev->dev.bus = &gpio_bus_type; gdev->dev.parent = gc->parent; - gdev->chip = gc; + rcu_assign_pointer(gdev->chip, gc); gc->gpiodev = gdev; gpiochip_set_data(gc, data); @@ -851,7 +960,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_free_ida; - device_initialize(&gdev->dev); if (gc->parent && gc->parent->driver) gdev->owner = gc->parent->driver->owner; else if (gc->owner) @@ -877,87 +985,97 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } gdev->ngpio = gc->ngpio; + gdev->can_sleep = gc->can_sleep; - spin_lock_irqsave(&gpio_lock, flags); - - /* - * TODO: this allocates a Linux GPIO number base in the global - * GPIO numberspace for this chip. In the long run we want to - * get *rid* of this numberspace and use only descriptors, but - * it may be a pipe dream. It will not happen before we get rid - * of the sysfs interface anyways. - */ - base = gc->base; - if (base < 0) { - base = gpiochip_find_base_unlocked(gc->ngpio); - if (base < 0) { - spin_unlock_irqrestore(&gpio_lock, flags); - ret = base; - base = 0; - goto err_free_label; - } + scoped_guard(mutex, &gpio_devices_lock) { /* - * TODO: it should not be necessary to reflect the assigned - * base outside of the GPIO subsystem. Go over drivers and - * see if anyone makes use of this, else drop this and assign - * a poison instead. + * TODO: this allocates a Linux GPIO number base in the global + * GPIO numberspace for this chip. In the long run we want to + * get *rid* of this numberspace and use only descriptors, but + * it may be a pipe dream. It will not happen before we get rid + * of the sysfs interface anyways. */ - gc->base = base; - } else { - dev_warn(&gdev->dev, - "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); - } - gdev->base = base; + base = gc->base; + if (base < 0) { + base = gpiochip_find_base_unlocked(gc->ngpio); + if (base < 0) { + ret = base; + base = 0; + goto err_free_label; + } - ret = gpiodev_add_to_list_unlocked(gdev); - if (ret) { - spin_unlock_irqrestore(&gpio_lock, flags); - chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); - goto err_free_label; - } + /* + * TODO: it should not be necessary to reflect the + * assigned base outside of the GPIO subsystem. Go over + * drivers and see if anyone makes use of this, else + * drop this and assign a poison instead. + */ + gc->base = base; + } else { + dev_warn(&gdev->dev, + "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); + } - for (i = 0; i < gc->ngpio; i++) - gdev->descs[i].gdev = gdev; + gdev->base = base; - spin_unlock_irqrestore(&gpio_lock, flags); + ret = gpiodev_add_to_list_unlocked(gdev); + if (ret) { + chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); + goto err_free_label; + } + } - BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); + rwlock_init(&gdev->line_state_lock); + RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); - init_rwsem(&gdev->sem); + + ret = init_srcu_struct(&gdev->srcu); + if (ret) + goto err_remove_from_list; + + ret = init_srcu_struct(&gdev->desc_srcu); + if (ret) + goto err_cleanup_gdev_srcu; #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif - if (gc->names) { - ret = gpiochip_set_desc_names(gc); - if (ret) - goto err_remove_from_list; - } + if (gc->names) + gpiochip_set_desc_names(gc); + ret = gpiochip_set_names(gc); if (ret) - goto err_remove_from_list; + goto err_cleanup_desc_srcu; ret = gpiochip_init_valid_mask(gc); if (ret) - goto err_remove_from_list; + goto err_cleanup_desc_srcu; - ret = of_gpiochip_add(gc); - if (ret) - goto err_free_gpiochip_mask; + for (desc_index = 0; desc_index < gc->ngpio; desc_index++) { + struct gpio_desc *desc = &gdev->descs[desc_index]; - for (i = 0; i < gc->ngpio; i++) { - struct gpio_desc *desc = &gdev->descs[i]; + desc->gdev = gdev; - if (gc->get_direction && gpiochip_line_is_valid(gc, i)) { - assign_bit(FLAG_IS_OUT, - &desc->flags, !gc->get_direction(gc, i)); - } else { + /* + * We would typically want to check the return value of + * get_direction() here but we must not check the return value + * and bail-out as pin controllers can have pins configured to + * alternate functions and return -EINVAL. Also: there's no + * need to take the SRCU lock here. + */ + if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) + assign_bit(FLAG_IS_OUT, &desc->flags, + !gc->get_direction(gc, desc_index)); + else assign_bit(FLAG_IS_OUT, &desc->flags, !gc->direction_input); - } } + ret = of_gpiochip_add(gc); + if (ret) + goto err_free_valid_mask; + ret = gpiochip_add_pin_ranges(gc); if (ret) goto err_remove_of_chip; @@ -1003,12 +1121,16 @@ err_free_hogs: gpiochip_remove_pin_ranges(gc); err_remove_of_chip: of_gpiochip_remove(gc); -err_free_gpiochip_mask: +err_free_valid_mask: gpiochip_free_valid_mask(gc); +err_cleanup_desc_srcu: + cleanup_srcu_struct(&gdev->desc_srcu); +err_cleanup_gdev_srcu: + cleanup_srcu_struct(&gdev->srcu); err_remove_from_list: - spin_lock_irqsave(&gpio_lock, flags); - list_del(&gdev->list); - spin_unlock_irqrestore(&gpio_lock, flags); + scoped_guard(mutex, &gpio_devices_lock) + list_del_rcu(&gdev->list); + synchronize_srcu(&gpio_devices_srcu); if (gdev->dev.release) { /* release() has been registered by gpiochip_setup_dev() */ gpio_device_put(gdev); @@ -1044,16 +1166,19 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); void gpiochip_remove(struct gpio_chip *gc) { struct gpio_device *gdev = gc->gpiodev; - unsigned long flags; - unsigned int i; - - down_write(&gdev->sem); /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); + gpiochip_free_remaining_irqs(gc); + + scoped_guard(mutex, &gpio_devices_lock) + list_del_rcu(&gdev->list); + synchronize_srcu(&gpio_devices_srcu); + /* Numb the device, cancelling all outstanding operations */ - gdev->chip = NULL; + rcu_assign_pointer(gdev->chip, NULL); + synchronize_srcu(&gdev->srcu); gpiochip_irqchip_remove(gc); acpi_gpiochip_remove(gc); of_gpiochip_remove(gc); @@ -1065,20 +1190,6 @@ void gpiochip_remove(struct gpio_chip *gc) */ gpiochip_set_data(gc, NULL); - spin_lock_irqsave(&gpio_lock, flags); - for (i = 0; i < gdev->ngpio; i++) { - if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags)) - break; - } - spin_unlock_irqrestore(&gpio_lock, flags); - - if (i != gdev->ngpio) - dev_crit(&gdev->dev, - "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); - - scoped_guard(spinlock_irqsave, &gpio_lock) - list_del(&gdev->list); - /* * The gpiochip side puts its use of the device to rest here: * if there are no userspace clients, the chardev and device will @@ -1086,7 +1197,6 @@ void gpiochip_remove(struct gpio_chip *gc) * gone. */ gcdev_unregister(gdev); - up_write(&gdev->sem); gpio_device_put(gdev); } EXPORT_SYMBOL_GPL(gpiochip_remove); @@ -1112,23 +1222,27 @@ EXPORT_SYMBOL_GPL(gpiochip_remove); * If the function returns non-NULL, the returned reference must be freed by * the caller using gpio_device_put(). */ -struct gpio_device *gpio_device_find(void *data, +struct gpio_device *gpio_device_find(const void *data, int (*match)(struct gpio_chip *gc, - void *data)) + const void *data)) { struct gpio_device *gdev; + struct gpio_chip *gc; - /* - * Not yet but in the future the spinlock below will become a mutex. - * Annotate this function before anyone tries to use it in interrupt - * context like it happened with gpiochip_find(). - */ might_sleep(); - guard(spinlock_irqsave)(&gpio_lock); + guard(srcu)(&gpio_devices_srcu); + + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { + if (!device_is_registered(&gdev->dev)) + continue; + + guard(srcu)(&gdev->srcu); + + gc = srcu_dereference(gdev->chip, &gdev->srcu); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->chip && match(gdev->chip, data)) + if (gc && match(gc, data)) return gpio_device_get(gdev); } @@ -1136,7 +1250,7 @@ struct gpio_device *gpio_device_find(void *data, } EXPORT_SYMBOL_GPL(gpio_device_find); -static int gpio_chip_match_by_label(struct gpio_chip *gc, void *label) +static int gpio_chip_match_by_label(struct gpio_chip *gc, const void *label) { return gc->label && !strcmp(gc->label, label); } @@ -1156,7 +1270,7 @@ struct gpio_device *gpio_device_find_by_label(const char *label) } EXPORT_SYMBOL_GPL(gpio_device_find_by_label); -static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, void *fwnode) +static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, const void *fwnode) { return device_match_fwnode(&gc->gpiodev->dev, fwnode); } @@ -1254,8 +1368,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) gpiochip_free_mask(&gc->irq.valid_mask); } -bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, - unsigned int offset) +static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, + unsigned int offset) { if (!gpiochip_line_is_valid(gc, offset)) return false; @@ -1264,7 +1378,6 @@ bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, return true; return test_bit(offset, gc->irq.valid_mask); } -EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY @@ -1439,6 +1552,46 @@ static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc, return offset; } +/** + * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * @reserve: If set, only reserve an interrupt vector instead of assigning one + * + * 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) +{ + struct gpio_chip *gc = domain->host_data; + unsigned int hwirq = irqd_to_hwirq(data); + + return gpiochip_lock_as_irq(gc, hwirq); +} + +/** + * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * + * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to + * be used as the deactivate function for the &struct irq_domain_ops. The + * host_data for the IRQ domain must be the &struct gpio_chip. + */ +static void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data) +{ + struct gpio_chip *gc = domain->host_data; + unsigned int hwirq = irqd_to_hwirq(data); + + return gpiochip_unlock_as_irq(gc, hwirq); +} + static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops) { ops->activate = gpiochip_irq_domain_activate; @@ -1555,8 +1708,12 @@ 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. */ -int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) +static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct gpio_chip *gc = d->host_data; int ret = 0; @@ -1593,9 +1750,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwi return 0; } -EXPORT_SYMBOL_GPL(gpiochip_irq_map); -void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) +static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) { struct gpio_chip *gc = d->host_data; @@ -1604,7 +1760,6 @@ void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); } -EXPORT_SYMBOL_GPL(gpiochip_irq_unmap); static const struct irq_domain_ops gpiochip_domain_ops = { .map = gpiochip_irq_map, @@ -1626,50 +1781,6 @@ static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc) return domain; } -/* - * TODO: move these activate/deactivate in under the hierarchicial - * irqchip implementation as static once SPMI and SSBI (all external - * users) are phased over. - */ -/** - * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ - * @domain: The IRQ domain used by this IRQ chip - * @data: Outermost irq_data associated with the IRQ - * @reserve: If set, only reserve an interrupt vector instead of assigning one - * - * 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. - */ -int gpiochip_irq_domain_activate(struct irq_domain *domain, - struct irq_data *data, bool reserve) -{ - struct gpio_chip *gc = domain->host_data; - unsigned int hwirq = irqd_to_hwirq(data); - - return gpiochip_lock_as_irq(gc, hwirq); -} -EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate); - -/** - * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ - * @domain: The IRQ domain used by this IRQ chip - * @data: Outermost irq_data associated with the IRQ - * - * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to - * be used as the deactivate function for the &struct irq_domain_ops. The - * host_data for the IRQ domain must be the &struct gpio_chip. - */ -void gpiochip_irq_domain_deactivate(struct irq_domain *domain, - struct irq_data *data) -{ - struct gpio_chip *gc = domain->host_data; - unsigned int hwirq = irqd_to_hwirq(data); - - return gpiochip_unlock_as_irq(gc, hwirq); -} -EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); - static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) { struct irq_domain *domain = gc->irq.domain; @@ -1834,6 +1945,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, @@ -1969,6 +2083,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) @@ -2005,6 +2122,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) { @@ -2038,6 +2158,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) @@ -2064,6 +2187,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, @@ -2115,13 +2241,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, @@ -2189,58 +2315,41 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); */ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) { - struct gpio_chip *gc = desc->gdev->chip; - unsigned long flags; unsigned int offset; int ret; - if (label) { - label = kstrdup_const(label, GFP_KERNEL); - if (!label) - return -ENOMEM; - } + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; - spin_lock_irqsave(&gpio_lock, flags); + if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) + return -EBUSY; /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ - if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { - desc_set_label(desc, label ? : "?"); - } else { - ret = -EBUSY; - goto out_free_unlock; - } - - if (gc->request) { - /* gc->request may sleep */ - spin_unlock_irqrestore(&gpio_lock, flags); + if (guard.gc->request) { offset = gpio_chip_hwgpio(desc); - if (gpiochip_line_is_valid(gc, offset)) - ret = gc->request(gc, offset); + if (gpiochip_line_is_valid(guard.gc, offset)) + ret = guard.gc->request(guard.gc, offset); else ret = -EINVAL; - spin_lock_irqsave(&gpio_lock, flags); - - if (ret) { - desc_set_label(desc, NULL); - clear_bit(FLAG_REQUESTED, &desc->flags); - goto out_free_unlock; - } + if (ret) + goto out_clear_bit; } - if (gc->get_direction) { - /* gc->get_direction may sleep */ - spin_unlock_irqrestore(&gpio_lock, flags); + + if (guard.gc->get_direction) gpiod_get_direction(desc); - spin_lock_irqsave(&gpio_lock, flags); - } - spin_unlock_irqrestore(&gpio_lock, flags); + + ret = desc_set_label(desc, label ? : "?"); + if (ret) + goto out_clear_bit; + return 0; -out_free_unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - kfree_const(label); +out_clear_bit: + clear_bit(FLAG_REQUESTED, &desc->flags); return ret; } @@ -2254,19 +2363,12 @@ static int validate_desc(const struct gpio_desc *desc, const char *func) { if (!desc) return 0; + if (IS_ERR(desc)) { pr_warn("%s: invalid GPIO (errorpointer)\n", func); return PTR_ERR(desc); } - if (!desc->gdev) { - pr_warn("%s: invalid GPIO (no device)\n", func); - return -EINVAL; - } - if (!desc->gdev->chip) { - dev_warn(&desc->gdev->dev, - "%s: backing chip is gone\n", func); - return 0; - } + return 1; } @@ -2302,60 +2404,47 @@ int gpiod_request(struct gpio_desc *desc, const char *label) return ret; } -static bool gpiod_free_commit(struct gpio_desc *desc) +static void gpiod_free_commit(struct gpio_desc *desc) { - struct gpio_chip *gc; unsigned long flags; - bool ret = false; might_sleep(); - spin_lock_irqsave(&gpio_lock, flags); + CLASS(gpio_chip_guard, guard)(desc); - gc = desc->gdev->chip; - if (gc && test_bit(FLAG_REQUESTED, &desc->flags)) { - if (gc->free) { - spin_unlock_irqrestore(&gpio_lock, flags); - might_sleep_if(gc->can_sleep); - gc->free(gc, gpio_chip_hwgpio(desc)); - spin_lock_irqsave(&gpio_lock, flags); - } - kfree_const(desc->label); - desc_set_label(desc, NULL); - clear_bit(FLAG_ACTIVE_LOW, &desc->flags); - clear_bit(FLAG_REQUESTED, &desc->flags); - clear_bit(FLAG_OPEN_DRAIN, &desc->flags); - clear_bit(FLAG_OPEN_SOURCE, &desc->flags); - clear_bit(FLAG_PULL_UP, &desc->flags); - clear_bit(FLAG_PULL_DOWN, &desc->flags); - clear_bit(FLAG_BIAS_DISABLE, &desc->flags); - clear_bit(FLAG_EDGE_RISING, &desc->flags); - clear_bit(FLAG_EDGE_FALLING, &desc->flags); - clear_bit(FLAG_IS_HOGGED, &desc->flags); + flags = READ_ONCE(desc->flags); + + if (guard.gc && test_bit(FLAG_REQUESTED, &flags)) { + if (guard.gc->free) + guard.gc->free(guard.gc, gpio_chip_hwgpio(desc)); + + clear_bit(FLAG_ACTIVE_LOW, &flags); + clear_bit(FLAG_REQUESTED, &flags); + clear_bit(FLAG_OPEN_DRAIN, &flags); + clear_bit(FLAG_OPEN_SOURCE, &flags); + clear_bit(FLAG_PULL_UP, &flags); + clear_bit(FLAG_PULL_DOWN, &flags); + clear_bit(FLAG_BIAS_DISABLE, &flags); + clear_bit(FLAG_EDGE_RISING, &flags); + clear_bit(FLAG_EDGE_FALLING, &flags); + clear_bit(FLAG_IS_HOGGED, &flags); #ifdef CONFIG_OF_DYNAMIC - desc->hog = NULL; + WRITE_ONCE(desc->hog, NULL); #endif - ret = true; + desc_set_label(desc, NULL); + WRITE_ONCE(desc->flags, flags); +#ifdef CONFIG_GPIO_CDEV + WRITE_ONCE(desc->debounce_period_us, 0); +#endif + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_RELEASED); } - - spin_unlock_irqrestore(&gpio_lock, flags); - gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED); - - return ret; } void gpiod_free(struct gpio_desc *desc) { - /* - * We must not use VALIDATE_DESC_VOID() as the underlying gdev->chip - * may already be NULL but we still want to put the references. - */ - if (!desc) - return; - - if (!gpiod_free_commit(desc)) - WARN_ON(1); + VALIDATE_DESC_VOID(desc); + gpiod_free_commit(desc); module_put(desc->gdev->owner); gpio_device_put(desc->gdev); } @@ -2381,20 +2470,12 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) if (IS_ERR(desc)) return NULL; - guard(spinlock_irqsave)(&gpio_lock); - if (!test_bit(FLAG_REQUESTED, &desc->flags)) return NULL; - /* - * FIXME: Once we mark gpiod_direction_input/output() and - * gpiod_get_direction() with might_sleep(), we'll be able to protect - * the GPIO descriptors with mutex (while value setting operations will - * become lockless). - * - * Until this happens, this allocation needs to be atomic. - */ - label = kstrdup(desc->label, GFP_ATOMIC); + guard(srcu)(&desc->gdev->desc_srcu); + + label = kstrdup(gpiod_get_label(desc), GFP_KERNEL); if (!label) return ERR_PTR(-ENOMEM); @@ -2402,6 +2483,11 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_dup_line_label); +static inline const char *function_name_or_default(const char *con_id) +{ + return con_id ?: "(default)"; +} + /** * gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor * @gc: GPIO chip @@ -2430,10 +2516,11 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, enum gpiod_flags dflags) { struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum); + const char *name = function_name_or_default(label); int ret; if (IS_ERR(desc)) { - chip_err(gc, "failed to get GPIO descriptor\n"); + chip_err(gc, "failed to get GPIO %s descriptor\n", name); return desc; } @@ -2443,11 +2530,13 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc, ret = gpiod_configure_flags(desc, label, lflags, dflags); if (ret) { - chip_err(gc, "setup of own GPIO %s failed\n", label); gpiod_free_commit(desc); + chip_err(gc, "setup of own GPIO %s failed\n", name); return ERR_PTR(ret); } + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); + return desc; } EXPORT_SYMBOL_GPL(gpiochip_request_own_desc); @@ -2476,24 +2565,38 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); * rely on gpio_request() having been called beforehand. */ -static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, - unsigned long config) +int gpio_do_set_config(struct gpio_desc *desc, unsigned long config) { - if (!gc->set_config) + int ret; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + if (!guard.gc->set_config) return -ENOTSUPP; - return gc->set_config(gc, offset, config); + ret = guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config); +#ifdef CONFIG_GPIO_CDEV + /* + * Special case - if we're setting debounce period, we need to store + * it in the descriptor in case user-space wants to know it. + */ + if (!ret && pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) + WRITE_ONCE(desc->debounce_period_us, + pinconf_to_config_argument(config)); +#endif + return ret; } static int gpio_set_config_with_argument(struct gpio_desc *desc, enum pin_config_param mode, u32 argument) { - struct gpio_chip *gc = desc->gdev->chip; unsigned long config; config = pinconf_to_config_packed(mode, argument); - return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); + return gpio_do_set_config(desc, config); } static int gpio_set_config_with_argument_optional(struct gpio_desc *desc, @@ -2527,13 +2630,16 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) static int gpio_set_bias(struct gpio_desc *desc) { enum pin_config_param bias; + unsigned long flags; unsigned int arg; - if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + flags = READ_ONCE(desc->flags); + + if (test_bit(FLAG_BIAS_DISABLE, &flags)) bias = PIN_CONFIG_BIAS_DISABLE; - else if (test_bit(FLAG_PULL_UP, &desc->flags)) + else if (test_bit(FLAG_PULL_UP, &flags)) bias = PIN_CONFIG_BIAS_PULL_UP; - else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + else if (test_bit(FLAG_PULL_DOWN, &flags)) bias = PIN_CONFIG_BIAS_PULL_DOWN; else return 0; @@ -2560,13 +2666,20 @@ 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) { - return gpio_set_config_with_argument_optional(desc, - PIN_CONFIG_INPUT_DEBOUNCE, - debounce); + int ret; + + ret = gpio_set_config_with_argument_optional(desc, + PIN_CONFIG_INPUT_DEBOUNCE, + debounce); + if (!ret) + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); + + return ret; } /** @@ -2576,22 +2689,37 @@ 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) { - struct gpio_chip *gc; - int ret = 0; + int ret; VALIDATE_DESC(desc); - gc = desc->gdev->chip; + + ret = gpiod_direction_input_nonotify(desc); + if (ret == 0) + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); + + return ret; +} +EXPORT_SYMBOL_GPL(gpiod_direction_input); + +int gpiod_direction_input_nonotify(struct gpio_desc *desc) +{ + int ret = 0, dir; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; /* * It is legal to have no .get() and .direction_input() specified if * the chip is output-only, but you can't specify .direction_input() * and not support the .get() operation, that doesn't make sense. */ - if (!gc->get && gc->direction_input) { + if (!guard.gc->get && guard.gc->direction_input) { gpiod_warn(desc, "%s: missing get() but have direction_input()\n", __func__); @@ -2604,14 +2732,21 @@ int gpiod_direction_input(struct gpio_desc *desc) * direction (if .get_direction() is supported) else we silently * assume we are in input mode after this. */ - if (gc->direction_input) { - ret = gc->direction_input(gc, gpio_chip_hwgpio(desc)); - } else if (gc->get_direction && - (gc->get_direction(gc, gpio_chip_hwgpio(desc)) != 1)) { - gpiod_warn(desc, - "%s: missing direction_input() operation and line is output\n", - __func__); - return -EIO; + if (guard.gc->direction_input) { + ret = guard.gc->direction_input(guard.gc, + gpio_chip_hwgpio(desc)); + } else if (guard.gc->get_direction) { + dir = guard.gc->get_direction(guard.gc, + gpio_chip_hwgpio(desc)); + if (dir < 0) + return dir; + + if (dir != GPIO_LINE_DIRECTION_IN) { + gpiod_warn(desc, + "%s: missing direction_input() operation and line is output\n", + __func__); + return -EIO; + } } if (ret == 0) { clear_bit(FLAG_IS_OUT, &desc->flags); @@ -2622,42 +2757,50 @@ int gpiod_direction_input(struct gpio_desc *desc) return ret; } -EXPORT_SYMBOL_GPL(gpiod_direction_input); static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { - struct gpio_chip *gc = desc->gdev->chip; - int val = !!value; - int ret = 0; + int val = !!value, ret = 0, dir; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; /* * It's OK not to specify .direction_output() if the gpiochip is * output-only, but if there is then not even a .set() operation it * is pretty tricky to drive the output line. */ - if (!gc->set && !gc->direction_output) { + if (!guard.gc->set && !guard.gc->direction_output) { gpiod_warn(desc, "%s: missing set() and direction_output() operations\n", __func__); return -EIO; } - if (gc->direction_output) { - ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val); + if (guard.gc->direction_output) { + ret = guard.gc->direction_output(guard.gc, + gpio_chip_hwgpio(desc), val); } else { /* Check that we are in output mode if we can */ - if (gc->get_direction && - gc->get_direction(gc, gpio_chip_hwgpio(desc))) { - gpiod_warn(desc, - "%s: missing direction_output() operation\n", - __func__); - return -EIO; + if (guard.gc->get_direction) { + dir = guard.gc->get_direction(guard.gc, + gpio_chip_hwgpio(desc)); + if (dir < 0) + return dir; + + if (dir != GPIO_LINE_DIRECTION_OUT) { + gpiod_warn(desc, + "%s: missing direction_output() operation\n", + __func__); + return -EIO; + } } /* * If we can't actively set the direction, we are some * output-only chip, so just drive the output as desired. */ - gc->set(gc, gpio_chip_hwgpio(desc), val); + guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), val); } if (!ret) @@ -2676,12 +2819,20 @@ 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) { + int ret; + VALIDATE_DESC(desc); - return gpiod_direction_output_raw_commit(desc, value); + + ret = gpiod_direction_output_raw_commit(desc, value); + if (ret == 0) + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); + + return ret; } EXPORT_SYMBOL_GPL(gpiod_direction_output_raw); @@ -2695,44 +2846,61 @@ 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) { int ret; VALIDATE_DESC(desc); - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + + ret = gpiod_direction_output_nonotify(desc, value); + if (ret == 0) + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); + + return ret; +} +EXPORT_SYMBOL_GPL(gpiod_direction_output); + +int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value) +{ + unsigned long flags; + int ret; + + flags = READ_ONCE(desc->flags); + + if (test_bit(FLAG_ACTIVE_LOW, &flags)) value = !value; else value = !!value; /* GPIOs used for enabled IRQs shall not be set as output */ - if (test_bit(FLAG_USED_AS_IRQ, &desc->flags) && - test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) { + if (test_bit(FLAG_USED_AS_IRQ, &flags) && + test_bit(FLAG_IRQ_IS_ENABLED, &flags)) { gpiod_err(desc, "%s: tried to set a GPIO tied to an IRQ as output\n", __func__); return -EIO; } - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { + if (test_bit(FLAG_OPEN_DRAIN, &flags)) { /* First see if we can enable open drain in hardware */ ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_DRAIN); if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ if (value) { - ret = gpiod_direction_input(desc); + ret = gpiod_direction_input_nonotify(desc); goto set_output_flag; } - } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { + } else if (test_bit(FLAG_OPEN_SOURCE, &flags)) { ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ if (!value) { - ret = gpiod_direction_input(desc); + ret = gpiod_direction_input_nonotify(desc); goto set_output_flag; } } else { @@ -2756,7 +2924,6 @@ set_output_flag: set_bit(FLAG_IS_OUT, &desc->flags); return ret; } -EXPORT_SYMBOL_GPL(gpiod_direction_output); /** * gpiod_enable_hw_timestamp_ns - Enable hardware timestamp in nanoseconds. @@ -2764,22 +2931,26 @@ 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) { int ret = 0; - struct gpio_chip *gc; VALIDATE_DESC(desc); - gc = desc->gdev->chip; - if (!gc->en_hw_timestamp) { + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + if (!guard.gc->en_hw_timestamp) { gpiod_warn(desc, "%s: hw ts not supported\n", __func__); return -ENOTSUPP; } - ret = gc->en_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags); + ret = guard.gc->en_hw_timestamp(guard.gc, + gpio_chip_hwgpio(desc), flags); if (ret) gpiod_warn(desc, "%s: hw ts request failed\n", __func__); @@ -2793,22 +2964,26 @@ 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) { int ret = 0; - struct gpio_chip *gc; VALIDATE_DESC(desc); - gc = desc->gdev->chip; - if (!gc->dis_hw_timestamp) { + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + if (!guard.gc->dis_hw_timestamp) { gpiod_warn(desc, "%s: hw ts not supported\n", __func__); return -ENOTSUPP; } - ret = gc->dis_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags); + ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc), + flags); if (ret) gpiod_warn(desc, "%s: hw ts release failed\n", __func__); @@ -2827,12 +3002,30 @@ EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns); */ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) { - struct gpio_chip *gc; + int ret; VALIDATE_DESC(desc); - gc = desc->gdev->chip; - return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); + ret = gpio_do_set_config(desc, config); + if (!ret) { + /* These are the only options we notify the userspace about. */ + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_OPEN_SOURCE: + case PIN_CONFIG_DRIVE_PUSH_PULL: + case PIN_CONFIG_INPUT_DEBOUNCE: + gpiod_line_state_notify(desc, + GPIO_V2_LINE_CHANGED_CONFIG); + break; + default: + break; + } + } + + return ret; } EXPORT_SYMBOL_GPL(gpiod_set_config); @@ -2881,7 +3074,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) { @@ -2898,6 +3092,7 @@ void gpiod_toggle_active_low(struct gpio_desc *desc) { VALIDATE_DESC_VOID(desc); change_bit(FLAG_ACTIVE_LOW, &desc->flags); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); } EXPORT_SYMBOL_GPL(gpiod_toggle_active_low); @@ -2930,10 +3125,19 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) { + struct gpio_device *gdev; struct gpio_chip *gc; int value; - gc = desc->gdev->chip; + /* FIXME Unable to use gpio_chip_guard due to const desc. */ + gdev = desc->gdev; + + guard(srcu)(&gdev->srcu); + + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) + return -ENODEV; + value = gpio_chip_get_value(gc, desc); value = value < 0 ? value : !!value; trace_gpio_value(desc_to_gpio(desc), 1, value); @@ -2943,6 +3147,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { + lockdep_assert_held(&gc->gpiodev->srcu); + if (gc->get_multiple) return gc->get_multiple(gc, mask, bits); if (gc->get) { @@ -2959,12 +3165,21 @@ static int gpio_chip_get_multiple(struct gpio_chip *gc, return -EIO; } +/* The 'other' chip must be protected with its GPIO device's SRCU. */ +static bool gpio_device_chip_cmp(struct gpio_device *gdev, struct gpio_chip *gc) +{ + guard(srcu)(&gdev->srcu); + + return gc == srcu_dereference(gdev->chip, &gdev->srcu); +} + int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, unsigned long *value_bitmap) { + struct gpio_chip *gc; int ret, i = 0; /* @@ -2976,10 +3191,15 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, array_size <= array_info->size && (void *)array_info == desc_array + array_info->size) { if (!can_sleep) - WARN_ON(array_info->chip->can_sleep); + WARN_ON(array_info->gdev->can_sleep); - ret = gpio_chip_get_multiple(array_info->chip, - array_info->get_mask, + guard(srcu)(&array_info->gdev->srcu); + gc = srcu_dereference(array_info->gdev->chip, + &array_info->gdev->srcu); + if (!gc) + return -ENODEV; + + ret = gpio_chip_get_multiple(gc, array_info->get_mask, value_bitmap); if (ret) return ret; @@ -2996,33 +3216,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, } while (i < array_size) { - struct gpio_chip *gc = desc_array[i]->gdev->chip; DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO); DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO); unsigned long *mask, *bits; int first, j; - if (likely(gc->ngpio <= FASTPATH_NGPIO)) { + CLASS(gpio_chip_guard, guard)(desc_array[i]); + if (!guard.gc) + return -ENODEV; + + if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) { mask = fastpath_mask; bits = fastpath_bits; } else { gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC; - mask = bitmap_alloc(gc->ngpio, flags); + mask = bitmap_alloc(guard.gc->ngpio, flags); if (!mask) return -ENOMEM; - bits = bitmap_alloc(gc->ngpio, flags); + bits = bitmap_alloc(guard.gc->ngpio, flags); if (!bits) { bitmap_free(mask); return -ENOMEM; } } - bitmap_zero(mask, gc->ngpio); + bitmap_zero(mask, guard.gc->ngpio); if (!can_sleep) - WARN_ON(gc->can_sleep); + WARN_ON(guard.gc->can_sleep); /* collect all inputs belonging to the same chip */ first = i; @@ -3037,9 +3260,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, i = find_next_zero_bit(array_info->get_mask, array_size, i); } while ((i < array_size) && - (desc_array[i]->gdev->chip == gc)); + gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc)); - ret = gpio_chip_get_multiple(gc, mask, bits); + ret = gpio_chip_get_multiple(guard.gc, mask, bits); if (ret) { if (mask != fastpath_mask) bitmap_free(mask); @@ -3076,7 +3299,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 @@ -3086,7 +3310,7 @@ int gpiod_get_raw_value(const struct gpio_desc *desc) { VALIDATE_DESC(desc); /* Should be using gpiod_get_raw_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); return gpiod_get_raw_value_commit(desc); } EXPORT_SYMBOL_GPL(gpiod_get_raw_value); @@ -3095,7 +3319,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 @@ -3107,7 +3332,7 @@ int gpiod_get_value(const struct gpio_desc *desc) VALIDATE_DESC(desc); /* Should be using gpiod_get_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); value = gpiod_get_raw_value_commit(desc); if (value < 0) @@ -3128,11 +3353,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, @@ -3155,10 +3382,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, @@ -3180,14 +3410,16 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value); */ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) { - int ret = 0; - struct gpio_chip *gc = desc->gdev->chip; - int offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpio_chip_hwgpio(desc); + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; if (value) { - ret = gc->direction_input(gc, offset); + ret = guard.gc->direction_input(guard.gc, offset); } else { - ret = gc->direction_output(gc, offset, 0); + ret = guard.gc->direction_output(guard.gc, offset, 0); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } @@ -3205,16 +3437,18 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) */ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) { - int ret = 0; - struct gpio_chip *gc = desc->gdev->chip; - int offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpio_chip_hwgpio(desc); + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; if (value) { - ret = gc->direction_output(gc, offset, 1); + ret = guard.gc->direction_output(guard.gc, offset, 1); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } else { - ret = gc->direction_input(gc, offset); + ret = guard.gc->direction_input(guard.gc, offset); } trace_gpio_direction(desc_to_gpio(desc), !value, ret); if (ret < 0) @@ -3225,11 +3459,12 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { - struct gpio_chip *gc; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; - gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); - gc->set(gc, gpio_chip_hwgpio(desc), value); + guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), value); } /* @@ -3245,6 +3480,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) static void gpio_chip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { + lockdep_assert_held(&gc->gpiodev->srcu); + if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); } else { @@ -3262,6 +3499,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, struct gpio_array *array_info, unsigned long *value_bitmap) { + struct gpio_chip *gc; int i = 0; /* @@ -3273,14 +3511,19 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, array_size <= array_info->size && (void *)array_info == desc_array + array_info->size) { if (!can_sleep) - WARN_ON(array_info->chip->can_sleep); + WARN_ON(array_info->gdev->can_sleep); + + guard(srcu)(&array_info->gdev->srcu); + gc = srcu_dereference(array_info->gdev->chip, + &array_info->gdev->srcu); + if (!gc) + return -ENODEV; if (!raw && !bitmap_empty(array_info->invert_mask, array_size)) bitmap_xor(value_bitmap, value_bitmap, array_info->invert_mask, array_size); - gpio_chip_set_multiple(array_info->chip, array_info->set_mask, - value_bitmap); + gpio_chip_set_multiple(gc, array_info->set_mask, value_bitmap); i = find_first_zero_bit(array_info->set_mask, array_size); if (i == array_size) @@ -3290,33 +3533,36 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, } while (i < array_size) { - struct gpio_chip *gc = desc_array[i]->gdev->chip; DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO); DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO); unsigned long *mask, *bits; int count = 0; - if (likely(gc->ngpio <= FASTPATH_NGPIO)) { + CLASS(gpio_chip_guard, guard)(desc_array[i]); + if (!guard.gc) + return -ENODEV; + + if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) { mask = fastpath_mask; bits = fastpath_bits; } else { gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC; - mask = bitmap_alloc(gc->ngpio, flags); + mask = bitmap_alloc(guard.gc->ngpio, flags); if (!mask) return -ENOMEM; - bits = bitmap_alloc(gc->ngpio, flags); + bits = bitmap_alloc(guard.gc->ngpio, flags); if (!bits) { bitmap_free(mask); return -ENOMEM; } } - bitmap_zero(mask, gc->ngpio); + bitmap_zero(mask, guard.gc->ngpio); if (!can_sleep) - WARN_ON(gc->can_sleep); + WARN_ON(guard.gc->can_sleep); do { struct gpio_desc *desc = desc_array[i]; @@ -3352,10 +3598,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, i = find_next_zero_bit(array_info->set_mask, array_size, i); } while ((i < array_size) && - (desc_array[i]->gdev->chip == gc)); + gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc)); /* push collected bits to outputs */ if (count != 0) - gpio_chip_set_multiple(gc, mask, bits); + gpio_chip_set_multiple(guard.gc, mask, bits); if (mask != fastpath_mask) bitmap_free(mask); @@ -3380,7 +3626,7 @@ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_raw_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); gpiod_set_raw_value_commit(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value); @@ -3421,7 +3667,7 @@ void gpiod_set_value(struct gpio_desc *desc, int value) { VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); gpiod_set_value_nocheck(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_value); @@ -3438,6 +3684,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, @@ -3463,6 +3712,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, @@ -3481,11 +3733,13 @@ 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) { VALIDATE_DESC(desc); - return desc->gdev->chip->can_sleep; + return desc->gdev->can_sleep; } EXPORT_SYMBOL_GPL(gpiod_cansleep); @@ -3493,20 +3747,21 @@ 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) { + int ret; + VALIDATE_DESC(desc); - if (name) { - name = kstrdup_const(name, GFP_KERNEL); - if (!name) - return -ENOMEM; - } - kfree_const(desc->label); - desc_set_label(desc, name); + ret = desc_set_label(desc, name); + if (ret == 0) + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); - return 0; + return ret; } EXPORT_SYMBOL_GPL(gpiod_set_consumer_name); @@ -3514,11 +3769,12 @@ 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) { + struct gpio_device *gdev; struct gpio_chip *gc; int offset; @@ -3527,10 +3783,16 @@ 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) || !desc->gdev || !desc->gdev->chip) + if (IS_ERR_OR_NULL(desc)) return -EINVAL; - gc = desc->gdev->chip; + gdev = desc->gdev; + /* FIXME Cannot use gpio_chip_guard due to const desc. */ + guard(srcu)(&gdev->srcu); + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) + return -ENODEV; + offset = gpio_chip_hwgpio(desc); if (gc->to_irq) { int retirq = gc->to_irq(gc, offset); @@ -3562,6 +3824,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) { @@ -3597,14 +3862,6 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) set_bit(FLAG_USED_AS_IRQ, &desc->flags); set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); - /* - * If the consumer has not set up a label (such as when the - * IRQ is referenced from .to_irq()) we set up a label here - * so it is clear this is used as an interrupt. - */ - if (!desc->label) - desc_set_label(desc, "interrupt"); - return 0; } EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq); @@ -3627,10 +3884,6 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) clear_bit(FLAG_USED_AS_IRQ, &desc->flags); clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); - - /* If we only had this marking, erase it */ - if (desc->label && !strcmp(desc->label, "interrupt")) - desc_set_label(desc, NULL); } EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); @@ -3725,7 +3978,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. @@ -3742,7 +3996,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. @@ -3772,10 +4027,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, @@ -3799,9 +4056,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, @@ -3864,6 +4124,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, @@ -3906,6 +4169,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, @@ -3923,8 +4189,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action) { - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - action, desc); + guard(read_lock_irqsave)(&desc->gdev->line_state_lock); + + raw_notifier_call_chain(&desc->gdev->line_state_notifier, action, desc); } /** @@ -4119,58 +4386,65 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode, enum gpiod_flags *flags, unsigned long *lookupflags) { + const char *name = function_name_or_default(con_id); struct gpio_desc *desc = ERR_PTR(-ENOENT); if (is_of_node(fwnode)) { - dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n", - fwnode, con_id); + dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n", fwnode, name); desc = of_find_gpio(to_of_node(fwnode), con_id, idx, lookupflags); } else if (is_acpi_node(fwnode)) { - dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n", - fwnode, con_id); + dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n", fwnode, name); desc = acpi_find_gpio(fwnode, con_id, idx, flags, lookupflags); } else if (is_software_node(fwnode)) { - dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n", - fwnode, con_id); + dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n", fwnode, name); desc = swnode_find_gpio(fwnode, con_id, idx, lookupflags); } return desc; } -static struct gpio_desc *gpiod_find_and_request(struct device *consumer, - struct fwnode_handle *fwnode, - const char *con_id, - unsigned int idx, - enum gpiod_flags flags, - const char *label, - bool platform_lookup_allowed) +struct gpio_desc *gpiod_find_and_request(struct device *consumer, + struct fwnode_handle *fwnode, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags, + const char *label, + bool platform_lookup_allowed) { unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT; - struct gpio_desc *desc; - int ret; + const char *name = function_name_or_default(con_id); + /* + * scoped_guard() is implemented as a for loop, meaning static + * analyzers will complain about these two not being initialized. + */ + struct gpio_desc *desc = NULL; + int ret = 0; + + scoped_guard(srcu, &gpio_devices_srcu) { + desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, + &flags, &lookupflags); + if (gpiod_not_found(desc) && platform_lookup_allowed) { + /* + * Either we are not using DT or ACPI, or their lookup + * did not return a result. In that case, use platform + * lookup as a fallback. + */ + dev_dbg(consumer, + "using lookup tables for GPIO lookup\n"); + desc = gpiod_find(consumer, con_id, idx, &lookupflags); + } + + if (IS_ERR(desc)) { + dev_dbg(consumer, "No GPIO consumer %s found\n", name); + return desc; + } - desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, &flags, &lookupflags); - if (gpiod_not_found(desc) && platform_lookup_allowed) { /* - * Either we are not using DT or ACPI, or their lookup did not - * return a result. In that case, use platform lookup as a - * fallback. + * If a connection label was passed use that, else attempt to use + * the device name as label */ - dev_dbg(consumer, "using lookup tables for GPIO lookup\n"); - desc = gpiod_find(consumer, con_id, idx, &lookupflags); + ret = gpiod_request(desc, label); } - - if (IS_ERR(desc)) { - dev_dbg(consumer, "No GPIO consumer %s found\n", con_id); - return desc; - } - - /* - * If a connection label was passed use that, else attempt to use - * the device name as label - */ - ret = gpiod_request(desc, label); if (ret) { if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) return ERR_PTR(ret); @@ -4183,19 +4457,18 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer, * * FIXME: Make this more sane and safe. */ - dev_info(consumer, - "nonexclusive access to GPIO for %s\n", con_id); + dev_info(consumer, "nonexclusive access to GPIO for %s\n", name); return desc; } ret = gpiod_configure_flags(desc, con_id, lookupflags, flags); if (ret < 0) { - dev_dbg(consumer, "setup of GPIO %s failed\n", con_id); gpiod_put(desc); + dev_err(consumer, "setup of GPIO %s failed: %d\n", name, ret); return ERR_PTR(ret); } - gpiod_line_state_notify(desc, GPIOLINE_CHANGED_REQUESTED); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); return desc; } @@ -4233,9 +4506,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) { @@ -4243,9 +4519,9 @@ int gpiod_count(struct device *dev, const char *con_id) int count = -ENOENT; if (is_of_node(fwnode)) - count = of_gpio_get_count(dev, con_id); + count = of_gpio_count(fwnode, con_id); else if (is_acpi_node(fwnode)) - count = acpi_gpio_count(dev, con_id); + count = acpi_gpio_count(fwnode, con_id); else if (is_software_node(fwnode)) count = swnode_gpio_count(fwnode, con_id); @@ -4262,7 +4538,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. */ @@ -4282,6 +4559,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, @@ -4300,13 +4582,15 @@ 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. */ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags) { + const char *name = function_name_or_default(con_id); int ret; if (lflags & GPIO_ACTIVE_LOW) @@ -4350,16 +4634,16 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { - gpiod_dbg(desc, "no flags found for %s\n", con_id); + gpiod_dbg(desc, "no flags found for GPIO %s\n", name); return 0; } /* Process flags */ if (dflags & GPIOD_FLAGS_BIT_DIR_OUT) - ret = gpiod_direction_output(desc, + ret = gpiod_direction_output_nonotify(desc, !!(dflags & GPIOD_FLAGS_BIT_DIR_VAL)); else - ret = gpiod_direction_input(desc); + ret = gpiod_direction_input_nonotify(desc); return ret; } @@ -4374,7 +4658,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. */ @@ -4402,6 +4687,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, @@ -4425,30 +4715,37 @@ 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) { - struct gpio_chip *gc; + struct gpio_device *gdev = desc->gdev; struct gpio_desc *local_desc; int hwnum; int ret; - gc = gpiod_to_chip(desc); + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + if (test_and_set_bit(FLAG_IS_HOGGED, &desc->flags)) + return 0; + hwnum = gpio_chip_hwgpio(desc); - local_desc = gpiochip_request_own_desc(gc, hwnum, name, + local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name, lflags, dflags); if (IS_ERR(local_desc)) { + clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = PTR_ERR(local_desc); pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n", - name, gc->label, hwnum, ret); + name, gdev->label, hwnum, ret); return ret; } - /* Mark GPIO as hogged so it can be identified and removed later */ - set_bit(FLAG_IS_HOGGED, &desc->flags); - gpiod_dbg(desc, "hogged as %s%s\n", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? @@ -4477,9 +4774,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, @@ -4487,9 +4786,10 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, { struct gpio_desc *desc; struct gpio_descs *descs; + struct gpio_device *gdev; struct gpio_array *array_info = NULL; - struct gpio_chip *gc; int count, bitmap_size; + unsigned long dflags; size_t descs_size; count = gpiod_count(dev, con_id); @@ -4510,7 +4810,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, descs->desc[descs->ndescs] = desc; - gc = gpiod_to_chip(desc); + gdev = gpiod_to_gpio_device(desc); /* * If pin hardware number of array member 0 is also 0, select * its chip as a candidate for fast bitmap processing path. @@ -4518,8 +4818,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) { struct gpio_descs *array; - bitmap_size = BITS_TO_LONGS(gc->ngpio > count ? - gc->ngpio : count); + bitmap_size = BITS_TO_LONGS(gdev->ngpio > count ? + gdev->ngpio : count); array = krealloc(descs, descs_size + struct_size(array_info, invert_mask, 3 * bitmap_size), @@ -4539,7 +4839,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, array_info->desc = descs->desc; array_info->size = count; - array_info->chip = gc; + array_info->gdev = gdev; bitmap_set(array_info->get_mask, descs->ndescs, count - descs->ndescs); bitmap_set(array_info->set_mask, descs->ndescs, @@ -4552,7 +4852,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, continue; /* Unmark array members which don't belong to the 'fast' chip */ - if (array_info->chip != gc) { + if (array_info->gdev != gdev) { __clear_bit(descs->ndescs, array_info->get_mask); __clear_bit(descs->ndescs, array_info->set_mask); } @@ -4575,9 +4875,10 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, array_info->set_mask); } } else { + dflags = READ_ONCE(desc->flags); /* Exclude open drain or open source from fast output */ - if (gpiochip_line_is_open_drain(gc, descs->ndescs) || - gpiochip_line_is_open_source(gc, descs->ndescs)) + if (test_bit(FLAG_OPEN_DRAIN, &dflags) || + test_bit(FLAG_OPEN_SOURCE, &dflags)) __clear_bit(descs->ndescs, array_info->set_mask); /* Identify 'fast' pins which require invertion */ @@ -4589,7 +4890,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev, if (array_info) dev_dbg(dev, "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n", - array_info->chip->label, array_info->size, + array_info->gdev->label, array_info->size, *array_info->get_mask, *array_info->set_mask, *array_info->invert_mask); return descs; @@ -4605,6 +4906,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, @@ -4712,90 +5019,112 @@ core_initcall(gpiolib_dev_init); static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { - struct gpio_chip *gc = gdev->chip; bool active_low, is_irq, is_out; unsigned int gpio = gdev->base; struct gpio_desc *desc; + struct gpio_chip *gc; int value; + guard(srcu)(&gdev->srcu); + + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) { + seq_puts(s, "Underlying GPIO chip is gone\n"); + return; + } + for_each_gpio_desc(gc, desc) { - if (test_bit(FLAG_REQUESTED, &desc->flags)) { + guard(srcu)(&desc->gdev->desc_srcu); + 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-%-3d (%-20.20s|%-20.20s) %s %s %s%s\n", - gpio, desc->name ?: "", desc->label, + seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n", + gpio, desc->name ?: "", gpiod_get_label(desc), is_out ? "out" : "in ", value >= 0 ? (value ? "hi" : "lo") : "? ", is_irq ? "IRQ " : "", active_low ? "ACTIVE LOW" : ""); } else if (desc->name) { - seq_printf(s, " gpio-%-3d (%-20.20s)\n", gpio, desc->name); + seq_printf(s, " gpio-%-3u (%-20.20s)\n", gpio, desc->name); } gpio++; } } +struct gpiolib_seq_priv { + bool newline; + int idx; +}; + static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { - unsigned long flags; - struct gpio_device *gdev = NULL; + struct gpiolib_seq_priv *priv; + struct gpio_device *gdev; loff_t index = *pos; - s->private = ""; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + s->private = priv; + if (*pos > 0) + priv->newline = true; + priv->idx = srcu_read_lock(&gpio_devices_srcu); - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) - if (index-- == 0) { - spin_unlock_irqrestore(&gpio_lock, flags); + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { + if (index-- == 0) return gdev; - } - spin_unlock_irqrestore(&gpio_lock, flags); + } return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { - unsigned long flags; - struct gpio_device *gdev = v; - void *ret = NULL; - - spin_lock_irqsave(&gpio_lock, flags); - if (list_is_last(&gdev->list, &gpio_devices)) - ret = NULL; - else - ret = list_first_entry(&gdev->list, struct gpio_device, list); - spin_unlock_irqrestore(&gpio_lock, flags); + struct gpiolib_seq_priv *priv = s->private; + struct gpio_device *gdev = v, *next; - s->private = "\n"; + next = list_entry_rcu(gdev->list.next, struct gpio_device, list); + gdev = &next->list == &gpio_devices ? NULL : next; + priv->newline = true; ++*pos; - return ret; + return gdev; } static void gpiolib_seq_stop(struct seq_file *s, void *v) { + struct gpiolib_seq_priv *priv = s->private; + + srcu_read_unlock(&gpio_devices_srcu, priv->idx); + kfree(priv); } static int gpiolib_seq_show(struct seq_file *s, void *v) { + struct gpiolib_seq_priv *priv = s->private; struct gpio_device *gdev = v; - struct gpio_chip *gc = gdev->chip; + struct gpio_chip *gc; struct device *parent; + if (priv->newline) + seq_putc(s, '\n'); + + guard(srcu)(&gdev->srcu); + + gc = srcu_dereference(gdev->chip, &gdev->srcu); if (!gc) { - seq_printf(s, "%s%s: (dangling chip)", (char *)s->private, - dev_name(&gdev->dev)); + seq_printf(s, "%s: (dangling chip)\n", dev_name(&gdev->dev)); return 0; } - seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private, - dev_name(&gdev->dev), - gdev->base, gdev->base + gdev->ngpio - 1); + seq_printf(s, "%s: GPIOs %u-%u", dev_name(&gdev->dev), gdev->base, + gdev->base + gdev->ngpio - 1); parent = gc->parent; if (parent) seq_printf(s, ", parent: %s/%s", diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index a4a2520b5f31..c129a03e2040 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -16,7 +16,9 @@ #include <linux/gpio/driver.h> #include <linux/module.h> #include <linux/notifier.h> -#include <linux/rwsem.h> +#include <linux/spinlock.h> +#include <linux/srcu.h> +#include <linux/workqueue.h> #define GPIOCHIP_NAME "gpiochip" @@ -31,8 +33,11 @@ * @chip: pointer to the corresponding gpiochip, holding static * data for this device * @descs: array of ngpio descriptors. + * @desc_srcu: ensures consistent state of GPIO descriptors exposed to users * @ngpio: the number of GPIO lines on this GPIO device, equal to the size * of the @descs array. + * @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep + * implying that they cannot be used from atomic context * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned * at device creation time. * @label: a descriptive name for the GPIO device, such as the part number @@ -41,11 +46,12 @@ * @list: links gpio_device:s together for traversal * @line_state_notifier: used to notify subscribers about lines being * requested, released or reconfigured + * @line_state_lock: RW-spinlock protecting the line state notifier + * @line_state_wq: used to emit line state events from a separate thread in + * process context * @device_notifier: used to notify character device wait queues about the GPIO * device being unregistered - * @sem: protects the structure from a NULL-pointer dereference of @chip by - * user-space operations when the device gets unregistered during - * a hot-unplug event + * @srcu: protects the pointer to the underlying GPIO chip * @pin_ranges: range of pins served by the GPIO driver * * This state container holds most of the runtime variable data @@ -59,16 +65,20 @@ struct gpio_device { int id; struct device *mockdev; struct module *owner; - struct gpio_chip *chip; + struct gpio_chip __rcu *chip; struct gpio_desc *descs; - int base; + struct srcu_struct desc_srcu; + unsigned int base; u16 ngpio; + bool can_sleep; const char *label; void *data; struct list_head list; - struct blocking_notifier_head line_state_notifier; + struct raw_notifier_head line_state_notifier; + rwlock_t line_state_lock; + struct workqueue_struct *line_state_wq; struct blocking_notifier_head device_notifier; - struct rw_semaphore sem; + struct srcu_struct srcu; #ifdef CONFIG_PINCTRL /* @@ -86,15 +96,28 @@ 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 * * @desc: Array of pointers to the GPIO descriptors * @size: Number of elements in desc - * @chip: Parent GPIO chip + * @gdev: Parent GPIO device * @get_mask: Get mask used in fastpath * @set_mask: Set mask used in fastpath * @invert_mask: Invert mask used in fastpath @@ -106,7 +129,7 @@ static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" }; struct gpio_array { struct gpio_desc **desc; unsigned int size; - struct gpio_chip *chip; + struct gpio_device *gdev; unsigned long *get_mask; unsigned long *set_mask; unsigned long invert_mask[]; @@ -134,10 +157,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); -extern spinlock_t gpio_lock; -extern struct list_head gpio_devices; - void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); +int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value); +int gpiod_direction_input_nonotify(struct gpio_desc *desc); + +struct gpio_desc_label { + struct rcu_head rh; + char str[]; +}; /** * struct gpio_desc - Opaque descriptor for a GPIO @@ -147,6 +174,7 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); * @label: Name of the consumer * @name: Line name * @hog: Pointer to the device node that hogs this line (if any) + * @debounce_period_us: Debounce period in microseconds * * These are obtained using gpiod_get() and are preferable to the old * integer-based handles. @@ -178,16 +206,41 @@ struct gpio_desc { #define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ /* Connection label */ - const char *label; + struct gpio_desc_label __rcu *label; /* Name of the GPIO */ const char *name; #ifdef CONFIG_OF_DYNAMIC struct device_node *hog; #endif +#ifdef CONFIG_GPIO_CDEV + /* debounce period in microseconds */ + unsigned int debounce_period_us; +#endif }; #define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) +struct gpio_chip_guard { + struct gpio_device *gdev; + struct gpio_chip *gc; + int idx; +}; + +DEFINE_CLASS(gpio_chip_guard, + struct gpio_chip_guard, + srcu_read_unlock(&_T.gdev->srcu, _T.idx), + ({ + struct gpio_chip_guard _guard; + + _guard.gdev = desc->gdev; + _guard.idx = srcu_read_lock(&_guard.gdev->srcu); + _guard.gc = srcu_dereference(_guard.gdev->chip, + &_guard.gdev->srcu); + + _guard; + }), + struct gpio_desc *desc) + int gpiod_request(struct gpio_desc *desc, const char *label); void gpiod_free(struct gpio_desc *desc); @@ -202,12 +255,23 @@ static inline int gpiod_request_user(struct gpio_desc *desc, const char *label) return ret; } +struct gpio_desc *gpiod_find_and_request(struct device *consumer, + struct fwnode_handle *fwnode, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags, + const char *label, + bool platform_lookup_allowed); + +int gpio_do_set_config(struct gpio_desc *desc, unsigned long config); int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags); 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); /* * Return the GPIO number of the passed descriptor relative to its chip @@ -219,31 +283,32 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) /* With descriptor prefix */ -#define gpiod_emerg(desc, fmt, ...) \ - pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\ - ##__VA_ARGS__) -#define gpiod_crit(desc, fmt, ...) \ - pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) -#define gpiod_err(desc, fmt, ...) \ - pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) -#define gpiod_warn(desc, fmt, ...) \ - pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) -#define gpiod_info(desc, fmt, ...) \ - pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) -#define gpiod_dbg(desc, fmt, ...) \ - pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\ - ##__VA_ARGS__) +#define gpiod_err(desc, fmt, ...) \ +do { \ + scoped_guard(srcu, &desc->gdev->desc_srcu) { \ + pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + } \ +} while (0) + +#define gpiod_warn(desc, fmt, ...) \ +do { \ + scoped_guard(srcu, &desc->gdev->desc_srcu) { \ + pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + } \ +} while (0) + +#define gpiod_dbg(desc, fmt, ...) \ +do { \ + scoped_guard(srcu, &desc->gdev->desc_srcu) { \ + pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + } \ +} while (0) /* With chip prefix */ -#define chip_emerg(gc, fmt, ...) \ - dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_crit(gc, fmt, ...) \ - dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) #define chip_err(gc, fmt, ...) \ dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) #define chip_warn(gc, fmt, ...) \ |