diff options
Diffstat (limited to 'drivers/gpio')
172 files changed, 5308 insertions, 1970 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f2c39bbff83a..e43abb322fa6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -12,6 +12,9 @@ menuconfig GPIOLIB  	  If unsure, say N. +config GPIOLIB_LEGACY +	def_bool y +  if GPIOLIB  config GPIOLIB_FASTPATH_LIMIT @@ -69,6 +72,14 @@ config GPIO_SYSFS  	  use the character device /dev/gpiochipN with the appropriate  	  ioctl() operations instead. +config GPIO_SYSFS_LEGACY +	bool "Enable legacy functionalities of the sysfs interface" +	depends on GPIO_SYSFS +	default y if GPIO_SYSFS +	help +	  Say Y here if you want to enable the legacy, global GPIO +	  numberspace-based functionalities of the sysfs interface. +  config GPIO_CDEV  	bool "Character device (/dev/gpiochipN) support" if EXPERT  	default y @@ -201,6 +212,7 @@ config GPIO_RASPBERRYPI_EXP  config GPIO_BCM_KONA  	bool "Broadcom Kona GPIO"  	depends on ARCH_BCM_MOBILE || COMPILE_TEST +	select GPIOLIB_IRQCHIP  	help  	  Turn on GPIO support for Broadcom "Kona" chips. @@ -213,6 +225,18 @@ config GPIO_BCM_XGS_IPROC  	help  	  Say yes here to enable GPIO support for Broadcom XGS iProc SoCs. +config GPIO_BLZP1600 +	tristate "Blaize BLZP1600 GPIO support" +	default y if ARCH_BLAIZE +	depends on ARCH_BLAIZE || COMPILE_TEST +	depends on OF_GPIO +	select GPIO_GENERIC +	select GPIOLIB_IRQCHIP +	help +	  Say Y or M here to add support for the Blaize BLZP1600 GPIO device. +	  The controller is based on the Verisilicon Microelectronics GPIO APB v0.2 +	  IP block. +  config GPIO_BRCMSTB  	tristate "BRCMSTB GPIO support"  	default y if (ARCH_BRCMSTB || BMIPS_GENERIC) @@ -241,6 +265,7 @@ config GPIO_DAVINCI  	tristate "TI Davinci/Keystone GPIO support"  	default y if ARCH_DAVINCI  	depends on ((ARM || ARM64) && (ARCH_DAVINCI || ARCH_KEYSTONE || ARCH_K3)) || COMPILE_TEST +	select GPIOLIB_IRQCHIP  	help  	  Say yes here to enable GPIO support for TI Davinci/Keystone SoCs. @@ -340,7 +365,7 @@ config GPIO_GRGPIO  	tristate "Aeroflex Gaisler GRGPIO support"  	depends on OF || COMPILE_TEST  	select GPIO_GENERIC -	select IRQ_DOMAIN +	select GPIOLIB_IRQCHIP  	help  	  Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB  	  VHDL IP core library. @@ -368,8 +393,7 @@ config GPIO_HLWD  config GPIO_ICH  	tristate "Intel ICH GPIO" -	depends on X86 -	depends on LPC_ICH +	depends on (X86 && LPC_ICH) || (COMPILE_TEST && HAS_IOPORT)  	help  	  Say yes here to support the GPIO functionality of a number of Intel  	  ICH-based chipsets.  Currently supported devices: ICH6, ICH7, ICH8 @@ -425,6 +449,7 @@ config GPIO_LPC18XX  	default y if ARCH_LPC18XX  	depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)  	select IRQ_DOMAIN_HIERARCHY +	select GPIOLIB_IRQCHIP  	help  	  Select this option to enable GPIO driver for  	  NXP LPC18XX/43XX devices. @@ -468,7 +493,7 @@ config GPIO_MPC8XXX  		   FSL_SOC_BOOKE || PPC_86xx || ARCH_LAYERSCAPE || ARM || \  		   COMPILE_TEST  	select GPIO_GENERIC -	select IRQ_DOMAIN +	select GPIOLIB_IRQCHIP  	help  	  Say Y here if you're going to use hardware that connects to the  	  MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs. @@ -540,7 +565,7 @@ config GPIO_OMAP  config GPIO_PL061  	tristate "PrimeCell PL061 GPIO support" -	depends on ARM_AMBA +	depends on ARM_AMBA || COMPILE_TEST  	select IRQ_DOMAIN  	select GPIOLIB_IRQCHIP  	help @@ -555,6 +580,7 @@ config GPIO_POLARFIRE_SOC  config GPIO_PXA  	bool "PXA GPIO support"  	depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST +	select GPIOLIB_IRQCHIP  	help  	  Say yes here to support the PXA GPIO device. @@ -604,7 +630,7 @@ config GPIO_ROCKCHIP  config GPIO_RTD  	tristate "Realtek DHC GPIO support" -	depends on ARCH_REALTEK +	depends on ARCH_REALTEK || COMPILE_TEST  	default y  	select GPIOLIB_IRQCHIP  	help @@ -656,6 +682,15 @@ config GPIO_SNPS_CREG  	  where only several fields in register belong to GPIO lines and  	  each GPIO line owns a field with different length and on/off value. +config GPIO_SPACEMIT_K1 +	tristate "SPACEMIT K1 GPIO support" +	depends on ARCH_SPACEMIT || COMPILE_TEST +	depends on OF_GPIO +	select GPIO_GENERIC +	select GPIOLIB_IRQCHIP +	help +	  Say yes here to support the SpacemiT's K1 GPIO device. +  config GPIO_SPEAR_SPICS  	bool "ST SPEAr13xx SPI Chip Select as GPIO support"  	depends on PLAT_SPEAR @@ -753,7 +788,7 @@ config GPIO_UNIPHIER  	  Say yes here to support UniPhier GPIOs.  config GPIO_VF610 -	bool "VF610 GPIO support" +	tristate "VF610 GPIO support"  	default y if SOC_VF610  	depends on ARCH_MXC || COMPILE_TEST  	select GPIOLIB_IRQCHIP @@ -830,14 +865,14 @@ config GPIO_ZEVIO  config GPIO_ZYNQ  	tristate "Xilinx Zynq GPIO support" -	depends on ARCH_ZYNQ || ARCH_ZYNQMP +	depends on ARCH_ZYNQ || ARCH_ZYNQMP || COMPILE_TEST  	select GPIOLIB_IRQCHIP  	help  	  Say yes here to support Xilinx Zynq GPIO controller.  config GPIO_ZYNQMP_MODEPIN  	tristate "ZynqMP ps-mode pin GPIO configuration driver" -	depends on ZYNQMP_FIRMWARE +	depends on ZYNQMP_FIRMWARE || COMPILE_TEST  	default ZYNQMP_FIRMWARE  	help  	  Say yes here to support the ZynqMP ps-mode pin GPIO configuration @@ -866,7 +901,7 @@ config GPIO_AMD_FCH  config GPIO_MSC313  	bool "MStar MSC313 GPIO support" -	depends on ARCH_MSTARV7 +	depends on ARCH_MSTARV7 || COMPILE_TEST  	default ARCH_MSTARV7  	select GPIOLIB_IRQCHIP  	select IRQ_DOMAIN_HIERARCHY @@ -1239,6 +1274,7 @@ config GPIO_ADP5520  config GPIO_ADP5585  	tristate "GPIO Support for ADP5585"  	depends on MFD_ADP5585 +	select GPIOLIB_IRQCHIP  	help  	  This option enables support for the GPIO function found in the Analog  	  Devices ADP5585. @@ -1365,7 +1401,7 @@ config GPIO_DLN2  config HTC_EGPIO  	bool "HTC EGPIO support" -	depends on ARM +	depends on ARM || COMPILE_TEST  	help  	  This driver supports the CPLD egpio chip present on  	  several HTC phones.  It provides basic support for input @@ -1440,6 +1476,16 @@ config GPIO_LP87565  	  This driver can also be built as a module. If so, the module will be  	  called gpio-lp87565. +config GPIO_MACSMC +	tristate "Apple Mac SMC GPIO" +	depends on MFD_MACSMC +	help +	  Support for GPIOs controlled by the SMC microcontroller on Apple Mac +	  systems. + +	  This driver can also be built as a module. If so, the module will be +	  called gpio-macsmc. +  config GPIO_MADERA  	tristate "Cirrus Logic Madera class codecs"  	depends on PINCTRL_MADERA @@ -1463,8 +1509,21 @@ config GPIO_MAX77650  	  GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor.  	  These chips have a single pin that can be configured as GPIO. +config GPIO_MAX77759 +	tristate "Maxim Integrated MAX77759 GPIO support" +	depends on MFD_MAX77759 +	default MFD_MAX77759 +	select GPIOLIB_IRQCHIP +	help +	  GPIO driver for MAX77759 PMIC from Maxim Integrated. +	  There are two GPIOs available on these chips in total, both of +	  which can also generate interrupts. + +	  This driver can also be built as a module. If so, the module will be +	  called gpio-max77759. +  config GPIO_PALMAS -	bool "TI PALMAS series PMICs GPIO" +	tristate "TI PALMAS series PMICs GPIO"  	depends on MFD_PALMAS  	help  	  Select this option to enable GPIO driver for the TI PALMAS @@ -1520,12 +1579,13 @@ config GPIO_TC3589X  config GPIO_TIMBERDALE  	bool "Support for timberdale GPIO IP"  	depends on MFD_TIMBERDALE +	select GPIOLIB_IRQCHIP  	help  	Add support for the GPIO IP in the timberdale FPGA.  config GPIO_TN48M_CPLD  	tristate "Delta Networks TN48M switch CPLD GPIO driver" -	depends on MFD_TN48M_CPLD +	depends on MFD_TN48M_CPLD || COMPILE_TEST  	select GPIO_REGMAP  	help  	  This enables support for the GPIOs found on the Delta @@ -1869,6 +1929,8 @@ menu "Virtual GPIO drivers"  config GPIO_AGGREGATOR  	tristate "GPIO Aggregator" +	select CONFIGFS_FS +	select DEV_SYNC_PROBE  	help  	  Say yes here to enable the GPIO Aggregator, which provides a way to  	  aggregate existing GPIO lines into a new virtual GPIO chip. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index af130882ffee..379f55e9ed1e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,11 +5,12 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG  obj-$(CONFIG_GPIOLIB)		+= gpiolib.o  obj-$(CONFIG_GPIOLIB)		+= gpiolib-devres.o -obj-$(CONFIG_GPIOLIB)		+= gpiolib-legacy.o +obj-$(CONFIG_GPIOLIB_LEGACY)	+= gpiolib-legacy.o  obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o  obj-$(CONFIG_GPIO_CDEV)		+= gpiolib-cdev.o  obj-$(CONFIG_GPIO_SYSFS)	+= gpiolib-sysfs.o  obj-$(CONFIG_GPIO_ACPI)		+= gpiolib-acpi.o +gpiolib-acpi-y			:= gpiolib-acpi-core.o gpiolib-acpi-quirks.o  obj-$(CONFIG_GPIOLIB)		+= gpiolib-swnode.o  # Device drivers. Generally keep list sorted alphabetically @@ -45,6 +46,7 @@ obj-$(CONFIG_GPIO_BCM_XGS_IPROC)	+= gpio-xgs-iproc.o  obj-$(CONFIG_GPIO_BD71815)		+= gpio-bd71815.o  obj-$(CONFIG_GPIO_BD71828)		+= gpio-bd71828.o  obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o +obj-$(CONFIG_GPIO_BLZP1600)		+= gpio-blzp1600.o  obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o  obj-$(CONFIG_GPIO_BT8XX)		+= gpio-bt8xx.o  obj-$(CONFIG_GPIO_CADENCE)		+= gpio-cadence.o @@ -97,6 +99,7 @@ obj-$(CONFIG_GPIO_LP873X)		+= gpio-lp873x.o  obj-$(CONFIG_GPIO_LP87565)		+= gpio-lp87565.o  obj-$(CONFIG_GPIO_LPC18XX)		+= gpio-lpc18xx.o  obj-$(CONFIG_GPIO_LPC32XX)		+= gpio-lpc32xx.o +obj-$(CONFIG_GPIO_MACSMC)		+= gpio-macsmc.o  obj-$(CONFIG_GPIO_MADERA)		+= gpio-madera.o  obj-$(CONFIG_GPIO_MAX3191X)		+= gpio-max3191x.o  obj-$(CONFIG_GPIO_MAX7300)		+= gpio-max7300.o @@ -105,6 +108,7 @@ obj-$(CONFIG_GPIO_MAX730X)		+= gpio-max730x.o  obj-$(CONFIG_GPIO_MAX732X)		+= gpio-max732x.o  obj-$(CONFIG_GPIO_MAX77620)		+= gpio-max77620.o  obj-$(CONFIG_GPIO_MAX77650)		+= gpio-max77650.o +obj-$(CONFIG_GPIO_MAX77759)		+= gpio-max77759.o  obj-$(CONFIG_GPIO_MB86S7X)		+= gpio-mb86s7x.o  obj-$(CONFIG_GPIO_MC33880)		+= gpio-mc33880.o  obj-$(CONFIG_GPIO_MENZ127)		+= gpio-menz127.o @@ -159,6 +163,7 @@ 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_SPACEMIT_K1)		+= gpio-spacemit-k1.o  obj-$(CONFIG_GPIO_SPEAR_SPICS)		+= gpio-spear-spics.o  obj-$(CONFIG_GPIO_SPRD)			+= gpio-sprd.o  obj-$(CONFIG_GPIO_STMPE)		+= gpio-stmpe.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 4b70cbaa1caa..7a09a4f58551 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -44,6 +44,13 @@ Work items:    to a machine description such as device tree, ACPI or fwnode that    implicitly does not use global GPIO numbers. +- Fix drivers to not read back struct gpio_chip::base. Some drivers do +  that and would be broken by attempts to poison it or make it dynamic. +  Example in AT91 pinctrl driver: +  https://lore.kernel.org/all/1d00c056-3d61-4c22-bedd-3bae0bf1ddc4@pengutronix.de/ +  This particular driver is also DT-only, so with the above fixed, the +  base can be made dynamic (set to -1) if CONFIG_GPIO_SYSFS is disabled. +  - When this work is complete (will require some of the items in the    following ongoing work as well) we can delete the old global    numberspace accessors from <linux/gpio.h> and eventually delete @@ -124,6 +131,11 @@ Work items:    helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use    this with dry-coding and sending to maintainers to test +- Move the MMIO GPIO specific fields out of struct gpio_chip into a +  dedicated structure. Currently every GPIO chip has them if gpio-mmio is +  enabled in Kconfig even if it itself doesn't register with the helper +  library. +  -------------------------------------------------------------------------------  Generic regmap GPIO @@ -176,16 +188,12 @@ remove the old ones and finally rename the new ones back to the old names.  ------------------------------------------------------------------------------- -Extend the sysfs ABI to allow exporting lines by their HW offsets - -The need to support the sysfs GPIO class is one of the main obstacles to -removing the global GPIO numberspace from the kernel. In order to wean users -off using global numbers from user-space, extend the existing interface with -new per-gpiochip export/unexport attributes that allow to refer to GPIOs using -their hardware offsets within the chip. +Remove legacy sysfs features -Encourage users to switch to using them and eventually remove the existing -global export/unexport attribues. +We have two parallel per-chip class devices and per-exported-line attribute +groups in sysfs. One is using the obsolete global GPIO numberspace and the +second relies on hardware offsets of pins within the chip. Remove the former +once user-space has switched to using the latter.  ------------------------------------------------------------------------------- diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 4dd5c2c330bb..c226524efeba 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -141,8 +141,8 @@ static int gen_74x164_probe(struct spi_device *spi)  	chip->gpio_chip.label = spi->modalias;  	chip->gpio_chip.direction_output = gen_74x164_direction_output;  	chip->gpio_chip.get = gen_74x164_get_value; -	chip->gpio_chip.set_rv = gen_74x164_set_value; -	chip->gpio_chip.set_multiple_rv = gen_74x164_set_multiple; +	chip->gpio_chip.set = gen_74x164_set_value; +	chip->gpio_chip.set_multiple = gen_74x164_set_multiple;  	chip->gpio_chip.base = -1;  	chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;  	chip->gpio_chip.can_sleep = true; diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index c7ac5a9ffb1f..bd2cc5f4f851 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -8,6 +8,7 @@  #include <linux/bits.h>  #include <linux/err.h>  #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h>  #include <linux/mod_devicetable.h>  #include <linux/module.h>  #include <linux/platform_device.h> @@ -18,8 +19,8 @@  #define MMIO_74XX_BIT_CNT(x)	((x) & GENMASK(7, 0))  struct mmio_74xx_gpio_priv { -	struct gpio_chip	gc; -	unsigned		flags; +	struct gpio_generic_chip gen_gc; +	unsigned int flags;  };  static const struct of_device_id mmio_74xx_gpio_ids[] = { @@ -99,16 +100,15 @@ static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)  {  	struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); -	if (priv->flags & MMIO_74XX_DIR_OUT) { -		gc->set(gc, gpio, val); -		return 0; -	} +	if (priv->flags & MMIO_74XX_DIR_OUT) +		return gpio_generic_chip_set(&priv->gen_gc, gpio, val);  	return -ENOTSUPP;  }  static int mmio_74xx_gpio_probe(struct platform_device *pdev)  { +	struct gpio_generic_chip_config config = { };  	struct mmio_74xx_gpio_priv *priv;  	void __iomem *dat;  	int err; @@ -123,19 +123,21 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev)  	if (IS_ERR(dat))  		return PTR_ERR(dat); -	err = bgpio_init(&priv->gc, &pdev->dev, -			 DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8), -			 dat, NULL, NULL, NULL, NULL, 0); +	config.dev = &pdev->dev; +	config.sz = DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8); +	config.dat = dat; + +	err = gpio_generic_chip_init(&priv->gen_gc, &config);  	if (err)  		return err; -	priv->gc.direction_input = mmio_74xx_dir_in; -	priv->gc.direction_output = mmio_74xx_dir_out; -	priv->gc.get_direction = mmio_74xx_get_direction; -	priv->gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); -	priv->gc.owner = THIS_MODULE; +	priv->gen_gc.gc.direction_input = mmio_74xx_dir_in; +	priv->gen_gc.gc.direction_output = mmio_74xx_dir_out; +	priv->gen_gc.gc.get_direction = mmio_74xx_get_direction; +	priv->gen_gc.gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); +	priv->gen_gc.gc.owner = THIS_MODULE; -	return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); +	return devm_gpiochip_add_data(&pdev->dev, &priv->gen_gc.gc, priv);  }  static struct platform_driver mmio_74xx_gpio_driver = { diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index dc2b941c3726..e5ac2d211013 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -430,7 +430,7 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios,  	chip->direction_input = adnp_gpio_direction_input;  	chip->direction_output = adnp_gpio_direction_output;  	chip->get = adnp_gpio_get; -	chip->set_rv = adnp_gpio_set; +	chip->set = adnp_gpio_set;  	chip->can_sleep = true;  	if (IS_ENABLED(CONFIG_DEBUG_FS)) diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c index 57d12c10cbda..6305c8b7dc05 100644 --- a/drivers/gpio/gpio-adp5520.c +++ b/drivers/gpio/gpio-adp5520.c @@ -122,7 +122,7 @@ static int adp5520_gpio_probe(struct platform_device *pdev)  	gc->direction_input  = adp5520_gpio_direction_input;  	gc->direction_output = adp5520_gpio_direction_output;  	gc->get = adp5520_gpio_get_value; -	gc->set_rv = adp5520_gpio_set_value; +	gc->set = adp5520_gpio_set_value;  	gc->can_sleep = true;  	gc->base = pdata->gpio_start; diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index d5c0f1b267c8..0fd3cc26d017 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -4,67 +4,131 @@   *   * Copyright 2022 NXP   * Copyright 2024 Ideas on Board Oy + * Copyright 2025 Analog Devices, Inc.   */ +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/container_of.h>  #include <linux/device.h>  #include <linux/gpio/driver.h>  #include <linux/mfd/adp5585.h>  #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/notifier.h>  #include <linux/platform_device.h>  #include <linux/regmap.h>  #include <linux/types.h> -#define ADP5585_GPIO_MAX	11 +/* + * Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the + * driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to + * 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver + * uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is + * marked as reserved in the device tree for variants that don't support it. + */ +#define ADP5585_BANK(n)			((n) >= 6 ? 1 : 0) +#define ADP5585_BIT(n)			((n) >= 6 ? BIT((n) - 6) : BIT(n)) + +/* + * Bank 0 covers pins "GPIO 1/R0" to "GPIO 8/R7", numbered 0 to 7 by the + * driver, bank 1 covers pins "GPIO 9/C0" to "GPIO 16/C7", numbered 8 to + * 15 and bank 3 covers pins "GPIO 17/C8" to "GPIO 19/C10", numbered 16 to 18. + */ +#define ADP5589_BANK(n)			((n) >> 3) +#define ADP5589_BIT(n)			BIT((n) & 0x7) + +struct adp5585_gpio_chip { +	int (*bank)(unsigned int off); +	int (*bit)(unsigned int off); +	unsigned int debounce_dis_a; +	unsigned int rpull_cfg_a; +	unsigned int gpo_data_a; +	unsigned int gpo_out_a; +	unsigned int gpio_dir_a; +	unsigned int gpi_stat_a; +	unsigned int gpi_int_lvl_a; +	unsigned int gpi_ev_a; +	unsigned int gpi_ev_min; +	unsigned int gpi_ev_max; +	bool has_bias_hole; +};  struct adp5585_gpio_dev {  	struct gpio_chip gpio_chip; +	struct notifier_block nb; +	const struct adp5585_gpio_chip *info;  	struct regmap *regmap; +	unsigned long irq_mask; +	unsigned long irq_en; +	unsigned long irq_active_high; +	/* used for irqchip bus locking */ +	struct mutex bus_lock;  }; +static int adp5585_gpio_bank(unsigned int off) +{ +	return ADP5585_BANK(off); +} + +static int adp5585_gpio_bit(unsigned int off) +{ +	return ADP5585_BIT(off); +} + +static int adp5589_gpio_bank(unsigned int off) +{ +	return ADP5589_BANK(off); +} + +static int adp5589_gpio_bit(unsigned int off) +{ +	return ADP5589_BIT(off); +} +  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); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info;  	unsigned int val; -	regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val); +	regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), &val); -	return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +	return val & info->bit(off) ? 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); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; -	return regmap_clear_bits(adp5585_gpio->regmap, -				 ADP5585_GPIO_DIRECTION_A + bank, bit); +	return regmap_clear_bits(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), +				 info->bit(off));  }  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); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; +	unsigned int bank = info->bank(off); +	unsigned int bit = info->bit(off);  	int ret; -	ret = regmap_update_bits(adp5585_gpio->regmap, -				 ADP5585_GPO_DATA_OUT_A + bank, bit, -				 val ? bit : 0); +	ret = regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + bank, +				 bit, val ? bit : 0);  	if (ret)  		return ret; -	return regmap_set_bits(adp5585_gpio->regmap, -			       ADP5585_GPIO_DIRECTION_A + bank, bit); +	return regmap_set_bits(adp5585_gpio->regmap, info->gpio_dir_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); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; +	unsigned int bank = info->bank(off); +	unsigned int bit = info->bit(off);  	unsigned int reg;  	unsigned int val; @@ -79,8 +143,8 @@ static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off)  	 * .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, info->gpio_dir_a + bank, &val); +	reg = val & bit ? info->gpo_data_a : info->gpi_stat_a;  	regmap_read(adp5585_gpio->regmap, reg + bank, &val);  	return !!(val & bit); @@ -90,17 +154,17 @@ static int 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); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; +	unsigned int bit = adp5585_gpio->info->bit(off); -	return regmap_update_bits(adp5585_gpio->regmap, -				  ADP5585_GPO_DATA_OUT_A + bank, +	return regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + info->bank(off),  				  bit, val ? bit : 0);  }  static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,  				 unsigned int off, unsigned int bias)  { +	const struct adp5585_gpio_chip *info = adp5585_gpio->info;  	unsigned int bit, reg, mask, val;  	/* @@ -108,8 +172,10 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,  	 * 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; +	bit = off * 2; +	if (info->has_bias_hole) +		bit += (off > 5 ? 4 : 0); +	reg = info->rpull_cfg_a + bit / 8;  	mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);  	val = bias << (bit % 8); @@ -119,22 +185,22 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,  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); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; +	unsigned int bit = adp5585_gpio->info->bit(off);  	return regmap_update_bits(adp5585_gpio->regmap, -				  ADP5585_GPO_OUT_MODE_A + bank, bit, +				  info->gpo_out_a + info->bank(off), 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); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; +	unsigned int bit = adp5585_gpio->info->bit(off);  	return regmap_update_bits(adp5585_gpio->regmap, -				  ADP5585_DEBOUNCE_DIS_A + bank, bit, +				  info->debounce_dis_a + info->bank(off), bit,  				  debounce ? 0 : bit);  } @@ -172,11 +238,175 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off,  	};  } +static int adp5585_gpio_request(struct gpio_chip *chip, unsigned int off) +{ +	struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; +	struct device *dev = chip->parent; +	struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); +	const struct adp5585_regs *regs = adp5585->regs; +	int ret; + +	ret = test_and_set_bit(off, adp5585->pin_usage); +	if (ret) +		return -EBUSY; + +	/* make sure it's configured for GPIO */ +	return regmap_clear_bits(adp5585_gpio->regmap, +				 regs->pin_cfg_a + info->bank(off), +				 info->bit(off)); +} + +static void adp5585_gpio_free(struct gpio_chip *chip, unsigned int off) +{ +	struct device *dev = chip->parent; +	struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + +	clear_bit(off, adp5585->pin_usage); +} + +static int adp5585_gpio_key_event(struct notifier_block *nb, unsigned long key, +				  void *data) +{ +	struct adp5585_gpio_dev *adp5585_gpio = container_of(nb, struct adp5585_gpio_dev, nb); +	struct device *dev = adp5585_gpio->gpio_chip.parent; +	unsigned long key_press = (unsigned long)data; +	unsigned int irq, irq_type; +	struct irq_data *irqd; +	bool active_high; +	unsigned int off; + +	/* make sure the event is for me */ +	if (key < adp5585_gpio->info->gpi_ev_min || key > adp5585_gpio->info->gpi_ev_max) +		return NOTIFY_DONE; + +	off = key - adp5585_gpio->info->gpi_ev_min; +	active_high = test_bit(off, &adp5585_gpio->irq_active_high); + +	irq = irq_find_mapping(adp5585_gpio->gpio_chip.irq.domain, off); +	if (!irq) +		return NOTIFY_BAD; + +	irqd = irq_get_irq_data(irq); +	if (!irqd) { +		dev_err(dev, "Could not get irq(%u) data\n", irq); +		return NOTIFY_BAD; +	} + +	dev_dbg_ratelimited(dev, "gpio-keys event(%u) press=%lu, a_high=%u\n", +			    off, key_press, active_high); + +	if (!active_high) +		key_press = !key_press; + +	irq_type = irqd_get_trigger_type(irqd); + +	if ((irq_type & IRQ_TYPE_EDGE_RISING && key_press) || +	    (irq_type & IRQ_TYPE_EDGE_FALLING && !key_press)) +		handle_nested_irq(irq); + +	return NOTIFY_STOP; +} + +static void adp5585_irq_bus_lock(struct irq_data *d) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + +	mutex_lock(&adp5585_gpio->bus_lock); +} + +static void adp5585_irq_bus_sync_unlock(struct irq_data *d) +{ +	struct gpio_chip *chip = irq_data_get_irq_chip_data(d); +	struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); +	const struct adp5585_gpio_chip *info = adp5585_gpio->info; +	irq_hw_number_t hwirq = irqd_to_hwirq(d); +	bool active_high = test_bit(hwirq, &adp5585_gpio->irq_active_high); +	bool enabled = test_bit(hwirq, &adp5585_gpio->irq_en); +	bool masked = test_bit(hwirq, &adp5585_gpio->irq_mask); +	unsigned int bank = adp5585_gpio->info->bank(hwirq); +	unsigned int bit = adp5585_gpio->info->bit(hwirq); + +	if (masked && !enabled) +		goto out_unlock; +	if (!masked && enabled) +		goto out_unlock; + +	regmap_update_bits(adp5585_gpio->regmap, info->gpi_int_lvl_a + bank, bit, +			   active_high ? bit : 0); +	regmap_update_bits(adp5585_gpio->regmap, info->gpi_ev_a + bank, bit, +			   masked ? 0 : bit); +	assign_bit(hwirq, &adp5585_gpio->irq_en, !masked); + +out_unlock: +	mutex_unlock(&adp5585_gpio->bus_lock); +} + +static void adp5585_irq_mask(struct irq_data *d) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); + +	__set_bit(hwirq, &adp5585_gpio->irq_mask); +	gpiochip_disable_irq(gc, hwirq); +} + +static void adp5585_irq_unmask(struct irq_data *d) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); + +	gpiochip_enable_irq(gc, hwirq); +	__clear_bit(hwirq, &adp5585_gpio->irq_mask); +} + +static int adp5585_irq_set_type(struct irq_data *d, unsigned int type) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); + +	if (!(type & IRQ_TYPE_EDGE_BOTH)) +		return -EINVAL; + +	assign_bit(hwirq, &adp5585_gpio->irq_active_high, +		   type == IRQ_TYPE_EDGE_RISING); + +	irq_set_handler_locked(d, handle_edge_irq); +	return 0; +} + +static const struct irq_chip adp5585_irq_chip = { +	.name = "adp5585", +	.irq_mask = adp5585_irq_mask, +	.irq_unmask = adp5585_irq_unmask, +	.irq_bus_lock = adp5585_irq_bus_lock, +	.irq_bus_sync_unlock = adp5585_irq_bus_sync_unlock, +	.irq_set_type = adp5585_irq_set_type, +	.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void adp5585_gpio_unreg_notifier(void *data) +{ +	struct adp5585_gpio_dev *adp5585_gpio = data; +	struct device *dev = adp5585_gpio->gpio_chip.parent; +	struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + +	blocking_notifier_chain_unregister(&adp5585->event_notifier, +					   &adp5585_gpio->nb); +} +  static int adp5585_gpio_probe(struct platform_device *pdev)  {  	struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent); +	const struct platform_device_id *id = platform_get_device_id(pdev);  	struct adp5585_gpio_dev *adp5585_gpio;  	struct device *dev = &pdev->dev; +	struct gpio_irq_chip *girq;  	struct gpio_chip *gc;  	int ret; @@ -186,6 +416,10 @@ static int adp5585_gpio_probe(struct platform_device *pdev)  	adp5585_gpio->regmap = adp5585->regmap; +	adp5585_gpio->info = (const struct adp5585_gpio_chip *)id->driver_data; +	if (!adp5585_gpio->info) +		return -ENODEV; +  	device_set_of_node_from_dev(dev, dev->parent);  	gc = &adp5585_gpio->gpio_chip; @@ -194,15 +428,45 @@ static int adp5585_gpio_probe(struct platform_device *pdev)  	gc->direction_input = adp5585_gpio_direction_input;  	gc->direction_output = adp5585_gpio_direction_output;  	gc->get = adp5585_gpio_get_value; -	gc->set_rv = adp5585_gpio_set_value; +	gc->set = adp5585_gpio_set_value;  	gc->set_config = adp5585_gpio_set_config; +	gc->request = adp5585_gpio_request; +	gc->free = adp5585_gpio_free;  	gc->can_sleep = true;  	gc->base = -1; -	gc->ngpio = ADP5585_GPIO_MAX; +	gc->ngpio = adp5585->n_pins;  	gc->label = pdev->name;  	gc->owner = THIS_MODULE; +	if (device_property_present(dev->parent, "interrupt-controller")) { +		if (!adp5585->irq) +			return dev_err_probe(dev, -EINVAL, +					     "Unable to serve as interrupt controller without IRQ\n"); + +		girq = &adp5585_gpio->gpio_chip.irq; +		gpio_irq_chip_set_chip(girq, &adp5585_irq_chip); +		girq->handler = handle_bad_irq; +		girq->threaded = true; + +		adp5585_gpio->nb.notifier_call = adp5585_gpio_key_event; +		ret = blocking_notifier_chain_register(&adp5585->event_notifier, +						       &adp5585_gpio->nb); +		if (ret) +			return ret; + +		ret = devm_add_action_or_reset(dev, adp5585_gpio_unreg_notifier, +					       adp5585_gpio); +		if (ret) +			return ret; +	} + +	/* everything masked by default */ +	adp5585_gpio->irq_mask = ~0UL; + +	ret = devm_mutex_init(dev, &adp5585_gpio->bus_lock); +	if (ret) +		return ret;  	ret = devm_gpiochip_add_data(dev, &adp5585_gpio->gpio_chip,  				     adp5585_gpio);  	if (ret) @@ -211,8 +475,40 @@ static int adp5585_gpio_probe(struct platform_device *pdev)  	return 0;  } +static const struct adp5585_gpio_chip adp5585_gpio_chip_info = { +	.bank = adp5585_gpio_bank, +	.bit = adp5585_gpio_bit, +	.debounce_dis_a = ADP5585_DEBOUNCE_DIS_A, +	.rpull_cfg_a = ADP5585_RPULL_CONFIG_A, +	.gpo_data_a = ADP5585_GPO_DATA_OUT_A, +	.gpo_out_a = ADP5585_GPO_OUT_MODE_A, +	.gpio_dir_a = ADP5585_GPIO_DIRECTION_A, +	.gpi_stat_a = ADP5585_GPI_STATUS_A, +	.has_bias_hole = true, +	.gpi_ev_min = ADP5585_GPI_EVENT_START, +	.gpi_ev_max = ADP5585_GPI_EVENT_END, +	.gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A, +	.gpi_ev_a = ADP5585_GPI_EVENT_EN_A, +}; + +static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { +	.bank = adp5589_gpio_bank, +	.bit = adp5589_gpio_bit, +	.debounce_dis_a = ADP5589_DEBOUNCE_DIS_A, +	.rpull_cfg_a = ADP5589_RPULL_CONFIG_A, +	.gpo_data_a = ADP5589_GPO_DATA_OUT_A, +	.gpo_out_a = ADP5589_GPO_OUT_MODE_A, +	.gpio_dir_a = ADP5589_GPIO_DIRECTION_A, +	.gpi_stat_a = ADP5589_GPI_STATUS_A, +	.gpi_ev_min = ADP5589_GPI_EVENT_START, +	.gpi_ev_max = ADP5589_GPI_EVENT_END, +	.gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A, +	.gpi_ev_a = ADP5589_GPI_EVENT_EN_A, +}; +  static const struct platform_device_id adp5585_gpio_id_table[] = { -	{ "adp5585-gpio" }, +	{ "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info }, +	{ "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info },  	{ /* Sentinel */ }  };  MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table); diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index d232ea865356..af9d8b3a711d 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -9,10 +9,13 @@  #include <linux/bitmap.h>  #include <linux/bitops.h> +#include <linux/configfs.h>  #include <linux/ctype.h>  #include <linux/delay.h>  #include <linux/idr.h>  #include <linux/kernel.h> +#include <linux/list.h> +#include <linux/lockdep.h>  #include <linux/mod_devicetable.h>  #include <linux/module.h>  #include <linux/mutex.h> @@ -27,226 +30,200 @@  #include <linux/gpio/driver.h>  #include <linux/gpio/machine.h> +#include "dev-sync-probe.h" +  #define AGGREGATOR_MAX_GPIOS 512 +#define AGGREGATOR_LEGACY_PREFIX "_sysfs"  /*   * GPIO Aggregator sysfs interface   */  struct gpio_aggregator { +	struct dev_sync_probe_data probe_data; +	struct config_group group;  	struct gpiod_lookup_table *lookups; -	struct platform_device *pdev; +	struct mutex lock; +	int id; + +	/* List of gpio_aggregator_line. Always added in order */ +	struct list_head list_head; + +	/* used by legacy sysfs interface only */ +	bool init_via_sysfs;  	char args[];  }; +struct gpio_aggregator_line { +	struct config_group group; +	struct gpio_aggregator *parent; +	struct list_head entry; + +	/* Line index within the aggregator device */ +	unsigned int idx; + +	/* Custom name for the virtual line */ +	const char *name; +	/* GPIO chip label or line name */ +	const char *key; +	/* Can be negative to indicate lookup by line name */ +	int offset; + +	enum gpio_lookup_flags flags; +}; + +struct gpio_aggregator_pdev_meta { +	bool init_via_sysfs; +}; +  static DEFINE_MUTEX(gpio_aggregator_lock);	/* protects idr */  static DEFINE_IDR(gpio_aggregator_idr); -static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, -			 int hwnum, unsigned int *n) +static int gpio_aggregator_alloc(struct gpio_aggregator **aggr, size_t arg_size)  { -	struct gpiod_lookup_table *lookups; +	int ret; -	lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2), -			   GFP_KERNEL); -	if (!lookups) +	struct gpio_aggregator *new __free(kfree) = kzalloc( +					sizeof(*new) + arg_size, GFP_KERNEL); +	if (!new)  		return -ENOMEM; -	lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0); +	scoped_guard(mutex, &gpio_aggregator_lock) +		ret = idr_alloc(&gpio_aggregator_idr, new, 0, 0, GFP_KERNEL); -	(*n)++; -	memset(&lookups->table[*n], 0, sizeof(lookups->table[*n])); +	if (ret < 0) +		return ret; -	aggr->lookups = lookups; +	new->id = ret; +	INIT_LIST_HEAD(&new->list_head); +	mutex_init(&new->lock); +	*aggr = no_free_ptr(new);  	return 0;  } -static int aggr_parse(struct gpio_aggregator *aggr) +static void gpio_aggregator_free(struct gpio_aggregator *aggr)  { -	char *args = skip_spaces(aggr->args); -	char *name, *offsets, *p; -	unsigned int i, n = 0; -	int error = 0; - -	unsigned long *bitmap __free(bitmap) = -			bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); -	if (!bitmap) -		return -ENOMEM; - -	args = next_arg(args, &name, &p); -	while (*args) { -		args = next_arg(args, &offsets, &p); - -		p = get_options(offsets, 0, &error); -		if (error == 0 || *p) { -			/* Named GPIO line */ -			error = aggr_add_gpio(aggr, name, U16_MAX, &n); -			if (error) -				return error; +	scoped_guard(mutex, &gpio_aggregator_lock) +		idr_remove(&gpio_aggregator_idr, aggr->id); -			name = offsets; -			continue; -		} +	mutex_destroy(&aggr->lock); +	kfree(aggr); +} -		/* GPIO chip + offset(s) */ -		error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); -		if (error) { -			pr_err("Cannot parse %s: %d\n", offsets, error); -			return error; -		} +static int gpio_aggregator_add_gpio(struct gpio_aggregator *aggr, +				    const char *key, int hwnum, unsigned int *n) +{ +	struct gpiod_lookup_table *lookups; -		for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { -			error = aggr_add_gpio(aggr, name, i, &n); -			if (error) -				return error; -		} +	lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2), +			   GFP_KERNEL); +	if (!lookups) +		return -ENOMEM; -		args = next_arg(args, &name, &p); -	} +	lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0); -	if (!n) { -		pr_err("No GPIOs specified\n"); -		return -EINVAL; -	} +	(*n)++; +	memset(&lookups->table[*n], 0, sizeof(lookups->table[*n])); +	aggr->lookups = lookups;  	return 0;  } -static ssize_t new_device_store(struct device_driver *driver, const char *buf, -				size_t count) +static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr)  { -	struct gpio_aggregator *aggr; -	struct platform_device *pdev; -	int res, id; +	lockdep_assert_held(&aggr->lock); -	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) { -		res = -ENOMEM; -		goto put_module; -	} - -	memcpy(aggr->args, buf, count + 1); +	return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev); +} -	aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), -				GFP_KERNEL); -	if (!aggr->lookups) { -		res = -ENOMEM; -		goto free_ga; -	} +/* Only aggregators created via legacy sysfs can be "activating". */ +static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr) +{ +	lockdep_assert_held(&aggr->lock); -	mutex_lock(&gpio_aggregator_lock); -	id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); -	mutex_unlock(&gpio_aggregator_lock); +	return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev); +} -	if (id < 0) { -		res = id; -		goto free_table; -	} +static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) +{ +	lockdep_assert_held(&aggr->lock); -	aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); -	if (!aggr->lookups->dev_id) { -		res = -ENOMEM; -		goto remove_idr; -	} +	return list_count_nodes(&aggr->list_head); +} -	res = aggr_parse(aggr); -	if (res) -		goto free_dev_id; +static struct gpio_aggregator_line * +gpio_aggregator_line_alloc(struct gpio_aggregator *parent, unsigned int idx, +			   char *key, int offset) +{ +	struct gpio_aggregator_line *line; -	gpiod_add_lookup_table(aggr->lookups); +	line = kzalloc(sizeof(*line), GFP_KERNEL); +	if (!line) +		return ERR_PTR(-ENOMEM); -	pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0); -	if (IS_ERR(pdev)) { -		res = PTR_ERR(pdev); -		goto remove_table; +	if (key) { +		line->key = kstrdup(key, GFP_KERNEL); +		if (!line->key) { +			kfree(line); +			return ERR_PTR(-ENOMEM); +		}  	} -	aggr->pdev = pdev; -	module_put(THIS_MODULE); -	return count; +	line->flags = GPIO_LOOKUP_FLAGS_DEFAULT; +	line->parent = parent; +	line->idx = idx; +	line->offset = offset; +	INIT_LIST_HEAD(&line->entry); -remove_table: -	gpiod_remove_lookup_table(aggr->lookups); -free_dev_id: -	kfree(aggr->lookups->dev_id); -remove_idr: -	mutex_lock(&gpio_aggregator_lock); -	idr_remove(&gpio_aggregator_idr, id); -	mutex_unlock(&gpio_aggregator_lock); -free_table: -	kfree(aggr->lookups); -free_ga: -	kfree(aggr); -put_module: -	module_put(THIS_MODULE); -	return res; -} - -static DRIVER_ATTR_WO(new_device); - -static void gpio_aggregator_free(struct gpio_aggregator *aggr) -{ -	platform_device_unregister(aggr->pdev); -	gpiod_remove_lookup_table(aggr->lookups); -	kfree(aggr->lookups->dev_id); -	kfree(aggr->lookups); -	kfree(aggr); +	return line;  } -static ssize_t delete_device_store(struct device_driver *driver, -				   const char *buf, size_t count) +static void gpio_aggregator_line_add(struct gpio_aggregator *aggr, +				     struct gpio_aggregator_line *line)  { -	struct gpio_aggregator *aggr; -	unsigned int id; -	int error; +	struct gpio_aggregator_line *tmp; -	if (!str_has_prefix(buf, DRV_NAME ".")) -		return -EINVAL; +	lockdep_assert_held(&aggr->lock); -	error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); -	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) { -		module_put(THIS_MODULE); -		return -ENOENT; +	list_for_each_entry(tmp, &aggr->list_head, entry) { +		if (tmp->idx > line->idx) { +			list_add_tail(&line->entry, &tmp->entry); +			return; +		}  	} - -	gpio_aggregator_free(aggr); -	module_put(THIS_MODULE); -	return count; +	list_add_tail(&line->entry, &aggr->list_head);  } -static DRIVER_ATTR_WO(delete_device); - -static struct attribute *gpio_aggregator_attrs[] = { -	&driver_attr_new_device.attr, -	&driver_attr_delete_device.attr, -	NULL -}; -ATTRIBUTE_GROUPS(gpio_aggregator); -static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) +static void gpio_aggregator_line_del(struct gpio_aggregator *aggr, +				     struct gpio_aggregator_line *line)  { -	gpio_aggregator_free(p); -	return 0; +	lockdep_assert_held(&aggr->lock); + +	list_del(&line->entry);  } -static void __exit gpio_aggregator_remove_all(void) +static void gpio_aggregator_free_lines(struct gpio_aggregator *aggr)  { -	mutex_lock(&gpio_aggregator_lock); -	idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); -	idr_destroy(&gpio_aggregator_idr); -	mutex_unlock(&gpio_aggregator_lock); +	struct gpio_aggregator_line *line, *tmp; + +	list_for_each_entry_safe(line, tmp, &aggr->list_head, entry) { +		configfs_unregister_group(&line->group); +		/* +		 * Normally, we acquire aggr->lock within the configfs +		 * callback. However, in the legacy sysfs interface case, +		 * calling configfs_(un)register_group while holding +		 * aggr->lock could cause a deadlock. Fortunately, this is +		 * unnecessary because the new_device/delete_device path +		 * and the module unload path are mutually exclusive, +		 * thanks to an explicit try_module_get. That's why this +		 * minimal scoped_guard suffices. +		 */ +		scoped_guard(mutex, &aggr->lock) +			gpio_aggregator_line_del(aggr, line); +		kfree(line->key); +		kfree(line->name); +		kfree(line); +	}  } @@ -557,8 +534,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,  	chip->direction_output = gpio_fwd_direction_output;  	chip->get = gpio_fwd_get;  	chip->get_multiple = gpio_fwd_get_multiple_locked; -	chip->set_rv = gpio_fwd_set; -	chip->set_multiple_rv = gpio_fwd_set_multiple_locked; +	chip->set = gpio_fwd_set; +	chip->set_multiple = gpio_fwd_set_multiple_locked;  	chip->to_irq = gpio_fwd_to_irq;  	chip->base = -1;  	chip->ngpio = ngpios; @@ -582,6 +559,728 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,  	return fwd;  } +/* + * Configfs interface + */ + +static struct gpio_aggregator * +to_gpio_aggregator(struct config_item *item) +{ +	struct config_group *group = to_config_group(item); + +	return container_of(group, struct gpio_aggregator, group); +} + +static struct gpio_aggregator_line * +to_gpio_aggregator_line(struct config_item *item) +{ +	struct config_group *group = to_config_group(item); + +	return container_of(group, struct gpio_aggregator_line, group); +} + +static struct fwnode_handle * +gpio_aggregator_make_device_sw_node(struct gpio_aggregator *aggr) +{ +	struct property_entry properties[2]; +	struct gpio_aggregator_line *line; +	size_t num_lines; +	int n = 0; + +	memset(properties, 0, sizeof(properties)); + +	num_lines = gpio_aggregator_count_lines(aggr); +	if (num_lines == 0) +		return NULL; + +	const char **line_names __free(kfree) = kcalloc( +				num_lines, sizeof(*line_names), GFP_KERNEL); +	if (!line_names) +		return ERR_PTR(-ENOMEM); + +	/* The list is always sorted as new elements are inserted in order. */ +	list_for_each_entry(line, &aggr->list_head, entry) +		line_names[n++] = line->name ?: ""; + +	properties[0] = PROPERTY_ENTRY_STRING_ARRAY_LEN( +					"gpio-line-names", +					line_names, num_lines); + +	return fwnode_create_software_node(properties, NULL); +} + +static int gpio_aggregator_activate(struct gpio_aggregator *aggr) +{ +	struct platform_device_info pdevinfo; +	struct gpio_aggregator_line *line; +	struct fwnode_handle *swnode; +	unsigned int n = 0; +	int ret = 0; + +	if (gpio_aggregator_count_lines(aggr) == 0) +		return -EINVAL; + +	aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), +				GFP_KERNEL); +	if (!aggr->lookups) +		return -ENOMEM; + +	swnode = gpio_aggregator_make_device_sw_node(aggr); +	if (IS_ERR(swnode)) { +		ret = PTR_ERR(swnode); +		goto err_remove_lookups; +	} + +	memset(&pdevinfo, 0, sizeof(pdevinfo)); +	pdevinfo.name = DRV_NAME; +	pdevinfo.id = aggr->id; +	pdevinfo.fwnode = swnode; + +	/* The list is always sorted as new elements are inserted in order. */ +	list_for_each_entry(line, &aggr->list_head, entry) { +		/* +		 * - Either GPIO chip label or line name must be configured +		 *   (i.e. line->key must be non-NULL) +		 * - Line directories must be named with sequential numeric +		 *   suffixes starting from 0. (i.e. ./line0, ./line1, ...) +		 */ +		if (!line->key || line->idx != n) { +			ret = -EINVAL; +			goto err_remove_swnode; +		} + +		if (line->offset < 0) +			ret = gpio_aggregator_add_gpio(aggr, line->key, +						       U16_MAX, &n); +		else +			ret = gpio_aggregator_add_gpio(aggr, line->key, +						       line->offset, &n); +		if (ret) +			goto err_remove_swnode; +	} + +	aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->id); +	if (!aggr->lookups->dev_id) { +		ret = -ENOMEM; +		goto err_remove_swnode; +	} + +	gpiod_add_lookup_table(aggr->lookups); + +	ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo); +	if (ret) +		goto err_remove_lookup_table; + +	return 0; + +err_remove_lookup_table: +	kfree(aggr->lookups->dev_id); +	gpiod_remove_lookup_table(aggr->lookups); +err_remove_swnode: +	fwnode_remove_software_node(swnode); +err_remove_lookups: +	kfree(aggr->lookups); + +	return ret; +} + +static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) +{ +	dev_sync_probe_unregister(&aggr->probe_data); +	gpiod_remove_lookup_table(aggr->lookups); +	kfree(aggr->lookups->dev_id); +	kfree(aggr->lookups); +} + +static void gpio_aggregator_lockup_configfs(struct gpio_aggregator *aggr, +					    bool lock) +{ +	struct configfs_subsystem *subsys = aggr->group.cg_subsys; +	struct gpio_aggregator_line *line; + +	/* +	 * The device only needs to depend on leaf lines. This is +	 * sufficient to lock up all the configfs entries that the +	 * instantiated, alive device depends on. +	 */ +	list_for_each_entry(line, &aggr->list_head, entry) { +		if (lock) +			configfs_depend_item_unlocked( +					subsys, &line->group.cg_item); +		else +			configfs_undepend_item_unlocked( +					&line->group.cg_item); +	} +} + +static ssize_t +gpio_aggregator_line_key_show(struct config_item *item, char *page) +{ +	struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); +	struct gpio_aggregator *aggr = line->parent; + +	guard(mutex)(&aggr->lock); + +	return sysfs_emit(page, "%s\n", line->key ?: ""); +} + +static ssize_t +gpio_aggregator_line_key_store(struct config_item *item, const char *page, +			       size_t count) +{ +	struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); +	struct gpio_aggregator *aggr = line->parent; + +	char *key __free(kfree) = kstrndup(skip_spaces(page), count, +					   GFP_KERNEL); +	if (!key) +		return -ENOMEM; + +	strim(key); + +	guard(mutex)(&aggr->lock); + +	if (gpio_aggregator_is_activating(aggr) || +	    gpio_aggregator_is_active(aggr)) +		return -EBUSY; + +	kfree(line->key); +	line->key = no_free_ptr(key); + +	return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, key); + +static ssize_t +gpio_aggregator_line_name_show(struct config_item *item, char *page) +{ +	struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); +	struct gpio_aggregator *aggr = line->parent; + +	guard(mutex)(&aggr->lock); + +	return sysfs_emit(page, "%s\n", line->name ?: ""); +} + +static ssize_t +gpio_aggregator_line_name_store(struct config_item *item, const char *page, +				size_t count) +{ +	struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); +	struct gpio_aggregator *aggr = line->parent; + +	char *name __free(kfree) = kstrndup(skip_spaces(page), count, +					    GFP_KERNEL); +	if (!name) +		return -ENOMEM; + +	strim(name); + +	guard(mutex)(&aggr->lock); + +	if (gpio_aggregator_is_activating(aggr) || +	    gpio_aggregator_is_active(aggr)) +		return -EBUSY; + +	kfree(line->name); +	line->name = no_free_ptr(name); + +	return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, name); + +static ssize_t +gpio_aggregator_line_offset_show(struct config_item *item, char *page) +{ +	struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); +	struct gpio_aggregator *aggr = line->parent; + +	guard(mutex)(&aggr->lock); + +	return sysfs_emit(page, "%d\n", line->offset); +} + +static ssize_t +gpio_aggregator_line_offset_store(struct config_item *item, const char *page, +				  size_t count) +{ +	struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); +	struct gpio_aggregator *aggr = line->parent; +	int offset, ret; + +	ret = kstrtoint(page, 0, &offset); +	if (ret) +		return ret; + +	/* +	 * When offset == -1, 'key' represents a line name to lookup. +	 * When 0 <= offset < 65535, '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) || offset < -1) +		return -EINVAL; + +	guard(mutex)(&aggr->lock); + +	if (gpio_aggregator_is_activating(aggr) || +	    gpio_aggregator_is_active(aggr)) +		return -EBUSY; + +	line->offset = offset; + +	return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, offset); + +static struct configfs_attribute *gpio_aggregator_line_attrs[] = { +	&gpio_aggregator_line_attr_key, +	&gpio_aggregator_line_attr_name, +	&gpio_aggregator_line_attr_offset, +	NULL +}; + +static ssize_t +gpio_aggregator_device_dev_name_show(struct config_item *item, char *page) +{ +	struct gpio_aggregator *aggr = to_gpio_aggregator(item); +	struct platform_device *pdev; + +	guard(mutex)(&aggr->lock); + +	pdev = aggr->probe_data.pdev; +	if (pdev) +		return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); + +	return sysfs_emit(page, "%s.%d\n", DRV_NAME, aggr->id); +} +CONFIGFS_ATTR_RO(gpio_aggregator_device_, dev_name); + +static ssize_t +gpio_aggregator_device_live_show(struct config_item *item, char *page) +{ +	struct gpio_aggregator *aggr = to_gpio_aggregator(item); + +	guard(mutex)(&aggr->lock); + +	return sysfs_emit(page, "%c\n", +			  gpio_aggregator_is_active(aggr) ? '1' : '0'); +} + +static ssize_t +gpio_aggregator_device_live_store(struct config_item *item, const char *page, +				  size_t count) +{ +	struct gpio_aggregator *aggr = to_gpio_aggregator(item); +	int ret = 0; +	bool live; + +	ret = kstrtobool(page, &live); +	if (ret) +		return ret; + +	if (!try_module_get(THIS_MODULE)) +		return -ENOENT; + +	if (live && !aggr->init_via_sysfs) +		gpio_aggregator_lockup_configfs(aggr, true); + +	scoped_guard(mutex, &aggr->lock) { +		if (gpio_aggregator_is_activating(aggr) || +		    (live == gpio_aggregator_is_active(aggr))) +			ret = -EPERM; +		else if (live) +			ret = gpio_aggregator_activate(aggr); +		else +			gpio_aggregator_deactivate(aggr); +	} + +	/* +	 * Undepend is required only if device disablement (live == 0) +	 * succeeds or if device enablement (live == 1) fails. +	 */ +	if (live == !!ret && !aggr->init_via_sysfs) +		gpio_aggregator_lockup_configfs(aggr, false); + +	module_put(THIS_MODULE); + +	return ret ?: count; +} +CONFIGFS_ATTR(gpio_aggregator_device_, live); + +static struct configfs_attribute *gpio_aggregator_device_attrs[] = { +	&gpio_aggregator_device_attr_dev_name, +	&gpio_aggregator_device_attr_live, +	NULL +}; + +static void +gpio_aggregator_line_release(struct config_item *item) +{ +	struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); +	struct gpio_aggregator *aggr = line->parent; + +	guard(mutex)(&aggr->lock); + +	gpio_aggregator_line_del(aggr, line); +	kfree(line->key); +	kfree(line->name); +	kfree(line); +} + +static struct configfs_item_operations gpio_aggregator_line_item_ops = { +	.release	= gpio_aggregator_line_release, +}; + +static const struct config_item_type gpio_aggregator_line_type = { +	.ct_item_ops	= &gpio_aggregator_line_item_ops, +	.ct_attrs	= gpio_aggregator_line_attrs, +	.ct_owner	= THIS_MODULE, +}; + +static void gpio_aggregator_device_release(struct config_item *item) +{ +	struct gpio_aggregator *aggr = to_gpio_aggregator(item); + +	/* +	 * At this point, aggr is neither active nor activating, +	 * so calling gpio_aggregator_deactivate() is always unnecessary. +	 */ +	gpio_aggregator_free(aggr); +} + +static struct configfs_item_operations gpio_aggregator_device_item_ops = { +	.release	= gpio_aggregator_device_release, +}; + +static struct config_group * +gpio_aggregator_device_make_group(struct config_group *group, const char *name) +{ +	struct gpio_aggregator *aggr = to_gpio_aggregator(&group->cg_item); +	struct gpio_aggregator_line *line; +	unsigned int idx; +	int ret, nchar; + +	ret = sscanf(name, "line%u%n", &idx, &nchar); +	if (ret != 1 || nchar != strlen(name)) +		return ERR_PTR(-EINVAL); + +	if (aggr->init_via_sysfs) +		/* +		 * Aggregators created via legacy sysfs interface are exposed as +		 * default groups, which means rmdir(2) is prohibited for them. +		 * For simplicity, and to avoid confusion, we also prohibit +		 * mkdir(2). +		 */ +		return ERR_PTR(-EPERM); + +	guard(mutex)(&aggr->lock); + +	if (gpio_aggregator_is_active(aggr)) +		return ERR_PTR(-EBUSY); + +	list_for_each_entry(line, &aggr->list_head, entry) +		if (line->idx == idx) +			return ERR_PTR(-EINVAL); + +	line = gpio_aggregator_line_alloc(aggr, idx, NULL, -1); +	if (IS_ERR(line)) +		return ERR_CAST(line); + +	config_group_init_type_name(&line->group, name, &gpio_aggregator_line_type); + +	gpio_aggregator_line_add(aggr, line); + +	return &line->group; +} + +static struct configfs_group_operations gpio_aggregator_device_group_ops = { +	.make_group	= gpio_aggregator_device_make_group, +}; + +static const struct config_item_type gpio_aggregator_device_type = { +	.ct_group_ops	= &gpio_aggregator_device_group_ops, +	.ct_item_ops	= &gpio_aggregator_device_item_ops, +	.ct_attrs	= gpio_aggregator_device_attrs, +	.ct_owner	= THIS_MODULE, +}; + +static struct config_group * +gpio_aggregator_make_group(struct config_group *group, const char *name) +{ +	struct gpio_aggregator *aggr; +	int ret; + +	/* +	 * "_sysfs" prefix is reserved for auto-generated config group +	 * for devices create via legacy sysfs interface. +	 */ +	if (strncmp(name, AGGREGATOR_LEGACY_PREFIX, +		    sizeof(AGGREGATOR_LEGACY_PREFIX) - 1) == 0) +		return ERR_PTR(-EINVAL); + +	/* arg space is unneeded */ +	ret = gpio_aggregator_alloc(&aggr, 0); +	if (ret) +		return ERR_PTR(ret); + +	config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); +	dev_sync_probe_init(&aggr->probe_data); + +	return &aggr->group; +} + +static struct configfs_group_operations gpio_aggregator_group_ops = { +	.make_group	= gpio_aggregator_make_group, +}; + +static const struct config_item_type gpio_aggregator_type = { +	.ct_group_ops	= &gpio_aggregator_group_ops, +	.ct_owner	= THIS_MODULE, +}; + +static struct configfs_subsystem gpio_aggregator_subsys = { +	.su_group = { +		.cg_item = { +			.ci_namebuf	= DRV_NAME, +			.ci_type	= &gpio_aggregator_type, +		}, +	}, +}; + +/* + * Sysfs interface + */ +static int gpio_aggregator_parse(struct gpio_aggregator *aggr) +{ +	char *args = skip_spaces(aggr->args); +	struct gpio_aggregator_line *line; +	char name[CONFIGFS_ITEM_NAME_LEN]; +	char *key, *offsets, *p; +	unsigned int i, n = 0; +	int error = 0; + +	unsigned long *bitmap __free(bitmap) = +			bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); +	if (!bitmap) +		return -ENOMEM; + +	args = next_arg(args, &key, &p); +	while (*args) { +		args = next_arg(args, &offsets, &p); + +		p = get_options(offsets, 0, &error); +		if (error == 0 || *p) { +			/* Named GPIO line */ +			scnprintf(name, sizeof(name), "line%u", n); +			line = gpio_aggregator_line_alloc(aggr, n, key, -1); +			if (IS_ERR(line)) { +				error = PTR_ERR(line); +				goto err; +			} +			config_group_init_type_name(&line->group, name, +						    &gpio_aggregator_line_type); +			error = configfs_register_group(&aggr->group, +							&line->group); +			if (error) +				goto err; +			scoped_guard(mutex, &aggr->lock) +				gpio_aggregator_line_add(aggr, line); + +			error = gpio_aggregator_add_gpio(aggr, key, U16_MAX, &n); +			if (error) +				goto err; + +			key = offsets; +			continue; +		} + +		/* GPIO chip + offset(s) */ +		error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); +		if (error) { +			pr_err("Cannot parse %s: %d\n", offsets, error); +			goto err; +		} + +		for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { +			scnprintf(name, sizeof(name), "line%u", n); +			line = gpio_aggregator_line_alloc(aggr, n, key, i); +			if (IS_ERR(line)) { +				error = PTR_ERR(line); +				goto err; +			} +			config_group_init_type_name(&line->group, name, +						    &gpio_aggregator_line_type); +			error = configfs_register_group(&aggr->group, +							&line->group); +			if (error) +				goto err; +			scoped_guard(mutex, &aggr->lock) +				gpio_aggregator_line_add(aggr, line); + +			error = gpio_aggregator_add_gpio(aggr, key, i, &n); +			if (error) +				goto err; +		} + +		args = next_arg(args, &key, &p); +	} + +	if (!n) { +		pr_err("No GPIOs specified\n"); +		error = -EINVAL; +		goto err; +	} + +	return 0; + +err: +	gpio_aggregator_free_lines(aggr); +	return error; +} + +static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, +						const char *buf, size_t count) +{ +	struct gpio_aggregator_pdev_meta meta = { .init_via_sysfs = true }; +	char name[CONFIGFS_ITEM_NAME_LEN]; +	struct gpio_aggregator *aggr; +	struct platform_device *pdev; +	int res; + +	if (!try_module_get(THIS_MODULE)) +		return -ENOENT; + +	/* kernfs guarantees string termination, so count + 1 is safe */ +	res = gpio_aggregator_alloc(&aggr, count + 1); +	if (res) +		goto put_module; + +	memcpy(aggr->args, buf, count + 1); + +	aggr->init_via_sysfs = true; +	aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), +				GFP_KERNEL); +	if (!aggr->lookups) { +		res = -ENOMEM; +		goto free_ga; +	} + +	aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->id); +	if (!aggr->lookups->dev_id) { +		res = -ENOMEM; +		goto free_table; +	} + +	scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id); +	config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); + +	/* +	 * Since the device created by sysfs might be toggled via configfs +	 * 'live' attribute later, this initialization is needed. +	 */ +	dev_sync_probe_init(&aggr->probe_data); + +	/* Expose to configfs */ +	res = configfs_register_group(&gpio_aggregator_subsys.su_group, +				      &aggr->group); +	if (res) +		goto free_dev_id; + +	res = gpio_aggregator_parse(aggr); +	if (res) +		goto unregister_group; + +	gpiod_add_lookup_table(aggr->lookups); + +	pdev = platform_device_register_data(NULL, DRV_NAME, aggr->id, &meta, sizeof(meta)); +	if (IS_ERR(pdev)) { +		res = PTR_ERR(pdev); +		goto remove_table; +	} + +	aggr->probe_data.pdev = pdev; +	module_put(THIS_MODULE); +	return count; + +remove_table: +	gpiod_remove_lookup_table(aggr->lookups); +unregister_group: +	configfs_unregister_group(&aggr->group); +free_dev_id: +	kfree(aggr->lookups->dev_id); +free_table: +	kfree(aggr->lookups); +free_ga: +	gpio_aggregator_free(aggr); +put_module: +	module_put(THIS_MODULE); +	return res; +} + +static struct driver_attribute driver_attr_gpio_aggregator_new_device = +	__ATTR(new_device, 0200, NULL, gpio_aggregator_new_device_store); + +static void gpio_aggregator_destroy(struct gpio_aggregator *aggr) +{ +	scoped_guard(mutex, &aggr->lock) { +		if (gpio_aggregator_is_activating(aggr) || +		    gpio_aggregator_is_active(aggr)) +			gpio_aggregator_deactivate(aggr); +	} +	gpio_aggregator_free_lines(aggr); +	configfs_unregister_group(&aggr->group); +	kfree(aggr); +} + +static ssize_t gpio_aggregator_delete_device_store(struct device_driver *driver, +						   const char *buf, size_t count) +{ +	struct gpio_aggregator *aggr; +	unsigned int id; +	int error; + +	if (!str_has_prefix(buf, DRV_NAME ".")) +		return -EINVAL; + +	error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); +	if (error) +		return error; + +	if (!try_module_get(THIS_MODULE)) +		return -ENOENT; + +	mutex_lock(&gpio_aggregator_lock); +	aggr = idr_find(&gpio_aggregator_idr, id); +	/* +	 * For simplicity, devices created via configfs cannot be deleted +	 * via sysfs. +	 */ +	if (aggr && aggr->init_via_sysfs) +		idr_remove(&gpio_aggregator_idr, id); +	else { +		mutex_unlock(&gpio_aggregator_lock); +		module_put(THIS_MODULE); +		return -ENOENT; +	} +	mutex_unlock(&gpio_aggregator_lock); + +	gpio_aggregator_destroy(aggr); +	module_put(THIS_MODULE); +	return count; +} + +static struct driver_attribute driver_attr_gpio_aggregator_delete_device = +	__ATTR(delete_device, 0200, NULL, gpio_aggregator_delete_device_store); + +static struct attribute *gpio_aggregator_attrs[] = { +	&driver_attr_gpio_aggregator_new_device.attr, +	&driver_attr_gpio_aggregator_delete_device.attr, +	NULL +}; +ATTRIBUTE_GROUPS(gpio_aggregator);  /*   *  GPIO Aggregator platform device @@ -589,7 +1288,9 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,  static int gpio_aggregator_probe(struct platform_device *pdev)  { +	struct gpio_aggregator_pdev_meta *meta;  	struct device *dev = &pdev->dev; +	bool init_via_sysfs = false;  	struct gpio_desc **descs;  	struct gpiochip_fwd *fwd;  	unsigned long features; @@ -603,10 +1304,28 @@ static int gpio_aggregator_probe(struct platform_device *pdev)  	if (!descs)  		return -ENOMEM; +	meta = dev_get_platdata(&pdev->dev); +	if (meta && meta->init_via_sysfs) +		init_via_sysfs = true; +  	for (i = 0; i < n; i++) {  		descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS); -		if (IS_ERR(descs[i])) +		if (IS_ERR(descs[i])) { +			/* +			 * Deferred probing is not suitable when the aggregator +			 * is created via configfs. They should just retry later +			 * whenever they like. For device creation via sysfs, +			 * error is propagated without overriding for backward +			 * compatibility. .prevent_deferred_probe is kept unset +			 * for other cases. +			 */ +			if (!init_via_sysfs && !dev_of_node(dev) && +			    descs[i] == ERR_PTR(-EPROBE_DEFER)) { +				pr_warn("Deferred probe canceled for creation via configfs.\n"); +				return -ENODEV; +			}  			return PTR_ERR(descs[i]); +		}  	}  	features = (uintptr_t)device_get_match_data(dev); @@ -640,9 +1359,63 @@ static struct platform_driver gpio_aggregator_driver = {  	},  }; +static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) +{ +	/* +	 * There should be no aggregator created via configfs, as their +	 * presence would prevent module unloading. +	 */ +	gpio_aggregator_destroy(p); +	return 0; +} + +static void __exit gpio_aggregator_remove_all(void) +{ +	/* +	 * Configfs callbacks acquire gpio_aggregator_lock when accessing +	 * gpio_aggregator_idr, so to prevent lock inversion deadlock, we +	 * cannot protect idr_for_each invocation here with +	 * gpio_aggregator_lock, as gpio_aggregator_idr_remove() accesses +	 * configfs groups. Fortunately, the new_device/delete_device path +	 * and the module unload path are mutually exclusive, thanks to an +	 * explicit try_module_get inside of those driver attr handlers. +	 * Also, when we reach here, no configfs entries present or being +	 * created. Therefore, no need to protect with gpio_aggregator_lock +	 * below. +	 */ +	idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); +	idr_destroy(&gpio_aggregator_idr); +} +  static int __init gpio_aggregator_init(void)  { -	return platform_driver_register(&gpio_aggregator_driver); +	int ret = 0; + +	config_group_init(&gpio_aggregator_subsys.su_group); +	mutex_init(&gpio_aggregator_subsys.su_mutex); +	ret = configfs_register_subsystem(&gpio_aggregator_subsys); +	if (ret) { +		pr_err("Failed to register the '%s' configfs subsystem: %d\n", +		       gpio_aggregator_subsys.su_group.cg_item.ci_namebuf, ret); +		mutex_destroy(&gpio_aggregator_subsys.su_mutex); +		return ret; +	} + +	/* +	 * CAVEAT: This must occur after configfs registration. Otherwise, +	 * a race condition could arise: driver attribute groups might be +	 * exposed and accessed by users before configfs registration +	 * completes. new_device_store() does not expect a partially +	 * initialized configfs state. +	 */ +	ret = platform_driver_register(&gpio_aggregator_driver); +	if (ret) { +		pr_err("Failed to register the platform driver: %d\n", ret); +		mutex_destroy(&gpio_aggregator_subsys.su_mutex); +		configfs_unregister_subsystem(&gpio_aggregator_subsys); +	} + +	return ret;  }  module_init(gpio_aggregator_init); @@ -650,6 +1423,7 @@ static void __exit gpio_aggregator_exit(void)  {  	gpio_aggregator_remove_all();  	platform_driver_unregister(&gpio_aggregator_driver); +	configfs_unregister_subsystem(&gpio_aggregator_subsys);  }  module_exit(gpio_aggregator_exit); diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c index 77a674cf99e4..4524c18a87e7 100644 --- a/drivers/gpio/gpio-altera-a10sr.c +++ b/drivers/gpio/gpio-altera-a10sr.c @@ -69,7 +69,7 @@ static const struct gpio_chip altr_a10sr_gc = {  	.label = "altr_a10sr_gpio",  	.owner = THIS_MODULE,  	.get = altr_a10sr_gpio_get, -	.set_rv = altr_a10sr_gpio_set, +	.set = altr_a10sr_gpio_set,  	.direction_input = altr_a10sr_gpio_direction_input,  	.direction_output = altr_a10sr_gpio_direction_output,  	.can_sleep = true, diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 1b28525726d7..9508d764cce4 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -259,7 +259,7 @@ static int altera_gpio_probe(struct platform_device *pdev)  	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_rv		= altera_gpio_set; +	altera_gc->gc.set		= altera_gpio_set;  	altera_gc->gc.owner		= THIS_MODULE;  	altera_gc->gc.parent		= &pdev->dev;  	altera_gc->gc.base		= -1; diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c index f8d0cea46049..e6c6c3ec7656 100644 --- a/drivers/gpio/gpio-amd-fch.c +++ b/drivers/gpio/gpio-amd-fch.c @@ -165,7 +165,7 @@ static int amd_fch_gpio_probe(struct platform_device *pdev)  	priv->gc.direction_output	= amd_fch_gpio_direction_output;  	priv->gc.get_direction		= amd_fch_gpio_get_direction;  	priv->gc.get			= amd_fch_gpio_get; -	priv->gc.set_rv			= amd_fch_gpio_set; +	priv->gc.set			= amd_fch_gpio_set;  	spin_lock_init(&priv->lock); diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 425d8472f744..15fd5e210d74 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -165,7 +165,7 @@ static struct amd_gpio gp = {  		.ngpio		= 32,  		.request	= amd_gpio_request,  		.free		= amd_gpio_free, -		.set_rv		= amd_gpio_set, +		.set		= amd_gpio_set,  		.get		= amd_gpio_get,  		.direction_output = amd_gpio_dirout,  		.direction_input = amd_gpio_dirin, diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index e530c94dcce8..a7e98d395d8e 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -39,7 +39,6 @@ static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset)  		return ret;  	if (change && persistent) { -		pm_runtime_mark_last_busy(chip->parent);  		pm_runtime_put_autosuspend(chip->parent);  	} @@ -82,7 +81,6 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)  			return ret;  		} -		pm_runtime_mark_last_busy(chip->parent);  		pm_runtime_put_autosuspend(chip->parent);  	} @@ -140,7 +138,7 @@ static const struct gpio_chip template_chip = {  	.direction_input	= arizona_gpio_direction_in,  	.get			= arizona_gpio_get,  	.direction_output	= arizona_gpio_direction_out, -	.set_rv			= arizona_gpio_set, +	.set			= arizona_gpio_set,  	.can_sleep		= true,  }; diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 00b31497ecff..7622f9e9f54a 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -596,7 +596,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)  	gpio->chip.request = NULL;  	gpio->chip.free = NULL;  	gpio->chip.get = aspeed_sgpio_get; -	gpio->chip.set_rv = aspeed_sgpio_set; +	gpio->chip.set = aspeed_sgpio_set;  	gpio->chip.set_config = aspeed_sgpio_set_config;  	gpio->chip.label = dev_name(&pdev->dev);  	gpio->chip.base = -1; diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 2d340a343a17..7953a9c4e36d 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -1352,7 +1352,7 @@ static int aspeed_gpio_probe(struct platform_device *pdev)  	gpio->chip.request = aspeed_gpio_request;  	gpio->chip.free = aspeed_gpio_free;  	gpio->chip.get = aspeed_gpio_get; -	gpio->chip.set_rv = aspeed_gpio_set; +	gpio->chip.set = aspeed_gpio_set;  	gpio->chip.set_config = aspeed_gpio_set_config;  	gpio->chip.label = dev_name(&pdev->dev);  	gpio->chip.base = -1; diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 17c287dc7471..208b71c59d58 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -339,7 +339,7 @@ static const struct gpio_chip template_chip = {  	.direction_input = bcm_kona_gpio_direction_input,  	.get = bcm_kona_gpio_get,  	.direction_output = bcm_kona_gpio_direction_output, -	.set_rv = bcm_kona_gpio_set, +	.set = bcm_kona_gpio_set,  	.set_config = bcm_kona_gpio_set_config,  	.to_irq = bcm_kona_gpio_to_irq,  	.base = 0, @@ -516,6 +516,7 @@ static struct irq_chip bcm_gpio_irq_chip = {  	.irq_set_type = bcm_kona_gpio_irq_set_type,  	.irq_request_resources = bcm_kona_gpio_irq_reqres,  	.irq_release_resources = bcm_kona_gpio_irq_relres, +	.flags = IRQCHIP_IMMUTABLE,  };  static struct of_device_id const bcm_kona_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-bd71815.c b/drivers/gpio/gpio-bd71815.c index 36701500925e..afb18a5a9d79 100644 --- a/drivers/gpio/gpio-bd71815.c +++ b/drivers/gpio/gpio-bd71815.c @@ -85,7 +85,7 @@ static const struct gpio_chip bd71815gpo_chip = {  	.owner			= THIS_MODULE,  	.get			= bd71815gpo_get,  	.get_direction		= bd71815gpo_direction_get, -	.set_rv			= bd71815gpo_set, +	.set			= bd71815gpo_set,  	.set_config		= bd71815_gpio_set_config,  	.can_sleep		= true,  }; diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c index 4ba151e5cf25..e439dbfffc62 100644 --- a/drivers/gpio/gpio-bd71828.c +++ b/drivers/gpio/gpio-bd71828.c @@ -109,7 +109,7 @@ static int bd71828_probe(struct platform_device *pdev)  	bdgpio->gpio.set_config = bd71828_gpio_set_config;  	bdgpio->gpio.can_sleep = true;  	bdgpio->gpio.get = bd71828_gpio_get; -	bdgpio->gpio.set_rv = bd71828_gpio_set; +	bdgpio->gpio.set = bd71828_gpio_set;  	bdgpio->gpio.base = -1;  	/* diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index 8df1361e3e84..7c95bb36511e 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -88,7 +88,7 @@ static const struct gpio_chip template_chip = {  	.direction_input	= bd9571mwv_gpio_direction_input,  	.direction_output	= bd9571mwv_gpio_direction_output,  	.get			= bd9571mwv_gpio_get, -	.set_rv			= bd9571mwv_gpio_set, +	.set			= bd9571mwv_gpio_set,  	.base			= -1,  	.ngpio			= 2,  	.can_sleep		= true, diff --git a/drivers/gpio/gpio-blzp1600.c b/drivers/gpio/gpio-blzp1600.c new file mode 100644 index 000000000000..055cb296ae54 --- /dev/null +++ b/drivers/gpio/gpio-blzp1600.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 VeriSilicon Limited. + * Copyright (C) 2025 Blaize, Inc. + */ + +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define GPIO_DIR_REG	0x00 +#define GPIO_CTRL_REG	0x04 +#define GPIO_SET_REG	0x08 +#define GPIO_CLR_REG	0x0C +#define GPIO_ODATA_REG	0x10 +#define GPIO_IDATA_REG	0x14 +#define GPIO_IEN_REG	0x18 +#define GPIO_IS_REG	0x1C +#define GPIO_IBE_REG	0x20 +#define GPIO_IEV_REG	0x24 +#define GPIO_RIS_REG	0x28 +#define GPIO_IM_REG	0x2C +#define GPIO_MIS_REG	0x30 +#define GPIO_IC_REG	0x34 +#define GPIO_DB_REG	0x38 +#define GPIO_DFG_REG	0x3C + +#define DRIVER_NAME "blzp1600-gpio" + +struct blzp1600_gpio { +	void __iomem *base; +	struct gpio_chip gc; +	int irq; +}; + +static inline struct blzp1600_gpio *get_blzp1600_gpio_from_irq_data(struct irq_data *d) +{ +	return gpiochip_get_data(irq_data_get_irq_chip_data(d)); +} + +static inline struct blzp1600_gpio *get_blzp1600_gpio_from_irq_desc(struct irq_desc *d) +{ +	return gpiochip_get_data(irq_desc_get_handler_data(d)); +} + +static inline u32 blzp1600_gpio_read(struct blzp1600_gpio *chip, unsigned int offset) +{ +	return readl_relaxed(chip->base + offset); +} + +static inline void blzp1600_gpio_write(struct blzp1600_gpio *chip, unsigned int offset, u32 val) +{ +	writel_relaxed(val, chip->base + offset); +} + +static inline void blzp1600_gpio_rmw(void __iomem *reg, u32 mask, bool set) +{ +	u32 val = readl_relaxed(reg); + +	if (set) +		val |= mask; +	else +		val &= ~mask; + +	writel_relaxed(val, reg); +} + +static void blzp1600_gpio_irq_mask(struct irq_data *d) +{ +	struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + +	guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); +	blzp1600_gpio_rmw(chip->base + GPIO_IM_REG, BIT(d->hwirq), 1); +} + +static void blzp1600_gpio_irq_unmask(struct irq_data *d) +{ +	struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + +	guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); +	blzp1600_gpio_rmw(chip->base + GPIO_IM_REG, BIT(d->hwirq), 0); +} + +static void blzp1600_gpio_irq_ack(struct irq_data *d) +{ +	struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + +	blzp1600_gpio_write(chip, GPIO_IC_REG, BIT(d->hwirq)); +} + +static void blzp1600_gpio_irq_enable(struct irq_data *d) +{ +	struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + +	gpiochip_enable_irq(&chip->gc, irqd_to_hwirq(d)); + +	guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); +	blzp1600_gpio_rmw(chip->base + GPIO_DIR_REG, BIT(d->hwirq), 0); +	blzp1600_gpio_rmw(chip->base + GPIO_IEN_REG, BIT(d->hwirq), 1); +} + +static void blzp1600_gpio_irq_disable(struct irq_data *d) +{ +	struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + +	guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); +	blzp1600_gpio_rmw(chip->base + GPIO_IEN_REG, BIT(d->hwirq), 0); +	gpiochip_disable_irq(&chip->gc, irqd_to_hwirq(d)); +} + +static int blzp1600_gpio_irq_set_type(struct irq_data *d, u32 type) +{ +	struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); +	u32 edge_level, single_both, fall_rise; +	int mask = BIT(d->hwirq); + +	guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); +	edge_level = blzp1600_gpio_read(chip, GPIO_IS_REG); +	single_both = blzp1600_gpio_read(chip, GPIO_IBE_REG); +	fall_rise = blzp1600_gpio_read(chip, GPIO_IEV_REG); + +	switch (type) { +	case IRQ_TYPE_EDGE_BOTH: +		edge_level &= ~mask; +		single_both |= mask; +		break; +	case IRQ_TYPE_EDGE_RISING: +		edge_level &= ~mask; +		single_both &= ~mask; +		fall_rise |= mask; +		break; +	case IRQ_TYPE_EDGE_FALLING: +		edge_level &= ~mask; +		single_both &= ~mask; +		fall_rise &= ~mask; +		break; +	case IRQ_TYPE_LEVEL_HIGH: +		edge_level |= mask; +		fall_rise |= mask; +		break; +	case IRQ_TYPE_LEVEL_LOW: +		edge_level |= mask; +		fall_rise &= ~mask; +		break; +	default: +		return -EINVAL; +	} + +	blzp1600_gpio_write(chip, GPIO_IS_REG, edge_level); +	blzp1600_gpio_write(chip, GPIO_IBE_REG, single_both); +	blzp1600_gpio_write(chip, GPIO_IEV_REG, fall_rise); + +	if (type & IRQ_TYPE_LEVEL_MASK) +		irq_set_handler_locked(d, handle_level_irq); +	else +		irq_set_handler_locked(d, handle_edge_irq); + +	return 0; +} + +static const struct irq_chip blzp1600_gpio_irqchip = { +	.name = DRIVER_NAME, +	.irq_ack = blzp1600_gpio_irq_ack, +	.irq_mask = blzp1600_gpio_irq_mask, +	.irq_unmask = blzp1600_gpio_irq_unmask, +	.irq_set_type = blzp1600_gpio_irq_set_type, +	.irq_enable = blzp1600_gpio_irq_enable, +	.irq_disable = blzp1600_gpio_irq_disable, +	.flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, +	GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void blzp1600_gpio_irqhandler(struct irq_desc *desc) +{ +	struct blzp1600_gpio *gpio = get_blzp1600_gpio_from_irq_desc(desc); +	struct irq_chip *irqchip = irq_desc_get_chip(desc); +	unsigned long irq_status; +	int hwirq = 0; + +	chained_irq_enter(irqchip, desc); +	irq_status = blzp1600_gpio_read(gpio, GPIO_RIS_REG); +	for_each_set_bit(hwirq, &irq_status, gpio->gc.ngpio) +		generic_handle_domain_irq(gpio->gc.irq.domain, hwirq); + +	chained_irq_exit(irqchip, desc); +} + +static int blzp1600_gpio_set_debounce(struct gpio_chip *gc, unsigned int offset, +				      unsigned int debounce) +{ +	struct blzp1600_gpio *chip = gpiochip_get_data(gc); + +	guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); +	blzp1600_gpio_rmw(chip->base + GPIO_DB_REG, BIT(offset), debounce); + +	return 0; +} + +static int blzp1600_gpio_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) +{ +	u32 debounce; + +	if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) +		return -ENOTSUPP; + +	debounce = pinconf_to_config_argument(config); +	return blzp1600_gpio_set_debounce(gc, offset, debounce); +} + +static int blzp1600_gpio_probe(struct platform_device *pdev) +{ +	struct blzp1600_gpio *chip; +	struct gpio_chip *gc; +	int ret; + +	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); +	if (!chip) +		return -ENOMEM; + +	chip->base = devm_platform_ioremap_resource(pdev, 0); +	if (IS_ERR(chip->base)) +		return PTR_ERR(chip->base); + +	ret = bgpio_init(&chip->gc, &pdev->dev, 4, chip->base + GPIO_IDATA_REG, +			 chip->base + GPIO_SET_REG, chip->base + GPIO_CLR_REG, +			 chip->base + GPIO_DIR_REG, NULL, 0); +	if (ret) +		return dev_err_probe(&pdev->dev, ret, "Failed to register generic gpio\n"); + +	/* configure the gpio chip */ +	gc = &chip->gc; +	gc->set_config = blzp1600_gpio_set_config; + +	if (device_property_present(&pdev->dev, "interrupt-controller")) { +		struct gpio_irq_chip *girq; + +		chip->irq = platform_get_irq(pdev, 0); +		if (chip->irq < 0) +			return chip->irq; + +		girq = &gc->irq; +		gpio_irq_chip_set_chip(girq, &blzp1600_gpio_irqchip); +		girq->parent_handler = blzp1600_gpio_irqhandler; +		girq->num_parents = 1; +		girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents), GFP_KERNEL); +		if (!girq->parents) +			return -ENOMEM; + +		girq->parents[0] = chip->irq; +		girq->default_type = IRQ_TYPE_NONE; +	} + +	return devm_gpiochip_add_data(&pdev->dev, gc, chip); +} + +static const struct of_device_id blzp1600_gpio_of_match[] = { +	{ .compatible = "blaize,blzp1600-gpio", }, +	{ /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, blzp1600_gpio_of_match); + +static struct platform_driver blzp1600_gpio_driver = { +	.driver		= { +		.name	= DRIVER_NAME, +		.of_match_table = blzp1600_gpio_of_match, +	}, +	.probe		= blzp1600_gpio_probe, +}; + +module_platform_driver(blzp1600_gpio_driver); + +MODULE_AUTHOR("Nikolaos Pasaloukos <nikolaos.pasaloukos@blaize.com>"); +MODULE_DESCRIPTION("Blaize BLZP1600 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index ca3472977431..e29a9589b3cc 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -436,10 +436,8 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev,  	struct device_node *np = dev->of_node;  	int err; -	priv->irq_domain = -		irq_domain_add_linear(np, priv->num_gpios, -				      &brcmstb_gpio_irq_domain_ops, -				      priv); +	priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev), priv->num_gpios, +						    &brcmstb_gpio_irq_domain_ops, priv);  	if (!priv->irq_domain) {  		dev_err(dev, "Couldn't allocate IRQ domain\n");  		return -ENXIO; diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 7c9e81fea37a..05401da03ca3 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -145,7 +145,7 @@ static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg)  	c->direction_input = bt8xxgpio_gpio_direction_input;  	c->get = bt8xxgpio_gpio_get;  	c->direction_output = bt8xxgpio_gpio_direction_output; -	c->set_rv = bt8xxgpio_gpio_set; +	c->set = bt8xxgpio_gpio_set;  	c->dbg_show = NULL;  	c->base = modparam_gpiobase;  	c->ngpio = BT8XXGPIO_NR_GPIOS; diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index e9dd2564c54f..c647953521c7 100644 --- a/drivers/gpio/gpio-cadence.c +++ b/drivers/gpio/gpio-cadence.c @@ -8,9 +8,11 @@   *  Boris Brezillon <boris.brezillon@free-electrons.com>   */ -#include <linux/gpio/driver.h> +#include <linux/cleanup.h>  #include <linux/clk.h> +#include <linux/gpio/driver.h>  #include <linux/interrupt.h> +#include <linux/gpio/generic.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/platform_device.h> @@ -30,7 +32,7 @@  #define CDNS_GPIO_IRQ_ANY_EDGE		0x2c  struct cdns_gpio_chip { -	struct gpio_chip gc; +	struct gpio_generic_chip gen_gc;  	void __iomem *regs;  	u32 bypass_orig;  }; @@ -38,29 +40,24 @@ struct cdns_gpio_chip {  static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset)  {  	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); -	unsigned long flags; -	raw_spin_lock_irqsave(&chip->bgpio_lock, flags); +	guard(gpio_generic_lock)(&cgpio->gen_gc);  	iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset),  		  cgpio->regs + CDNS_GPIO_BYPASS_MODE); -	raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);  	return 0;  }  static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset)  {  	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); -	unsigned long flags; -	raw_spin_lock_irqsave(&chip->bgpio_lock, flags); +	guard(gpio_generic_lock)(&cgpio->gen_gc);  	iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) |  		  (BIT(offset) & cgpio->bypass_orig),  		  cgpio->regs + CDNS_GPIO_BYPASS_MODE); - -	raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);  }  static void cdns_gpio_irq_mask(struct irq_data *d) @@ -85,13 +82,12 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)  {  	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);  	struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); -	unsigned long flags;  	u32 int_value;  	u32 int_type;  	u32 mask = BIT(d->hwirq);  	int ret = 0; -	raw_spin_lock_irqsave(&chip->bgpio_lock, flags); +	guard(gpio_generic_lock)(&cgpio->gen_gc);  	int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask;  	int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask; @@ -108,15 +104,12 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)  	} else if (type == IRQ_TYPE_LEVEL_LOW) {  		int_type |= mask;  	} else { -		ret = -EINVAL; -		goto err_irq_type; +		return -EINVAL;  	}  	iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE);  	iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE); -err_irq_type: -	raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags);  	return ret;  } @@ -150,6 +143,7 @@ static const struct irq_chip cdns_gpio_irqchip = {  static int cdns_gpio_probe(struct platform_device *pdev)  { +	struct gpio_generic_chip_config config = { };  	struct cdns_gpio_chip *cgpio;  	int ret, irq;  	u32 dir_prev; @@ -176,32 +170,33 @@ static int cdns_gpio_probe(struct platform_device *pdev)  	 * gpiochip_lock_as_irq:  	 * tried to flag a GPIO set as output for IRQ  	 * Generic GPIO driver stores the direction value internally, -	 * so it needs to be changed before bgpio_init() is called. +	 * so it needs to be changed before gpio_generic_chip_init() is called.  	 */  	dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE);  	iowrite32(GENMASK(num_gpios - 1, 0),  		  cgpio->regs + CDNS_GPIO_DIRECTION_MODE); -	ret = bgpio_init(&cgpio->gc, &pdev->dev, 4, -			 cgpio->regs + CDNS_GPIO_INPUT_VALUE, -			 cgpio->regs + CDNS_GPIO_OUTPUT_VALUE, -			 NULL, -			 NULL, -			 cgpio->regs + CDNS_GPIO_DIRECTION_MODE, -			 BGPIOF_READ_OUTPUT_REG_SET); +	config.dev = &pdev->dev; +	config.sz = 4; +	config.dat = cgpio->regs + CDNS_GPIO_INPUT_VALUE; +	config.set = cgpio->regs + CDNS_GPIO_OUTPUT_VALUE; +	config.dirin = cgpio->regs + CDNS_GPIO_DIRECTION_MODE; +	config.flags = BGPIOF_READ_OUTPUT_REG_SET; + +	ret = gpio_generic_chip_init(&cgpio->gen_gc, &config);  	if (ret) {  		dev_err(&pdev->dev, "Failed to register generic gpio, %d\n",  			ret);  		goto err_revert_dir;  	} -	cgpio->gc.label = dev_name(&pdev->dev); -	cgpio->gc.ngpio = num_gpios; -	cgpio->gc.parent = &pdev->dev; -	cgpio->gc.base = -1; -	cgpio->gc.owner = THIS_MODULE; -	cgpio->gc.request = cdns_gpio_request; -	cgpio->gc.free = cdns_gpio_free; +	cgpio->gen_gc.gc.label = dev_name(&pdev->dev); +	cgpio->gen_gc.gc.ngpio = num_gpios; +	cgpio->gen_gc.gc.parent = &pdev->dev; +	cgpio->gen_gc.gc.base = -1; +	cgpio->gen_gc.gc.owner = THIS_MODULE; +	cgpio->gen_gc.gc.request = cdns_gpio_request; +	cgpio->gen_gc.gc.free = cdns_gpio_free;  	clk = devm_clk_get_enabled(&pdev->dev, NULL);  	if (IS_ERR(clk)) { @@ -218,7 +213,7 @@ static int cdns_gpio_probe(struct platform_device *pdev)  	if (irq >= 0) {  		struct gpio_irq_chip *girq; -		girq = &cgpio->gc.irq; +		girq = &cgpio->gen_gc.gc.irq;  		gpio_irq_chip_set_chip(girq, &cdns_gpio_irqchip);  		girq->parent_handler = cdns_gpio_irq_handler;  		girq->num_parents = 1; @@ -234,7 +229,7 @@ static int cdns_gpio_probe(struct platform_device *pdev)  		girq->handler = handle_level_irq;  	} -	ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio); +	ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gen_gc.gc, cgpio);  	if (ret < 0) {  		dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);  		goto err_revert_dir; diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c index 1495bec62456..0efa1b61001a 100644 --- a/drivers/gpio/gpio-cgbc.c +++ b/drivers/gpio/gpio-cgbc.c @@ -171,7 +171,7 @@ static int cgbc_gpio_probe(struct platform_device *pdev)  	chip->direction_output = cgbc_gpio_direction_output;  	chip->get_direction = cgbc_gpio_get_direction;  	chip->get = cgbc_gpio_get; -	chip->set_rv = cgbc_gpio_set; +	chip->set = cgbc_gpio_set;  	chip->ngpio = CGBC_GPIO_NGPIO;  	ret = devm_mutex_init(dev, &gpio->lock); diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index d69a24dd4828..24ff2347d599 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -8,13 +8,15 @@  #include <linux/err.h>  #include <linux/module.h>  #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h>  #include <linux/platform_device.h>  static int clps711x_gpio_probe(struct platform_device *pdev)  { +	struct gpio_generic_chip_config config = { };  	struct device_node *np = pdev->dev.of_node; +	struct gpio_generic_chip *gen_gc;  	void __iomem *dat, *dir; -	struct gpio_chip *gc;  	int err, id;  	if (!np) @@ -24,8 +26,8 @@ static int clps711x_gpio_probe(struct platform_device *pdev)  	if ((id < 0) || (id > 4))  		return -ENODEV; -	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); -	if (!gc) +	gen_gc = devm_kzalloc(&pdev->dev, sizeof(*gen_gc), GFP_KERNEL); +	if (!gen_gc)  		return -ENOMEM;  	dat = devm_platform_ioremap_resource(pdev, 0); @@ -36,35 +38,37 @@ static int clps711x_gpio_probe(struct platform_device *pdev)  	if (IS_ERR(dir))  		return PTR_ERR(dir); +	config.dev = &pdev->dev; +	config.sz = 1; +	config.dat = dat; +  	switch (id) {  	case 3:  		/* PORTD is inverted logic for direction register */ -		err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL, -				 NULL, dir, 0); +		config.dirin = dir;  		break;  	default: -		err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL, -				 dir, NULL, 0); +		config.dirout = dir;  		break;  	} +	err = gpio_generic_chip_init(gen_gc, &config);  	if (err)  		return err;  	switch (id) {  	case 4:  		/* PORTE is 3 lines only */ -		gc->ngpio = 3; +		gen_gc->gc.ngpio = 3;  		break;  	default:  		break;  	} -	gc->base = -1; -	gc->owner = THIS_MODULE; -	platform_set_drvdata(pdev, gc); +	gen_gc->gc.base = -1; +	gen_gc->gc.owner = THIS_MODULE; -	return devm_gpiochip_add_data(&pdev->dev, gc, NULL); +	return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL);  }  static const struct of_device_id clps711x_gpio_ids[] = { diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index 8b49f02c7896..f8ea961fa1de 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -167,7 +167,7 @@ static int creg_gpio_probe(struct platform_device *pdev)  	hcg->gc.label = dev_name(dev);  	hcg->gc.base = -1;  	hcg->gc.ngpio = ngpios; -	hcg->gc.set_rv = creg_gpio_set; +	hcg->gc.set = creg_gpio_set;  	hcg->gc.direction_output = creg_gpio_dir_out;  	ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg); diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c index 53cd5ff6247b..435483826c6e 100644 --- a/drivers/gpio/gpio-cros-ec.c +++ b/drivers/gpio/gpio-cros-ec.c @@ -188,7 +188,7 @@ static int cros_ec_gpio_probe(struct platform_device *pdev)  	gc->can_sleep = true;  	gc->label = dev_name(dev);  	gc->base = -1; -	gc->set_rv = cros_ec_gpio_set; +	gc->set = cros_ec_gpio_set;  	gc->get = cros_ec_gpio_get;  	gc->get_direction = cros_ec_gpio_get_direction; diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 8db7cca3a060..0fb5c06d0886 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -349,7 +349,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)  	cg->chip.direction_input = crystalcove_gpio_dir_in;  	cg->chip.direction_output = crystalcove_gpio_dir_out;  	cg->chip.get = crystalcove_gpio_get; -	cg->chip.set_rv = crystalcove_gpio_set; +	cg->chip.set = crystalcove_gpio_set;  	cg->chip.base = -1;  	cg->chip.ngpio = CRYSTALCOVE_VGPIO_NUM;  	cg->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c index 143d1f4173a6..8affe4e9f90e 100644 --- a/drivers/gpio/gpio-cs5535.c +++ b/drivers/gpio/gpio-cs5535.c @@ -296,7 +296,7 @@ static struct cs5535_gpio_chip cs5535_gpio_chip = {  		.request = chip_gpio_request,  		.get = chip_gpio_get, -		.set_rv = chip_gpio_set, +		.set = chip_gpio_set,  		.direction_input = chip_direction_input,  		.direction_output = chip_direction_output, diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c index 6482c5b267db..495f0ee58505 100644 --- a/drivers/gpio/gpio-da9052.c +++ b/drivers/gpio/gpio-da9052.c @@ -172,7 +172,7 @@ static const struct gpio_chip reference_gp = {  	.label = "da9052-gpio",  	.owner = THIS_MODULE,  	.get = da9052_gpio_get, -	.set_rv = da9052_gpio_set, +	.set = da9052_gpio_set,  	.direction_input = da9052_gpio_direction_input,  	.direction_output = da9052_gpio_direction_output,  	.to_irq = da9052_gpio_to_irq, diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c index 3d9d0c700100..a09bd6eb93cf 100644 --- a/drivers/gpio/gpio-da9055.c +++ b/drivers/gpio/gpio-da9055.c @@ -116,7 +116,7 @@ static const struct gpio_chip reference_gp = {  	.label = "da9055-gpio",  	.owner = THIS_MODULE,  	.get = da9055_gpio_get, -	.set_rv = da9055_gpio_set, +	.set = da9055_gpio_set,  	.direction_input = da9055_gpio_direction_input,  	.direction_output = da9055_gpio_direction_output,  	.to_irq = da9055_gpio_to_irq, diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 63fc7888c1d4..538f27209ce7 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -68,15 +68,6 @@ static inline u32 __gpio_mask(unsigned gpio)  	return 1 << (gpio % 32);  } -static inline struct davinci_gpio_regs __iomem *irq2regs(struct irq_data *d) -{ -	struct davinci_gpio_regs __iomem *g; - -	g = (__force struct davinci_gpio_regs __iomem *)irq_data_get_irq_chip_data(d); - -	return g; -} -  static int davinci_gpio_irq_setup(struct platform_device *pdev);  /*--------------------------------------------------------------------------*/ @@ -211,7 +202,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)  	chips->chip.direction_input = davinci_direction_in;  	chips->chip.get = davinci_gpio_get;  	chips->chip.direction_output = davinci_direction_out; -	chips->chip.set_rv = davinci_gpio_set; +	chips->chip.set = davinci_gpio_set;  	chips->chip.ngpio = ngpio;  	chips->chip.base = -1; @@ -255,19 +246,27 @@ static int davinci_gpio_probe(struct platform_device *pdev)  static void gpio_irq_mask(struct irq_data *d)  { -	struct davinci_gpio_regs __iomem *g = irq2regs(d); +	struct davinci_gpio_controller *chips = irq_data_get_irq_chip_data(d); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); +	struct davinci_gpio_regs __iomem *g = chips->regs[hwirq / 32];  	uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d);  	writel_relaxed(mask, &g->clr_falling);  	writel_relaxed(mask, &g->clr_rising); + +	gpiochip_disable_irq(&chips->chip, hwirq);  }  static void gpio_irq_unmask(struct irq_data *d)  { -	struct davinci_gpio_regs __iomem *g = irq2regs(d); +	struct davinci_gpio_controller *chips = irq_data_get_irq_chip_data(d); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); +	struct davinci_gpio_regs __iomem *g = chips->regs[hwirq / 32];  	uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d);  	unsigned status = irqd_get_trigger_type(d); +	gpiochip_enable_irq(&chips->chip, hwirq); +  	status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;  	if (!status)  		status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING; @@ -286,12 +285,13 @@ static int gpio_irq_type(struct irq_data *d, unsigned trigger)  	return 0;  } -static struct irq_chip gpio_irqchip = { +static const struct irq_chip gpio_irqchip = {  	.name		= "GPIO",  	.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, +	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS,  };  static void gpio_irq_handler(struct irq_desc *desc) @@ -399,12 +399,11 @@ davinci_gpio_irq_map(struct irq_domain *d, unsigned int irq,  {  	struct davinci_gpio_controller *chips =  				(struct davinci_gpio_controller *)d->host_data; -	struct davinci_gpio_regs __iomem *g = chips->regs[hw / 32];  	irq_set_chip_and_handler_name(irq, &gpio_irqchip, handle_simple_irq,  				"davinci_gpio");  	irq_set_irq_type(irq, IRQ_TYPE_NONE); -	irq_set_chip_data(irq, (__force void *)g); +	irq_set_chip_data(irq, (__force void *)chips);  	irq_set_handler_data(irq, (void *)(uintptr_t)__gpio_mask(hw));  	return 0; @@ -479,9 +478,8 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)  			return irq;  		} -		irq_domain = irq_domain_add_legacy(dev->of_node, ngpio, irq, 0, -							&davinci_gpio_irq_ops, -							chips); +		irq_domain = irq_domain_create_legacy(dev_fwnode(dev), ngpio, irq, 0, +						      &davinci_gpio_irq_ops, chips);  		if (!irq_domain) {  			dev_err(dev, "Couldn't register an IRQ domain\n");  			return -ENODEV; diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 596da59d4b13..4670ffd7ea7f 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -220,11 +220,12 @@ static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return dln2_gpio_pin_get_out_val(dln2, offset);  } -static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int dln2_gpio_set(struct gpio_chip *chip, unsigned int offset, +			 int value)  {  	struct dln2_gpio *dln2 = gpiochip_get_data(chip); -	dln2_gpio_pin_set_out_val(dln2, offset, value); +	return dln2_gpio_pin_set_out_val(dln2, offset, value);  }  static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/gpio/gpio-ds4520.c b/drivers/gpio/gpio-ds4520.c index 1903deaef3e9..f52ecae382a4 100644 --- a/drivers/gpio/gpio-ds4520.c +++ b/drivers/gpio/gpio-ds4520.c @@ -25,7 +25,6 @@ static int ds4520_gpio_probe(struct i2c_client *client)  	struct gpio_regmap_config config = { };  	struct device *dev = &client->dev;  	struct regmap *regmap; -	u32 ngpio;  	u32 base;  	int ret; @@ -33,10 +32,6 @@ static int ds4520_gpio_probe(struct i2c_client *client)  	if (ret)  		return dev_err_probe(dev, ret, "Missing 'reg' property.\n"); -	ret = device_property_read_u32(dev, "ngpios", &ngpio); -	if (ret) -		return dev_err_probe(dev, ret, "Missing 'ngpios' property.\n"); -  	regmap = devm_regmap_init_i2c(client, &ds4520_regmap_config);  	if (IS_ERR(regmap))  		return dev_err_probe(dev, PTR_ERR(regmap), @@ -44,7 +39,6 @@ static int ds4520_gpio_probe(struct i2c_client *client)  	config.regmap = regmap;  	config.parent = dev; -	config.ngpio = ngpio;  	config.reg_dat_base = base + DS4520_IO_STATUS0;  	config.reg_set_base = base + DS4520_PULLUP0; diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index d4bf8d187e16..50fafeda8d7e 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -203,9 +203,10 @@ static int sprd_eic_direction_input(struct gpio_chip *chip, unsigned int offset)  	return 0;  } -static void sprd_eic_set(struct gpio_chip *chip, unsigned int offset, int value) +static int sprd_eic_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	/* EICs are always input, nothing need to do here. */ +	return 0;  }  static int sprd_eic_set_debounce(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 6c862c572322..a214b0672726 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -204,13 +204,15 @@ static void __em_gio_set(struct gpio_chip *chip, unsigned int reg,  		     (BIT(shift + 16)) | (value << shift));  } -static void em_gio_set(struct gpio_chip *chip, unsigned offset, int value) +static int em_gio_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	/* output is split into two registers */  	if (offset < 16)  		__em_gio_set(chip, GIO_OL, offset, value);  	else  		__em_gio_set(chip, GIO_OH, offset - 16, value); + +	return 0;  }  static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset, @@ -323,8 +325,8 @@ static int em_gio_probe(struct platform_device *pdev)  	irq_chip->irq_release_resources = em_gio_irq_relres;  	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; -	p->irq_domain = irq_domain_add_simple(dev->of_node, ngpios, 0, -					      &em_gio_irq_domain_ops, p); +	p->irq_domain = irq_domain_create_simple(dev_fwnode(dev), ngpios, 0, +						 &em_gio_irq_domain_ops, p);  	if (!p->irq_domain) {  		dev_err(dev, "cannot initialize irq domain\n");  		return -ENXIO; diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c index 69834db2c1cf..cf47afc578a9 100644 --- a/drivers/gpio/gpio-en7523.c +++ b/drivers/gpio/gpio-en7523.c @@ -4,6 +4,7 @@  #include <linux/io.h>  #include <linux/bits.h>  #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h>  #include <linux/mod_devicetable.h>  #include <linux/module.h>  #include <linux/platform_device.h> @@ -13,28 +14,23 @@  /**   * struct airoha_gpio_ctrl - Airoha GPIO driver data - * @gc: Associated gpio_chip instance. + * @gen_gc: Associated gpio_generic_chip instance.   * @data: The data register.   * @dir: [0] The direction register for the lower 16 pins.   * [1]: The direction register for the higher 16 pins.   * @output: The output enable register.   */  struct airoha_gpio_ctrl { -	struct gpio_chip gc; +	struct gpio_generic_chip gen_gc;  	void __iomem *data;  	void __iomem *dir[2];  	void __iomem *output;  }; -static struct airoha_gpio_ctrl *gc_to_ctrl(struct gpio_chip *gc) -{ -	return container_of(gc, struct airoha_gpio_ctrl, gc); -} -  static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio,  			  int val, int out)  { -	struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); +	struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc);  	u32 dir = ioread32(ctrl->dir[gpio / 16]);  	u32 output = ioread32(ctrl->output);  	u32 mask = BIT((gpio % 16) * 2); @@ -50,7 +46,7 @@ static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio,  	iowrite32(dir, ctrl->dir[gpio / 16]);  	if (out) -		gc->set(gc, gpio, val); +		gpio_generic_chip_set(&ctrl->gen_gc, gpio, val);  	iowrite32(output, ctrl->output); @@ -70,7 +66,7 @@ static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio)  static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio)  { -	struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); +	struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc);  	u32 dir = ioread32(ctrl->dir[gpio / 16]);  	u32 mask = BIT((gpio % 16) * 2); @@ -79,6 +75,7 @@ static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio)  static int airoha_gpio_probe(struct platform_device *pdev)  { +	struct gpio_generic_chip_config config = { };  	struct device *dev = &pdev->dev;  	struct airoha_gpio_ctrl *ctrl;  	int err; @@ -103,18 +100,21 @@ static int airoha_gpio_probe(struct platform_device *pdev)  	if (IS_ERR(ctrl->output))  		return PTR_ERR(ctrl->output); -	err = bgpio_init(&ctrl->gc, dev, 4, ctrl->data, NULL, -			 NULL, NULL, NULL, 0); +	config.dev = dev; +	config.sz = 4; +	config.dat = ctrl->data; + +	err = gpio_generic_chip_init(&ctrl->gen_gc, &config);  	if (err)  		return dev_err_probe(dev, err, "unable to init generic GPIO"); -	ctrl->gc.ngpio = AIROHA_GPIO_MAX; -	ctrl->gc.owner = THIS_MODULE; -	ctrl->gc.direction_output = airoha_dir_out; -	ctrl->gc.direction_input = airoha_dir_in; -	ctrl->gc.get_direction = airoha_get_dir; +	ctrl->gen_gc.gc.ngpio = AIROHA_GPIO_MAX; +	ctrl->gen_gc.gc.owner = THIS_MODULE; +	ctrl->gen_gc.gc.direction_output = airoha_dir_out; +	ctrl->gen_gc.gc.direction_input = airoha_dir_in; +	ctrl->gen_gc.gc.get_direction = airoha_get_dir; -	return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); +	return devm_gpiochip_add_data(dev, &ctrl->gen_gc.gc, ctrl);  }  static const struct of_device_id airoha_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index d5909a4f0433..9053662f1817 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -93,8 +93,8 @@ static int exar_get_value(struct gpio_chip *chip, unsigned int offset)  	return !!(regmap_test_bits(exar_gpio->regmap, addr, BIT(bit)));  } -static void exar_set_value(struct gpio_chip *chip, unsigned int offset, -			   int value) +static int exar_set_value(struct gpio_chip *chip, unsigned int offset, +			  int value)  {  	struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);  	unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset); @@ -105,7 +105,7 @@ static void exar_set_value(struct gpio_chip *chip, unsigned int offset,  	 * 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); +	return regmap_write_bits(exar_gpio->regmap, addr, BIT(bit), bit_value);  }  static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, @@ -114,11 +114,13 @@ static int exar_direction_output(struct gpio_chip *chip, unsigned int offset,  	struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);  	unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset);  	unsigned int bit = exar_offset_to_bit(exar_gpio, offset); +	int ret; -	exar_set_value(chip, offset, value); -	regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit)); +	ret = exar_set_value(chip, offset, value); +	if (ret) +		return ret; -	return 0; +	return regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit));  }  static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 3875fd940ccb..4d5b927ad70f 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -159,7 +159,8 @@ static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);  static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);  static int f7188x_gpio_direction_out(struct gpio_chip *chip,  				     unsigned offset, int value); -static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value); +static int f7188x_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value);  static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset,  				  unsigned long config); @@ -391,7 +392,8 @@ static int f7188x_gpio_direction_out(struct gpio_chip *chip,  	return 0;  } -static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int f7188x_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	int err;  	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); @@ -400,7 +402,8 @@ static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)  	err = superio_enter(sio->addr);  	if (err) -		return; +		return err; +  	superio_select(sio->addr, sio->device);  	data_out = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase)); @@ -411,6 +414,8 @@ static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)  	superio_outb(sio->addr, f7188x_gpio_data_out(bank->regbase), data_out);  	superio_exit(sio->addr); + +	return 0;  }  static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/gpio/gpio-graniterapids.c b/drivers/gpio/gpio-graniterapids.c index ad6a045fd3d2..121bf29a27f5 100644 --- a/drivers/gpio/gpio-graniterapids.c +++ b/drivers/gpio/gpio-graniterapids.c @@ -116,7 +116,7 @@ static int gnr_gpio_get(struct gpio_chip *gc, unsigned int gpio)  	return !!(dw & GNR_CFG_DW_RXSTATE);  } -static void gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +static int gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)  {  	u32 clear = 0;  	u32 set = 0; @@ -126,7 +126,7 @@ static void gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)  	else  		clear = GNR_CFG_DW_TXSTATE; -	gnr_gpio_configure_line(gc, gpio, clear, set); +	return gnr_gpio_configure_line(gc, gpio, clear, set);  }  static int gnr_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 30a0522ae735..f3f8bab62f94 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -170,6 +170,8 @@ static void grgpio_irq_mask(struct irq_data *d)  	grgpio_set_imask(priv, offset, 0);  	raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); + +	gpiochip_disable_irq(&priv->gc, d->hwirq);  }  static void grgpio_irq_unmask(struct irq_data *d) @@ -178,6 +180,7 @@ static void grgpio_irq_unmask(struct irq_data *d)  	int offset = d->hwirq;  	unsigned long flags; +	gpiochip_enable_irq(&priv->gc, d->hwirq);  	raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags);  	grgpio_set_imask(priv, offset, 1); @@ -185,11 +188,13 @@ static void grgpio_irq_unmask(struct irq_data *d)  	raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);  } -static struct irq_chip grgpio_irq_chip = { +static const struct irq_chip grgpio_irq_chip = {  	.name			= "grgpio",  	.irq_mask		= grgpio_irq_mask,  	.irq_unmask		= grgpio_irq_unmask,  	.irq_set_type		= grgpio_irq_set_type, +	.flags = IRQCHIP_IMMUTABLE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS,  };  static irqreturn_t grgpio_irq_handler(int irq, void *dev) @@ -397,9 +402,8 @@ static int grgpio_probe(struct platform_device *ofdev)  			return -EINVAL;  		} -		priv->domain = irq_domain_add_linear(np, gc->ngpio, -						     &grgpio_irq_domain_ops, -						     priv); +		priv->domain = irq_domain_create_linear(dev_fwnode(&ofdev->dev), gc->ngpio, +							&grgpio_irq_domain_ops, priv);  		if (!priv->domain) {  			dev_err(dev, "Could not add irq domain\n");  			return -EINVAL; diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 7e29a2d8de1a..2e5d97b7363f 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -62,9 +62,9 @@ static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value)  	return i2c_smbus_write_byte(gw->client, gw->out);  } -static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value) +static int gw_pld_set8(struct gpio_chip *gc, unsigned int offset, int value)  { -	gw_pld_output8(gc, offset, value); +	return gw_pld_output8(gc, offset, value);  }  static int gw_pld_probe(struct i2c_client *client) diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index a40bd56673fe..2eaed83214d8 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -170,7 +170,7 @@ static int egpio_direction_input(struct gpio_chip *chip, unsigned offset)   * Output pins   */ -static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int egpio_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	unsigned long     flag;  	struct egpio_chip *egpio; @@ -198,6 +198,8 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)  		egpio->cached_values &= ~(1 << offset);  	egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg);  	spin_unlock_irqrestore(&ei->lock, flag); + +	return 0;  }  static int egpio_direction_output(struct gpio_chip *chip, @@ -206,12 +208,10 @@ static int egpio_direction_output(struct gpio_chip *chip,  	struct egpio_chip *egpio;  	egpio = gpiochip_get_data(chip); -	if (test_bit(offset, &egpio->is_out)) { -		egpio_set(chip, offset, value); -		return 0; -	} else { -		return -EINVAL; -	} +	if (test_bit(offset, &egpio->is_out)) +		return egpio_set(chip, offset, value); + +	return -EINVAL;  }  static int egpio_get_direction(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 0be9285efebc..1802c9116ffe 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -175,12 +175,16 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr)  static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr,  					int val)  { +	int ret; +  	/* Disable blink hardware which is available for GPIOs from 0 to 31. */  	if (nr < 32 && ichx_priv.desc->have_blink)  		ichx_write_bit(GPO_BLINK, nr, 0, 0);  	/* Set GPIO output value. */ -	ichx_write_bit(GPIO_LVL, nr, val, 0); +	ret = ichx_write_bit(GPIO_LVL, nr, val, 0); +	if (ret) +		return ret;  	/*  	 * Try setting pin as an output and verify it worked since many pins @@ -252,9 +256,9 @@ static int ich6_gpio_request(struct gpio_chip *chip, unsigned int nr)  	return ichx_gpio_request(chip, nr);  } -static void ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val) +static int ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val)  { -	ichx_write_bit(GPIO_LVL, nr, val, 0); +	return ichx_write_bit(GPIO_LVL, nr, val, 0);  }  static void ichx_gpiolib_setup(struct gpio_chip *chip) diff --git a/drivers/gpio/gpio-idt3243x.c b/drivers/gpio/gpio-idt3243x.c index 00f547d26254..535f25514455 100644 --- a/drivers/gpio/gpio-idt3243x.c +++ b/drivers/gpio/gpio-idt3243x.c @@ -37,7 +37,7 @@ static void idt_gpio_dispatch(struct irq_desc *desc)  	pending = readl(ctrl->pic + IDT_PIC_IRQ_PEND);  	pending &= ~ctrl->mask_cache;  	for_each_set_bit(bit, &pending, gc->ngpio) { -		virq = irq_linear_revmap(gc->irq.domain, bit); +		virq = irq_find_mapping(gc->irq.domain, bit);  		if (virq)  			generic_handle_irq(virq);  	} diff --git a/drivers/gpio/gpio-imx-scu.c b/drivers/gpio/gpio-imx-scu.c index 13baf465aedf..0a75afecf9f8 100644 --- a/drivers/gpio/gpio-imx-scu.c +++ b/drivers/gpio/gpio-imx-scu.c @@ -6,8 +6,10 @@   * to control the PIN resources on SCU domain.   */ +#include <linux/cleanup.h>  #include <linux/kernel.h>  #include <linux/module.h> +#include <linux/mutex.h>  #include <linux/gpio/driver.h>  #include <linux/platform_device.h>  #include <linux/firmware/imx/svc/rm.h> @@ -37,16 +39,11 @@ static int imx_scu_gpio_get(struct gpio_chip *chip, unsigned int offset)  	int level;  	int err; -	if (offset >= chip->ngpio) -		return -EINVAL; - -	mutex_lock(&priv->lock); - -	/* to read PIN state via scu api */ -	err = imx_sc_misc_get_control(priv->handle, -			scu_rsrc_arr[offset], 0, &level); -	mutex_unlock(&priv->lock); - +	scoped_guard(mutex, &priv->lock) { +		/* to read PIN state via scu api */ +		err = imx_sc_misc_get_control(priv->handle, +					      scu_rsrc_arr[offset], 0, &level); +	}  	if (err) {  		dev_err(priv->dev, "SCU get failed: %d\n", err);  		return err; @@ -55,31 +52,26 @@ static int imx_scu_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return level;  } -static void imx_scu_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int imx_scu_gpio_set(struct gpio_chip *chip, unsigned int offset, +			    int value)  {  	struct scu_gpio_priv *priv = gpiochip_get_data(chip);  	int err; -	if (offset >= chip->ngpio) -		return; - -	mutex_lock(&priv->lock); - -	/* to set PIN output level via scu api */ -	err = imx_sc_misc_set_control(priv->handle, -			scu_rsrc_arr[offset], 0, value); -	mutex_unlock(&priv->lock); - +	scoped_guard(mutex, &priv->lock) { +		/* to set PIN output level via scu api */ +		err = imx_sc_misc_set_control(priv->handle, +					      scu_rsrc_arr[offset], 0, value); +	}  	if (err)  		dev_err(priv->dev, "SCU set (%d) failed: %d\n",  				scu_rsrc_arr[offset], err); + +	return err;  }  static int imx_scu_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)  { -	if (offset >= chip->ngpio) -		return -EINVAL; -  	return GPIO_LINE_DIRECTION_OUT;  } @@ -99,7 +91,10 @@ static int imx_scu_gpio_probe(struct platform_device *pdev)  		return ret;  	priv->dev = dev; -	mutex_init(&priv->lock); + +	ret = devm_mutex_init(&pdev->dev, &priv->lock); +	if (ret) +		return ret;  	gc = &priv->chip;  	gc->base = -1; diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index f332341fd4c8..5d677bcfccf2 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -213,8 +213,7 @@ exit:  	return rc;  } -static void it87_gpio_set(struct gpio_chip *chip, -			  unsigned gpio_num, int val) +static int it87_gpio_set(struct gpio_chip *chip, unsigned int gpio_num, int val)  {  	u8 mask, curr_vals;  	u16 reg; @@ -228,6 +227,8 @@ static void it87_gpio_set(struct gpio_chip *chip,  		outb(curr_vals | mask, reg);  	else  		outb(curr_vals & ~mask, reg); + +	return 0;  }  static int it87_gpio_direction_out(struct gpio_chip *chip, @@ -249,7 +250,9 @@ static int it87_gpio_direction_out(struct gpio_chip *chip,  	/* set the output enable bit */  	superio_set_mask(mask, group + it87_gpio->output_base); -	it87_gpio_set(chip, gpio_num, val); +	rc = it87_gpio_set(chip, gpio_num, val); +	if (rc) +		goto exit;  	superio_exit(); diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c index cdf50e4ea165..b0c4a3346e7d 100644 --- a/drivers/gpio/gpio-janz-ttl.c +++ b/drivers/gpio/gpio-janz-ttl.c @@ -76,7 +76,7 @@ static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)  	return !!ret;  } -static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value) +static int ttl_set_value(struct gpio_chip *gpio, unsigned int offset, int value)  {  	struct ttl_module *mod = dev_get_drvdata(gpio->parent);  	void __iomem *port; @@ -103,6 +103,8 @@ static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)  	iowrite16be(*shadow, port);  	spin_unlock(&mod->lock); + +	return 0;  }  static void ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val) diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c index 4ea15f08e0f4..923aad3ab4d4 100644 --- a/drivers/gpio/gpio-kempld.c +++ b/drivers/gpio/gpio-kempld.c @@ -63,7 +63,8 @@ static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset)  	return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset), offset);  } -static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct kempld_gpio_data *gpio = gpiochip_get_data(chip);  	struct kempld_device_data *pld = gpio->pld; @@ -71,6 +72,8 @@ static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value)  	kempld_get_mutex(pld);  	kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);  	kempld_release_mutex(pld); + +	return 0;  }  static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index 3d0ff09284fb..c64aaa896766 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -166,11 +166,11 @@ static int gpio_latch_probe(struct platform_device *pdev)  	if (gpio_latch_can_sleep(priv, n_latches)) {  		priv->gc.can_sleep = true; -		priv->gc.set_rv = gpio_latch_set_can_sleep; +		priv->gc.set = gpio_latch_set_can_sleep;  		mutex_init(&priv->mutex);  	} else {  		priv->gc.can_sleep = false; -		priv->gc.set_rv = gpio_latch_set; +		priv->gc.set = gpio_latch_set;  		spin_lock_init(&priv->spinlock);  	} diff --git a/drivers/gpio/gpio-ljca.c b/drivers/gpio/gpio-ljca.c index 817ecb12d550..3b4f8830c741 100644 --- a/drivers/gpio/gpio-ljca.c +++ b/drivers/gpio/gpio-ljca.c @@ -144,8 +144,8 @@ static int ljca_gpio_get_value(struct gpio_chip *chip, unsigned int offset)  	return ljca_gpio_read(ljca_gpio, offset);  } -static void ljca_gpio_set_value(struct gpio_chip *chip, unsigned int offset, -				int val) +static int ljca_gpio_set_value(struct gpio_chip *chip, unsigned int offset, +			       int val)  {  	struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);  	int ret; @@ -155,6 +155,8 @@ static void ljca_gpio_set_value(struct gpio_chip *chip, unsigned int offset,  		dev_err(chip->parent,  			"set value failed offset: %u val: %d ret: %d\n",  			offset, val, ret); + +	return ret;  }  static int ljca_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -183,7 +185,10 @@ static int ljca_gpio_direction_output(struct gpio_chip *chip,  	if (ret)  		return ret; -	ljca_gpio_set_value(chip, offset, val); +	ret = ljca_gpio_set_value(chip, offset, val); +	if (ret) +		return ret; +  	set_bit(offset, ljca_gpio->output_enabled);  	return 0; diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c index 05d62011f335..cb9dbcc290ad 100644 --- a/drivers/gpio/gpio-logicvc.c +++ b/drivers/gpio/gpio-logicvc.c @@ -61,23 +61,22 @@ static int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset)  	return !!(value & bit);  } -static void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int logicvc_gpio_set(struct gpio_chip *chip, unsigned int offset, +			    int value)  {  	struct logicvc_gpio *logicvc = gpiochip_get_data(chip);  	unsigned int reg, bit;  	logicvc_gpio_offset(logicvc, offset, ®, &bit); -	regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0); +	return regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0);  }  static int logicvc_gpio_direction_output(struct gpio_chip *chip,  					 unsigned offset, int value)  {  	/* Pins are always configured as output, so just set the value. */ -	logicvc_gpio_set(chip, offset, value); - -	return 0; +	return logicvc_gpio_set(chip, offset, value);  }  static struct regmap_config logicvc_gpio_regmap_config = { diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index a9a93036f08f..818c606fbc51 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -105,7 +105,7 @@ static int loongson_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)  	return GPIO_LINE_DIRECTION_OUT;  } -static void loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +static int loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)  {  	unsigned long flags;  	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip); @@ -113,6 +113,8 @@ static void loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int valu  	spin_lock_irqsave(&lgpio->lock, flags);  	loongson_commit_level(lgpio, pin, value);  	spin_unlock_irqrestore(&lgpio->lock, flags); + +	return 0;  }  static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) @@ -220,6 +222,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0 = {  	.conf_offset = 0x0,  	.in_offset = 0xc,  	.out_offset = 0x8, +	.inten_offset = 0x14,  };  static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { @@ -228,6 +231,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = {  	.conf_offset = 0x0,  	.in_offset = 0x20,  	.out_offset = 0x10, +	.inten_offset = 0x30,  };  static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = { @@ -244,6 +248,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls3a5000_data = {  	.conf_offset = 0x0,  	.in_offset = 0xc,  	.out_offset = 0x8, +	.inten_offset = 0x14,  };  static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = { @@ -252,6 +257,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = {  	.conf_offset = 0x800,  	.in_offset = 0xa00,  	.out_offset = 0x900, +	.inten_offset = 0xb00,  };  /* LS7A2000 chipset GPIO */ @@ -261,12 +267,13 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data0 = {  	.conf_offset = 0x800,  	.in_offset = 0xa00,  	.out_offset = 0x900, +	.inten_offset = 0xb00,  };  /* LS7A2000 ACPI GPIO */  static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data1 = {  	.label = "ls7a2000_gpio", -	.mode = BYTE_CTRL_MODE, +	.mode = BIT_CTRL_MODE,  	.conf_offset = 0x4,  	.in_offset = 0x8,  	.out_offset = 0x0, @@ -279,6 +286,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls3a6000_data = {  	.conf_offset = 0x0,  	.in_offset = 0xc,  	.out_offset = 0x8, +	.inten_offset = 0x14,  };  static const struct of_device_id loongson_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c index a42145873cc9..f3e0559f969d 100644 --- a/drivers/gpio/gpio-loongson.c +++ b/drivers/gpio/gpio-loongson.c @@ -48,8 +48,8 @@ static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio)  	return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET));  } -static void loongson_gpio_set_value(struct gpio_chip *chip, -		unsigned gpio, int value) +static int loongson_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, +				   int value)  {  	u32 val; @@ -61,6 +61,8 @@ static void loongson_gpio_set_value(struct gpio_chip *chip,  		val &= ~BIT(gpio);  	LOONGSON_GPIODATA = val;  	spin_unlock(&gpio_lock); + +	return 0;  }  static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 8e58242f5123..e8e00daff7df 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -147,7 +147,8 @@ static int lp3943_gpio_get(struct gpio_chip *chip, unsigned int offset)  		return lp3943_get_gpio_out_status(lp3943_gpio, chip, offset);  } -static void lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip);  	u8 data; @@ -157,15 +158,19 @@ static void lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, int val  	else  		data = LP3943_GPIO_OUT_LOW; -	lp3943_gpio_set_mode(lp3943_gpio, offset, data); +	return lp3943_gpio_set_mode(lp3943_gpio, offset, data);  }  static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,  					int value)  {  	struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); +	int ret; + +	ret = lp3943_gpio_set(chip, offset, value); +	if (ret) +		return ret; -	lp3943_gpio_set(chip, offset, value);  	lp3943_gpio->input_mask &= ~BIT(offset);  	return 0; diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index 5c79ba1f229c..5376708a81bf 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -58,14 +58,14 @@ static int lp873x_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return val & BIT(offset * BITS_PER_GPO);  } -static void lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset, -			    int value) +static int lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct lp873x_gpio *gpio = gpiochip_get_data(chip); -	regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, -			   BIT(offset * BITS_PER_GPO), -			   value ? BIT(offset * BITS_PER_GPO) : 0); +	return regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, +				  BIT(offset * BITS_PER_GPO), +				  value ? BIT(offset * BITS_PER_GPO) : 0);  }  static int lp873x_gpio_request(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index d3ce027de081..0f337c1283b2 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -30,13 +30,13 @@ static int lp87565_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return !!(val & BIT(offset));  } -static void lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, -			     int value) +static int lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, +			    int value)  {  	struct lp87565_gpio *gpio = gpiochip_get_data(chip); -	regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, -			   BIT(offset), value ? BIT(offset) : 0); +	return regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, +				  BIT(offset), value ? BIT(offset) : 0);  }  static int lp87565_gpio_get_direction(struct gpio_chip *chip, @@ -69,8 +69,11 @@ static int lp87565_gpio_direction_output(struct gpio_chip *chip,  					 unsigned int offset, int value)  {  	struct lp87565_gpio *gpio = gpiochip_get_data(chip); +	int ret; -	lp87565_gpio_set(chip, offset, value); +	ret = lp87565_gpio_set(chip, offset, value); +	if (ret) +		return ret;  	return regmap_update_bits(gpio->map,  				  LP87565_REG_GPIO_CONFIG, diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index 2cf9fb4637a2..37a2342eb2e6 100644 --- a/drivers/gpio/gpio-lpc18xx.c +++ b/drivers/gpio/gpio-lpc18xx.c @@ -42,6 +42,7 @@ struct lpc18xx_gpio_pin_ic {  	void __iomem *base;  	struct irq_domain *domain;  	struct raw_spinlock lock; +	struct gpio_chip *gpio;  };  struct lpc18xx_gpio_chip { @@ -74,6 +75,7 @@ static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)  {  	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;  	u32 type = irqd_get_trigger_type(d); +	irq_hw_number_t hwirq = irqd_to_hwirq(d);  	raw_spin_lock(&ic->lock); @@ -88,12 +90,17 @@ static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)  	raw_spin_unlock(&ic->lock);  	irq_chip_mask_parent(d); + +	gpiochip_disable_irq(ic->gpio, hwirq);  }  static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d)  {  	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;  	u32 type = irqd_get_trigger_type(d); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); + +	gpiochip_enable_irq(ic->gpio, hwirq);  	raw_spin_lock(&ic->lock); @@ -149,13 +156,14 @@ static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type)  	return 0;  } -static struct irq_chip lpc18xx_gpio_pin_ic = { +static const struct irq_chip lpc18xx_gpio_pin_ic = {  	.name		= "LPC18xx GPIO pin",  	.irq_mask	= lpc18xx_gpio_pin_ic_mask,  	.irq_unmask	= lpc18xx_gpio_pin_ic_unmask,  	.irq_eoi	= lpc18xx_gpio_pin_ic_eoi,  	.irq_set_type	= lpc18xx_gpio_pin_ic_set_type, -	.flags		= IRQCHIP_SET_TYPE_MASKED, +	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_SET_TYPE_MASKED, +	GPIOCHIP_IRQ_RESOURCE_HELPERS,  };  static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain, @@ -240,17 +248,16 @@ static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc)  	raw_spin_lock_init(&ic->lock); -	ic->domain = irq_domain_add_hierarchy(parent_domain, 0, -					      NR_LPC18XX_GPIO_PIN_IC_IRQS, -					      dev->of_node, -					      &lpc18xx_gpio_pin_ic_domain_ops, -					      ic); +	ic->domain = irq_domain_create_hierarchy(parent_domain, 0, NR_LPC18XX_GPIO_PIN_IC_IRQS, +						 dev_fwnode(dev), &lpc18xx_gpio_pin_ic_domain_ops, +						 ic);  	if (!ic->domain) {  		pr_err("unable to add irq domain\n");  		ret = -ENODEV;  		goto free_iomap;  	} +	ic->gpio = &gc->gpio;  	gc->pin_ic = ic;  	return 0; @@ -263,10 +270,14 @@ free_ic:  	return ret;  } -static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int lpc18xx_gpio_set(struct gpio_chip *chip, unsigned int offset, +			    int value)  {  	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip); +  	writeb(value ? 1 : 0, gc->base + offset); + +	return 0;  }  static int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index c097e310c9e8..37fc54fc7385 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -340,28 +340,34 @@ static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,  	return 0;  } -static void lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, unsigned pin, -	int value) +static int lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, +				       unsigned int pin, int value)  {  	struct lpc32xx_gpio_chip *group = gpiochip_get_data(chip);  	__set_gpio_level_p012(group, pin, value); + +	return 0;  } -static void lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, unsigned pin, -	int value) +static int lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, +				     unsigned int pin, int value)  {  	struct lpc32xx_gpio_chip *group = gpiochip_get_data(chip);  	__set_gpio_level_p3(group, pin, value); + +	return 0;  } -static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin, -	int value) +static int lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned int pin, +				 int value)  {  	struct lpc32xx_gpio_chip *group = gpiochip_get_data(chip);  	__set_gpo_level_p3(group, pin, value); + +	return 0;  }  static int lpc32xx_gpo_get_value(struct gpio_chip *chip, unsigned pin) diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c new file mode 100644 index 000000000000..30ef258e7655 --- /dev/null +++ b/drivers/gpio/gpio-macsmc.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC GPIO driver + * Copyright The Asahi Linux Contributors + * + * This driver implements basic SMC PMU GPIO support that can read inputs + * and write outputs. Mode changes and IRQ config are not yet implemented. + */ + +#include <linux/bitmap.h> +#include <linux/device.h> +#include <linux/gpio/driver.h> +#include <linux/mfd/core.h> +#include <linux/mfd/macsmc.h> + +#define MAX_GPIO 64 + +/* + * Commands 0-6 are, presumably, the intended API. + * Command 0xff lets you get/set the pin configuration in detail directly, + * but the bit meanings seem not to be stable between devices/PMU hardware + * versions. + * + * We're going to try to make do with the low commands for now. + * We don't implement pin mode changes at this time. + */ + +#define CMD_ACTION	(0 << 24) +#define CMD_OUTPUT	(1 << 24) +#define CMD_INPUT	(2 << 24) +#define CMD_PINMODE	(3 << 24) +#define CMD_IRQ_ENABLE	(4 << 24) +#define CMD_IRQ_ACK	(5 << 24) +#define CMD_IRQ_MODE	(6 << 24) +#define CMD_CONFIG	(0xff << 24) + +#define MODE_INPUT	0 +#define MODE_OUTPUT	1 +#define MODE_VALUE_0	0 +#define MODE_VALUE_1	2 + +#define IRQ_MODE_HIGH		0 +#define IRQ_MODE_LOW		1 +#define IRQ_MODE_RISING		2 +#define IRQ_MODE_FALLING	3 +#define IRQ_MODE_BOTH		4 + +#define CONFIG_MASK	GENMASK(23, 16) +#define CONFIG_VAL	GENMASK(7, 0) + +#define CONFIG_OUTMODE	GENMASK(7, 6) +#define CONFIG_IRQMODE	GENMASK(5, 3) +#define CONFIG_PULLDOWN	BIT(2) +#define CONFIG_PULLUP	BIT(1) +#define CONFIG_OUTVAL	BIT(0) + +/* + * Output modes seem to differ depending on the PMU in use... ? + * j274 / M1 (Sera PMU): + *   0 = input + *   1 = output + *   2 = open drain + *   3 = disable + * j314 / M1Pro (Maverick PMU): + *   0 = input + *   1 = open drain + *   2 = output + *   3 = ? + */ + +struct macsmc_gpio { +	struct device *dev; +	struct apple_smc *smc; +	struct gpio_chip gc; + +	int first_index; +}; + +static int macsmc_gpio_nr(smc_key key) +{ +	int low = hex_to_bin(key & 0xff); +	int high = hex_to_bin((key >> 8) & 0xff); + +	if (low < 0 || high < 0) +		return -1; + +	return low | (high << 4); +} + +static int macsmc_gpio_key(unsigned int offset) +{ +	return _SMC_KEY("gP\0\0") | hex_asc_hi(offset) << 8 | hex_asc_lo(offset); +} + +static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp) +{ +	struct apple_smc *smc = smcgp->smc; +	smc_key key = macsmc_gpio_key(0); +	smc_key first_key, last_key; +	int start, count, ret; + +	/* Return early if the key is out of bounds */ +	ret = apple_smc_get_key_by_index(smc, 0, &first_key); +	if (ret) +		return ret; +	if (key <= first_key) +		return -ENODEV; + +	ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key); +	if (ret) +		return ret; +	if (key > last_key) +		return -ENODEV; + +	/* Binary search to find index of first SMC key bigger or equal to key */ +	start = 0; +	count = smc->key_count; +	while (count > 1) { +		smc_key pkey; +		int pivot = start + ((count - 1) >> 1); + +		ret = apple_smc_get_key_by_index(smc, pivot, &pkey); +		if (ret < 0) +			return ret; + +		if (pkey == key) +			return pivot; + +		pivot++; + +		if (pkey < key) { +			count -= pivot - start; +			start = pivot; +		} else { +			count = pivot - start; +		} +	} + +	return start; +} + +static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ +	struct macsmc_gpio *smcgp = gpiochip_get_data(gc); +	smc_key key = macsmc_gpio_key(offset); +	u32 val; +	int ret; + +	/* First try reading the explicit pin mode register */ +	ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val); +	if (!ret) +		return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; + +	/* +	 * Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode. +	 * Fall back to reading IRQ mode, which will only succeed for inputs. +	 */ +	ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val); +	return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int macsmc_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ +	struct macsmc_gpio *smcgp = gpiochip_get_data(gc); +	smc_key key = macsmc_gpio_key(offset); +	u32 cmd, val; +	int ret; + +	ret = macsmc_gpio_get_direction(gc, offset); +	if (ret < 0) +		return ret; + +	if (ret == GPIO_LINE_DIRECTION_OUT) +		cmd = CMD_OUTPUT; +	else +		cmd = CMD_INPUT; + +	ret = apple_smc_rw_u32(smcgp->smc, key, cmd, &val); +	if (ret < 0) +		return ret; + +	return val ? 1 : 0; +} + +static int macsmc_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ +	struct macsmc_gpio *smcgp = gpiochip_get_data(gc); +	smc_key key = macsmc_gpio_key(offset); +	int ret; + +	value |= CMD_OUTPUT; +	ret = apple_smc_write_u32(smcgp->smc, key, CMD_OUTPUT | value); +	if (ret < 0) +		dev_err(smcgp->dev, "GPIO set failed %p4ch = 0x%x\n", +			&key, value); + +	return ret; +} + +static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc, +				       unsigned long *valid_mask, unsigned int ngpios) +{ +	struct macsmc_gpio *smcgp = gpiochip_get_data(gc); +	int count; +	int i; + +	count = min(smcgp->smc->key_count, MAX_GPIO); + +	bitmap_zero(valid_mask, ngpios); + +	for (i = 0; i < count; i++) { +		int ret, gpio_nr; +		smc_key key; + +		ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key); +		if (ret < 0) +			return ret; + +		if (key > SMC_KEY(gPff)) +			break; + +		gpio_nr = macsmc_gpio_nr(key); +		if (gpio_nr < 0 || gpio_nr > MAX_GPIO) { +			dev_err(smcgp->dev, "Bad GPIO key %p4ch\n", &key); +			continue; +		} + +		set_bit(gpio_nr, valid_mask); +	} + +	return 0; +} + +static int macsmc_gpio_probe(struct platform_device *pdev) +{ +	struct macsmc_gpio *smcgp; +	struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); +	smc_key key; +	int ret; + +	smcgp = devm_kzalloc(&pdev->dev, sizeof(*smcgp), GFP_KERNEL); +	if (!smcgp) +		return -ENOMEM; + +	smcgp->dev = &pdev->dev; +	smcgp->smc = smc; + +	smcgp->first_index = macsmc_gpio_find_first_gpio_index(smcgp); +	if (smcgp->first_index < 0) +		return smcgp->first_index; + +	ret = apple_smc_get_key_by_index(smc, smcgp->first_index, &key); +	if (ret < 0) +		return ret; + +	if (key > macsmc_gpio_key(MAX_GPIO - 1)) +		return -ENODEV; + +	dev_info(smcgp->dev, "First GPIO key: %p4ch\n", &key); + +	smcgp->gc.label = "macsmc-pmu-gpio"; +	smcgp->gc.owner = THIS_MODULE; +	smcgp->gc.get = macsmc_gpio_get; +	smcgp->gc.set = macsmc_gpio_set; +	smcgp->gc.get_direction = macsmc_gpio_get_direction; +	smcgp->gc.init_valid_mask = macsmc_gpio_init_valid_mask; +	smcgp->gc.can_sleep = true; +	smcgp->gc.ngpio = MAX_GPIO; +	smcgp->gc.base = -1; +	smcgp->gc.parent = &pdev->dev; + +	return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp); +} + +static const struct of_device_id macsmc_gpio_of_table[] = { +	{ .compatible = "apple,smc-gpio", }, +	{} +}; +MODULE_DEVICE_TABLE(of, macsmc_gpio_of_table); + +static struct platform_driver macsmc_gpio_driver = { +	.driver = { +		.name = "macsmc-gpio", +		.of_match_table = macsmc_gpio_of_table, +	}, +	.probe = macsmc_gpio_probe, +}; +module_platform_driver(macsmc_gpio_driver); + +MODULE_AUTHOR("Hector Martin <marcan@marcan.st>"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC GPIO driver"); diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c index 8f38303fcbc4..551faf9655b2 100644 --- a/drivers/gpio/gpio-madera.c +++ b/drivers/gpio/gpio-madera.c @@ -87,23 +87,17 @@ static int madera_gpio_direction_out(struct gpio_chip *chip,  				  MADERA_GP1_LVL_MASK, reg_val);  } -static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset, -			    int value) +static int madera_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct madera_gpio *madera_gpio = gpiochip_get_data(chip);  	struct madera *madera = madera_gpio->madera;  	unsigned int reg_offset = 2 * offset;  	unsigned int reg_val = value ? MADERA_GP1_LVL : 0; -	int ret; - -	ret = regmap_update_bits(madera->regmap, -				 MADERA_GPIO1_CTRL_1 + reg_offset, -				 MADERA_GP1_LVL_MASK, reg_val); -	/* set() doesn't return an error so log a warning */ -	if (ret) -		dev_warn(madera->dev, "Failed to write to 0x%x (%d)\n", -			 MADERA_GPIO1_CTRL_1 + reg_offset, ret); +	return regmap_update_bits(madera->regmap, +				  MADERA_GPIO1_CTRL_1 + reg_offset, +				  MADERA_GP1_LVL_MASK, reg_val);  }  static const struct gpio_chip madera_gpio_chip = { diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index fc0708ab5192..6e6504ab740a 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -103,19 +103,6 @@ static int max3191x_direction_input(struct gpio_chip *gpio, unsigned int offset)  	return 0;  } -static int max3191x_direction_output(struct gpio_chip *gpio, -				     unsigned int offset, int value) -{ -	return -EINVAL; -} - -static void max3191x_set(struct gpio_chip *gpio, unsigned int offset, int value) -{ } - -static void max3191x_set_multiple(struct gpio_chip *gpio, unsigned long *mask, -				  unsigned long *bits) -{ } -  static unsigned int max3191x_wordlen(struct max3191x_chip *max3191x)  {  	return max3191x->mode == STATUS_BYTE_ENABLED ? 2 : 1; @@ -421,9 +408,6 @@ static int max3191x_probe(struct spi_device *spi)  	max3191x->gpio.get_direction = max3191x_get_direction;  	max3191x->gpio.direction_input = max3191x_direction_input; -	max3191x->gpio.direction_output = max3191x_direction_output; -	max3191x->gpio.set = max3191x_set; -	max3191x->gpio.set_multiple = max3191x_set_multiple;  	max3191x->gpio.get = max3191x_get;  	max3191x->gpio.get_multiple = max3191x_get_multiple;  	max3191x->gpio.set_config = max3191x_set_config; diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index e688c13c8cc3..84c7c2dca822 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -143,18 +143,21 @@ static int max7301_get(struct gpio_chip *chip, unsigned offset)  	return level;  } -static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) +static int max7301_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct max7301 *ts = gpiochip_get_data(chip); +	int ret;  	/* First 4 pins are unused in the controller */  	offset += 4;  	mutex_lock(&ts->lock); -	__max7301_set(ts, offset, value); +	ret = __max7301_set(ts, offset, value);  	mutex_unlock(&ts->lock); + +	return ret;  }  int __max730x_probe(struct max7301 *ts) diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 49d362907bc7..a61d670ceeda 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -225,16 +225,19 @@ out:  	mutex_unlock(&chip->lock);  } -static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +static int max732x_gpio_set_value(struct gpio_chip *gc, unsigned int off, +				  int val)  {  	unsigned base = off & ~0x7;  	uint8_t mask = 1u << (off & 0x7);  	max732x_gpio_set_mask(gc, base, mask, val << (off & 0x7)); + +	return 0;  } -static void max732x_gpio_set_multiple(struct gpio_chip *gc, -				      unsigned long *mask, unsigned long *bits) +static int max732x_gpio_set_multiple(struct gpio_chip *gc, +				     unsigned long *mask, unsigned long *bits)  {  	unsigned mask_lo = mask[0] & 0xff;  	unsigned mask_hi = (mask[0] >> 8) & 0xff; @@ -243,6 +246,8 @@ static void max732x_gpio_set_multiple(struct gpio_chip *gc,  		max732x_gpio_set_mask(gc, 0, mask_lo, bits[0] & 0xff);  	if (mask_hi)  		max732x_gpio_set_mask(gc, 8, mask_hi, (bits[0] >> 8) & 0xff); + +	return 0;  }  static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 8c2a5609161f..02eca400b307 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -223,20 +223,17 @@ static int max77620_gpio_set_debounce(struct max77620_gpio *mgpio,  	return ret;  } -static void max77620_gpio_set(struct gpio_chip *gc, unsigned int offset, -			      int value) +static int max77620_gpio_set(struct gpio_chip *gc, unsigned int offset, +			     int value)  {  	struct max77620_gpio *mgpio = gpiochip_get_data(gc);  	u8 val; -	int ret;  	val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH :  				MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; -	ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), -				 MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); -	if (ret < 0) -		dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret); +	return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), +				  MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val);  }  static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset, diff --git a/drivers/gpio/gpio-max77650.c b/drivers/gpio/gpio-max77650.c index a553e141059f..4540da4c1418 100644 --- a/drivers/gpio/gpio-max77650.c +++ b/drivers/gpio/gpio-max77650.c @@ -166,7 +166,7 @@ static int max77650_gpio_probe(struct platform_device *pdev)  	chip->gc.direction_input = max77650_gpio_direction_input;  	chip->gc.direction_output = max77650_gpio_direction_output; -	chip->gc.set_rv = max77650_gpio_set_value; +	chip->gc.set = max77650_gpio_set_value;  	chip->gc.get = max77650_gpio_get_value;  	chip->gc.get_direction = max77650_gpio_get_direction;  	chip->gc.set_config = max77650_gpio_set_config; diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c new file mode 100644 index 000000000000..5e48eb03e7b3 --- /dev/null +++ b/drivers/gpio/gpio-max77759.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2020 Google Inc +// Copyright 2025 Linaro Ltd. +// +// GPIO driver for Maxim MAX77759 + +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/device/driver.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#include <linux/lockdep.h> +#include <linux/mfd/max77759.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/seq_file.h> + +#define MAX77759_N_GPIOS   ARRAY_SIZE(max77759_gpio_line_names) +static const char * const max77759_gpio_line_names[] = { "GPIO5", "GPIO6" }; + +struct max77759_gpio_chip { +	struct regmap *map; +	struct max77759 *max77759; +	struct gpio_chip gc; +	struct mutex maxq_lock; /* protect MaxQ r/m/w operations */ + +	struct mutex irq_lock; /* protect irq bus */ +	int irq_mask; +	int irq_mask_changed; +	int irq_trig; +	int irq_trig_changed; +}; + +#define MAX77759_GPIOx_TRIGGER(offs, val) (((val) & 1) << (offs)) +#define MAX77759_GPIOx_TRIGGER_MASK(offs) MAX77759_GPIOx_TRIGGER(offs, ~0) +enum max77759_trigger_gpio_type { +	MAX77759_GPIO_TRIGGER_RISING = 0, +	MAX77759_GPIO_TRIGGER_FALLING = 1 +}; + +#define MAX77759_GPIOx_DIR(offs, dir) (((dir) & 1) << (2 + (3 * (offs)))) +#define MAX77759_GPIOx_DIR_MASK(offs) MAX77759_GPIOx_DIR(offs, ~0) +enum max77759_control_gpio_dir { +	MAX77759_GPIO_DIR_IN = 0, +	MAX77759_GPIO_DIR_OUT = 1 +}; + +#define MAX77759_GPIOx_OUTVAL(offs, val) (((val) & 1) << (3 + (3 * (offs)))) +#define MAX77759_GPIOx_OUTVAL_MASK(offs) MAX77759_GPIOx_OUTVAL(offs, ~0) + +#define MAX77759_GPIOx_INVAL_MASK(offs) (BIT(4) << (3 * (offs))) + +static int max77759_gpio_maxq_gpio_trigger_read(struct max77759_gpio_chip *chip) +{ +	DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); +	DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); +	int ret; + +	cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_READ; + +	ret = max77759_maxq_command(chip->max77759, cmd, rsp); +	if (ret < 0) +		return ret; + +	return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_trigger_write(struct max77759_gpio_chip *chip, +						 u8 trigger) +{ +	DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + +	cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_WRITE; +	cmd->cmd[1] = trigger; + +	return max77759_maxq_command(chip->max77759, cmd, NULL); +} + +static int max77759_gpio_maxq_gpio_control_read(struct max77759_gpio_chip *chip) +{ +	DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); +	DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); +	int ret; + +	cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_CONTROL_READ; + +	ret = max77759_maxq_command(chip->max77759, cmd, rsp); +	if (ret < 0) +		return ret; + +	return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_control_write(struct max77759_gpio_chip *chip, +						 u8 ctrl) +{ +	DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + +	cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_CONTROL_WRITE; +	cmd->cmd[1] = ctrl; + +	return max77759_maxq_command(chip->max77759, cmd, NULL); +} + +static int +max77759_gpio_direction_from_control(int ctrl, unsigned int offset) +{ +	enum max77759_control_gpio_dir dir; + +	dir = !!(ctrl & MAX77759_GPIOx_DIR_MASK(offset)); +	return ((dir == MAX77759_GPIO_DIR_OUT) +		? GPIO_LINE_DIRECTION_OUT +		: GPIO_LINE_DIRECTION_IN); +} + +static int max77759_gpio_get_direction(struct gpio_chip *gc, +				       unsigned int offset) +{ +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	int ctrl; + +	ctrl = max77759_gpio_maxq_gpio_control_read(chip); +	if (ctrl < 0) +		return ctrl; + +	return max77759_gpio_direction_from_control(ctrl, offset); +} + +static int max77759_gpio_direction_helper(struct gpio_chip *gc, +					  unsigned int offset, +					  enum max77759_control_gpio_dir dir, +					  int value) +{ +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	int ctrl, new_ctrl; + +	guard(mutex)(&chip->maxq_lock); + +	ctrl = max77759_gpio_maxq_gpio_control_read(chip); +	if (ctrl < 0) +		return ctrl; + +	new_ctrl = ctrl & ~MAX77759_GPIOx_DIR_MASK(offset); +	new_ctrl |= MAX77759_GPIOx_DIR(offset, dir); + +	if (dir == MAX77759_GPIO_DIR_OUT) { +		new_ctrl &= ~MAX77759_GPIOx_OUTVAL_MASK(offset); +		new_ctrl |= MAX77759_GPIOx_OUTVAL(offset, value); +	} + +	if (new_ctrl == ctrl) +		return 0; + +	return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static int max77759_gpio_direction_input(struct gpio_chip *gc, +					 unsigned int offset) +{ +	return max77759_gpio_direction_helper(gc, offset, +					      MAX77759_GPIO_DIR_IN, -1); +} + +static int max77759_gpio_direction_output(struct gpio_chip *gc, +					  unsigned int offset, int value) +{ +	return max77759_gpio_direction_helper(gc, offset, +					      MAX77759_GPIO_DIR_OUT, value); +} + +static int max77759_gpio_get_value(struct gpio_chip *gc, unsigned int offset) +{ +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	int ctrl, mask; + +	ctrl = max77759_gpio_maxq_gpio_control_read(chip); +	if (ctrl < 0) +		return ctrl; + +	/* +	 * The input status bit doesn't reflect the pin state when the GPIO is +	 * configured as an output. Check the direction, and inspect the input +	 * or output bit accordingly. +	 */ +	mask = ((max77759_gpio_direction_from_control(ctrl, offset) +		 == GPIO_LINE_DIRECTION_IN) +		? MAX77759_GPIOx_INVAL_MASK(offset) +		: MAX77759_GPIOx_OUTVAL_MASK(offset)); + +	return !!(ctrl & mask); +} + +static int max77759_gpio_set_value(struct gpio_chip *gc, +				   unsigned int offset, int value) +{ +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	int ctrl, new_ctrl; + +	guard(mutex)(&chip->maxq_lock); + +	ctrl = max77759_gpio_maxq_gpio_control_read(chip); +	if (ctrl < 0) +		return ctrl; + +	new_ctrl = ctrl & ~MAX77759_GPIOx_OUTVAL_MASK(offset); +	new_ctrl |= MAX77759_GPIOx_OUTVAL(offset, value); + +	if (new_ctrl == ctrl) +		return 0; + +	return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static void max77759_gpio_irq_mask(struct irq_data *d) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); + +	chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); +	chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); +	chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); + +	gpiochip_disable_irq(gc, hwirq); +} + +static void max77759_gpio_irq_unmask(struct irq_data *d) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); + +	gpiochip_enable_irq(gc, hwirq); + +	chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); +	chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 0); +	chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); +} + +static int max77759_gpio_set_irq_type(struct irq_data *d, unsigned int type) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	irq_hw_number_t hwirq = irqd_to_hwirq(d); + +	chip->irq_trig &= ~MAX77759_GPIOx_TRIGGER_MASK(hwirq); +	switch (type) { +	case IRQ_TYPE_EDGE_RISING: +		chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq, +						MAX77759_GPIO_TRIGGER_RISING); +		break; + +	case IRQ_TYPE_EDGE_FALLING: +		chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq, +						MAX77759_GPIO_TRIGGER_FALLING); +		break; + +	default: +		return -EINVAL; +	} + +	chip->irq_trig_changed |= MAX77759_GPIOx_TRIGGER(hwirq, 1); + +	return 0; +} + +static void max77759_gpio_bus_lock(struct irq_data *d) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + +	mutex_lock(&chip->irq_lock); +} + +static int max77759_gpio_bus_sync_unlock_helper(struct gpio_chip *gc, +						struct max77759_gpio_chip *chip) +					       __must_hold(&chip->maxq_lock) +{ +	int ctrl, trigger, new_trigger, new_ctrl; +	unsigned long irq_trig_changed; +	int offset; +	int ret; + +	lockdep_assert_held(&chip->maxq_lock); + +	ctrl = max77759_gpio_maxq_gpio_control_read(chip); +	trigger = max77759_gpio_maxq_gpio_trigger_read(chip); +	if (ctrl < 0 || trigger < 0) { +		dev_err(gc->parent, "failed to read current state: %d / %d\n", +			ctrl, trigger); +		return (ctrl < 0) ? ctrl : trigger; +	} + +	new_trigger = trigger & ~chip->irq_trig_changed; +	new_trigger |= (chip->irq_trig & chip->irq_trig_changed); + +	/* change GPIO direction if required */ +	new_ctrl = ctrl; +	irq_trig_changed = chip->irq_trig_changed; +	for_each_set_bit(offset, &irq_trig_changed, MAX77759_N_GPIOS) { +		new_ctrl &= ~MAX77759_GPIOx_DIR_MASK(offset); +		new_ctrl |= MAX77759_GPIOx_DIR(offset, MAX77759_GPIO_DIR_IN); +	} + +	if (new_trigger != trigger) { +		ret = max77759_gpio_maxq_gpio_trigger_write(chip, new_trigger); +		if (ret) { +			dev_err(gc->parent, +				"failed to write new trigger: %d\n", ret); +			return ret; +		} +	} + +	if (new_ctrl != ctrl) { +		ret = max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +		if (ret) { +			dev_err(gc->parent, +				"failed to write new control: %d\n", ret); +			return ret; +		} +	} + +	chip->irq_trig_changed = 0; + +	return 0; +} + +static void max77759_gpio_bus_sync_unlock(struct irq_data *d) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); +	struct max77759_gpio_chip *chip = gpiochip_get_data(gc); +	int ret; + +	scoped_guard(mutex, &chip->maxq_lock) { +		ret = max77759_gpio_bus_sync_unlock_helper(gc, chip); +		if (ret) +			goto out_unlock; +	} + +	ret = regmap_update_bits(chip->map, +				 MAX77759_MAXQ_REG_UIC_INT1_M, +				 chip->irq_mask_changed, chip->irq_mask); +	if (ret) { +		dev_err(gc->parent, +			"failed to update UIC_INT1 irq mask: %d\n", ret); +		goto out_unlock; +	} + +	chip->irq_mask_changed = 0; + +out_unlock: +	mutex_unlock(&chip->irq_lock); +} + +static void max77759_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) +{ +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + +	seq_puts(p, dev_name(gc->parent)); +} + +static const struct irq_chip max77759_gpio_irq_chip = { +	.irq_mask		= max77759_gpio_irq_mask, +	.irq_unmask		= max77759_gpio_irq_unmask, +	.irq_set_type		= max77759_gpio_set_irq_type, +	.irq_bus_lock		= max77759_gpio_bus_lock, +	.irq_bus_sync_unlock	= max77759_gpio_bus_sync_unlock, +	.irq_print_chip		= max77759_gpio_irq_print_chip, +	.flags			= IRQCHIP_IMMUTABLE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static irqreturn_t max77759_gpio_irqhandler(int irq, void *data) +{ +	struct max77759_gpio_chip *chip = data; +	struct gpio_chip *gc = &chip->gc; +	bool handled = false; + +	/* iterate until no interrupt is pending */ +	while (true) { +		unsigned int uic_int1; +		int ret; +		unsigned long pending; +		int offset; + +		ret = regmap_read(chip->map, MAX77759_MAXQ_REG_UIC_INT1, +				  &uic_int1); +		if (ret < 0) { +			dev_err_ratelimited(gc->parent, +					    "failed to read IRQ status: %d\n", +					    ret); +			/* +			 * If !handled, we have looped not even once, which +			 * means we should return IRQ_NONE in that case (and +			 * of course IRQ_HANDLED otherwise). +			 */ +			return IRQ_RETVAL(handled); +		} + +		pending = uic_int1; +		pending &= (MAX77759_MAXQ_REG_UIC_INT1_GPIO6I +			    | MAX77759_MAXQ_REG_UIC_INT1_GPIO5I); +		if (!pending) +			break; + +		for_each_set_bit(offset, &pending, MAX77759_N_GPIOS) { +			/* +			 * ACK interrupt by writing 1 to bit 'offset', all +			 * others need to be written as 0. This needs to be +			 * done unconditionally hence regmap_set_bits() is +			 * inappropriate here. +			 */ +			regmap_write(chip->map, MAX77759_MAXQ_REG_UIC_INT1, +				     BIT(offset)); + +			handle_nested_irq(irq_find_mapping(gc->irq.domain, +							   offset)); + +			handled = true; +		} +	} + +	return IRQ_RETVAL(handled); +} + +static int max77759_gpio_probe(struct platform_device *pdev) +{ +	struct max77759_gpio_chip *chip; +	int irq; +	struct gpio_irq_chip *girq; +	int ret; +	unsigned long irq_flags; +	struct irq_data *irqd; + +	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); +	if (!chip) +		return -ENOMEM; + +	chip->map = dev_get_regmap(pdev->dev.parent, "maxq"); +	if (!chip->map) +		return dev_err_probe(&pdev->dev, -ENODEV, "Missing regmap\n"); + +	irq = platform_get_irq_byname(pdev, "GPI"); +	if (irq < 0) +		return dev_err_probe(&pdev->dev, irq, "Failed to get IRQ\n"); + +	chip->max77759 = dev_get_drvdata(pdev->dev.parent); +	ret = devm_mutex_init(&pdev->dev, &chip->maxq_lock); +	if (ret) +		return ret; +	ret = devm_mutex_init(&pdev->dev, &chip->irq_lock); +	if (ret) +		return ret; + +	chip->gc.base = -1; +	chip->gc.label = dev_name(&pdev->dev); +	chip->gc.parent = &pdev->dev; +	chip->gc.can_sleep = true; + +	chip->gc.names = max77759_gpio_line_names; +	chip->gc.ngpio = MAX77759_N_GPIOS; +	chip->gc.get_direction = max77759_gpio_get_direction; +	chip->gc.direction_input = max77759_gpio_direction_input; +	chip->gc.direction_output = max77759_gpio_direction_output; +	chip->gc.get = max77759_gpio_get_value; +	chip->gc.set = max77759_gpio_set_value; + +	girq = &chip->gc.irq; +	gpio_irq_chip_set_chip(girq, &max77759_gpio_irq_chip); +	/* This will let us handle the parent IRQ in the driver */ +	girq->parent_handler = NULL; +	girq->num_parents = 0; +	girq->parents = NULL; +	girq->default_type = IRQ_TYPE_NONE; +	girq->handler = handle_simple_irq; +	girq->threaded = true; + +	ret = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); +	if (ret < 0) +		return dev_err_probe(&pdev->dev, ret, +				     "Failed to add GPIO chip\n"); + +	irq_flags = IRQF_ONESHOT | IRQF_SHARED; +	irqd = irq_get_irq_data(irq); +	if (irqd) +		irq_flags |= irqd_get_trigger_type(irqd); + +	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, +					max77759_gpio_irqhandler, irq_flags, +					dev_name(&pdev->dev), chip); +	if (ret < 0) +		return dev_err_probe(&pdev->dev, ret, +				     "Failed to request IRQ\n"); + +	return ret; +} + +static const struct of_device_id max77759_gpio_of_id[] = { +	{ .compatible = "maxim,max77759-gpio", }, +	{ } +}; +MODULE_DEVICE_TABLE(of, max77759_gpio_of_id); + +static const struct platform_device_id max77759_gpio_platform_id[] = { +	{ "max77759-gpio", }, +	{ } +}; +MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id); + +static struct platform_driver max77759_gpio_driver = { +	.driver = { +		.name = "max77759-gpio", +		.probe_type = PROBE_PREFER_ASYNCHRONOUS, +		.of_match_table = max77759_gpio_of_id, +	}, +	.probe = max77759_gpio_probe, +	.id_table = max77759_gpio_platform_id, +}; + +module_platform_driver(max77759_gpio_driver); + +MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>"); +MODULE_DESCRIPTION("GPIO driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 7ee891ef6905..581a71872eab 100644 --- a/drivers/gpio/gpio-mb86s7x.c +++ b/drivers/gpio/gpio-mb86s7x.c @@ -119,7 +119,7 @@ static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned gpio)  	return !!(readl(gchip->base + PDR(gpio)) & OFFSET(gpio));  } -static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value) +static int mb86s70_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)  {  	struct mb86s70_gpio_chip *gchip = gpiochip_get_data(gc);  	unsigned long flags; @@ -135,6 +135,8 @@ static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value)  	writel(val, gchip->base + PDR(gpio));  	spin_unlock_irqrestore(&gchip->lock, flags); + +	return 0;  }  static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index 5fb357d7b78a..9a40e9579e95 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -57,15 +57,18 @@ static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value)  } -static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) +static int mc33880_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct mc33880 *mc = gpiochip_get_data(chip); +	int ret;  	mutex_lock(&mc->lock); -	__mc33880_set(mc, offset, value); +	ret = __mc33880_set(mc, offset, value);  	mutex_unlock(&mc->lock); + +	return ret;  }  static int mc33880_probe(struct spi_device *spi) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 48e3768a830e..f6af81bf2b13 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -89,7 +89,7 @@ struct ioh_gpio {  static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; -static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)  {  	u32 reg_val;  	struct ioh_gpio *chip =	gpiochip_get_data(gpio); @@ -104,6 +104,8 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)  	iowrite32(reg_val, &chip->reg->regs[chip->ch].po);  	spin_unlock_irqrestore(&chip->spinlock, flags); + +	return 0;  }  static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr) diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 6f3dda6b635f..390f2e74a9d8 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -397,7 +397,7 @@ mlxbf2_gpio_probe(struct platform_device *pdev)  	gc->ngpio = npins;  	gc->owner = THIS_MODULE; -	irq = platform_get_irq(pdev, 0); +	irq = platform_get_irq_optional(pdev, 0);  	if (irq >= 0) {  		girq = &gs->gc.irq;  		gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_irq_chip); diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 10ea71273c89..9875e34bde72 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -190,7 +190,9 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)  	struct mlxbf3_gpio_context *gs;  	struct gpio_irq_chip *girq;  	struct gpio_chip *gc; +	char *colon_ptr;  	int ret, irq; +	long num;  	gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);  	if (!gs) @@ -227,25 +229,39 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)  	gc->owner = THIS_MODULE;  	gc->add_pin_ranges = mlxbf3_gpio_add_pin_ranges; -	irq = platform_get_irq(pdev, 0); -	if (irq >= 0) { -		girq = &gs->gc.irq; -		gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip); -		girq->default_type = IRQ_TYPE_NONE; -		/* This will let us handle the parent IRQ in the driver */ -		girq->num_parents = 0; -		girq->parents = NULL; -		girq->parent_handler = NULL; -		girq->handler = handle_bad_irq; - -		/* -		 * Directly request the irq here instead of passing -		 * a flow-handler because the irq is shared. -		 */ -		ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler, -				       IRQF_SHARED, dev_name(dev), gs); -		if (ret) -			return dev_err_probe(dev, ret, "failed to request IRQ"); +	colon_ptr = strchr(dev_name(dev), ':'); +	if (!colon_ptr) { +		dev_err(dev, "invalid device name format\n"); +		return -EINVAL; +	} + +	ret = kstrtol(++colon_ptr, 16, &num); +	if (ret) { +		dev_err(dev, "invalid device instance\n"); +		return ret; +	} + +	if (!num) { +		irq = platform_get_irq(pdev, 0); +		if (irq >= 0) { +			girq = &gs->gc.irq; +			gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip); +			girq->default_type = IRQ_TYPE_NONE; +			/* This will let us handle the parent IRQ in the driver */ +			girq->num_parents = 0; +			girq->parents = NULL; +			girq->parent_handler = NULL; +			girq->handler = handle_bad_irq; + +			/* +			 * Directly request the irq here instead of passing +			 * a flow-handler because the irq is shared. +			 */ +			ret = devm_request_irq(dev, irq, mlxbf3_gpio_irq_handler, +					       IRQF_SHARED, dev_name(dev), gs); +			if (ret) +				return dev_err_probe(dev, ret, "failed to request IRQ"); +		}  	}  	platform_set_drvdata(pdev, gs); diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index 14ae25783438..8f1405733d98 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -55,9 +55,9 @@ static void ltq_mm_apply(struct ltq_mm *chip)   * @gpio:   GPIO signal number.   * @val:    Value to be written to specified signal.   * - * Set the shadow value and call ltq_mm_apply. + * Set the shadow value and call ltq_mm_apply. Always returns 0.   */ -static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value) +static int ltq_mm_set(struct gpio_chip *gc, unsigned int offset, int value)  {  	struct ltq_mm *chip = gpiochip_get_data(gc); @@ -66,6 +66,8 @@ static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value)  	else  		chip->shadow &= ~(1 << offset);  	ltq_mm_apply(chip); + +	return 0;  }  /** @@ -78,9 +80,7 @@ static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value)   */  static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value)  { -	ltq_mm_set(gc, offset, value); - -	return 0; +	return ltq_mm_set(gc, offset, value);  }  /** diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 4841e4ebe7a6..021ad62778c2 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -211,11 +211,12 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,  	return 0;  } -static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val)  { +	return 0;  } -static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  {  	unsigned long mask = bgpio_line2mask(gc, gpio);  	unsigned long flags; @@ -230,10 +231,12 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  	gc->write_reg(gc->reg_dat, gc->bgpio_data);  	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + +	return 0;  } -static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, -				 int val) +static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, +				int val)  {  	unsigned long mask = bgpio_line2mask(gc, gpio); @@ -241,9 +244,11 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,  		gc->write_reg(gc->reg_set, mask);  	else  		gc->write_reg(gc->reg_clr, mask); + +	return 0;  } -static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)  {  	unsigned long mask = bgpio_line2mask(gc, gpio);  	unsigned long flags; @@ -258,6 +263,8 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)  	gc->write_reg(gc->reg_set, gc->bgpio_data);  	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + +	return 0;  }  static void bgpio_multiple_get_masks(struct gpio_chip *gc, @@ -298,21 +305,25 @@ static void bgpio_set_multiple_single_reg(struct gpio_chip *gc,  	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);  } -static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, +static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,  			       unsigned long *bits)  {  	bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_dat); + +	return 0;  } -static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, -				   unsigned long *bits) +static int bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, +				  unsigned long *bits)  {  	bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_set); + +	return 0;  } -static void bgpio_set_multiple_with_clear(struct gpio_chip *gc, -					  unsigned long *mask, -					  unsigned long *bits) +static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, +					 unsigned long *mask, +					 unsigned long *bits)  {  	unsigned long set_mask, clear_mask; @@ -322,6 +333,8 @@ static void bgpio_set_multiple_with_clear(struct gpio_chip *gc,  		gc->write_reg(gc->reg_set, set_mask);  	if (clear_mask)  		gc->write_reg(gc->reg_clr, clear_mask); + +	return 0;  }  static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out) @@ -335,6 +348,11 @@ static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_ou  		return pinctrl_gpio_direction_input(gc, gpio);  } +static int bgpio_dir_in_err(struct gpio_chip *gc, unsigned int gpio) +{ +	return -EINVAL; +} +  static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)  {  	return bgpio_dir_return(gc, gpio, false); @@ -566,7 +584,11 @@ static int bgpio_setup_direction(struct gpio_chip *gc,  			gc->direction_output = bgpio_dir_out_err;  		else  			gc->direction_output = bgpio_simple_dir_out; -		gc->direction_input = bgpio_simple_dir_in; + +		if (flags & BGPIOF_NO_INPUT) +			gc->direction_input = bgpio_dir_in_err; +		else +			gc->direction_input = bgpio_simple_dir_in;  	}  	return 0; @@ -712,28 +734,6 @@ static const struct of_device_id bgpio_of_match[] = {  };  MODULE_DEVICE_TABLE(of, bgpio_of_match); -static struct bgpio_pdata *bgpio_parse_fw(struct device *dev, unsigned long *flags) -{ -	struct bgpio_pdata *pdata; - -	if (!dev_fwnode(dev)) -		return NULL; - -	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -	if (!pdata) -		return ERR_PTR(-ENOMEM); - -	pdata->base = -1; - -	if (device_is_big_endian(dev)) -		*flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; - -	if (device_property_read_bool(dev, "no-output")) -		*flags |= BGPIOF_NO_OUTPUT; - -	return pdata; -} -  static int bgpio_pdev_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -745,18 +745,10 @@ static int bgpio_pdev_probe(struct platform_device *pdev)  	void __iomem *dirin;  	unsigned long sz;  	unsigned long flags = 0; +	unsigned int base;  	int err;  	struct gpio_chip *gc; -	struct bgpio_pdata *pdata; - -	pdata = bgpio_parse_fw(dev, &flags); -	if (IS_ERR(pdata)) -		return PTR_ERR(pdata); - -	if (!pdata) { -		pdata = dev_get_platdata(dev); -		flags = pdev->id_entry->driver_data; -	} +	const char *label;  	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");  	if (!r) @@ -788,17 +780,27 @@ static int bgpio_pdev_probe(struct platform_device *pdev)  	if (!gc)  		return -ENOMEM; +	if (device_is_big_endian(dev)) +		flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; + +	if (device_property_read_bool(dev, "no-output")) +		flags |= BGPIOF_NO_OUTPUT; +  	err = bgpio_init(gc, dev, sz, dat, set, clr, dirout, dirin, flags);  	if (err)  		return err; -	if (pdata) { -		if (pdata->label) -			gc->label = pdata->label; -		gc->base = pdata->base; -		if (pdata->ngpio > 0) -			gc->ngpio = pdata->ngpio; -	} +	err = device_property_read_string(dev, "label", &label); +	if (!err) +		gc->label = label; + +	/* +	 * This property *must not* be used in device-tree sources, it's only +	 * meant to be passed to the driver from board files and MFD core. +	 */ +	err = device_property_read_u32(dev, "gpio-mmio,base", &base); +	if (!err && base <= INT_MAX) +		gc->base = base;  	platform_set_drvdata(pdev, gc); @@ -809,9 +811,6 @@ static const struct platform_device_id bgpio_id_table[] = {  	{  		.name		= "basic-mmio-gpio",  		.driver_data	= 0, -	}, { -		.name		= "basic-mmio-gpio-be", -		.driver_data	= BGPIOF_BIG_ENDIAN,  	},  	{ }  }; diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 266c0953d914..a7d69f3835c1 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -449,9 +449,9 @@ static int gpio_mockup_probe(struct platform_device *pdev)  	gc->owner = THIS_MODULE;  	gc->parent = dev;  	gc->get = gpio_mockup_get; -	gc->set_rv = gpio_mockup_set; +	gc->set = gpio_mockup_set;  	gc->get_multiple = gpio_mockup_get_multiple; -	gc->set_multiple_rv = gpio_mockup_set_multiple; +	gc->set_multiple = gpio_mockup_set_multiple;  	gc->direction_output = gpio_mockup_dirout;  	gc->direction_input = gpio_mockup_dirin;  	gc->get_direction = gpio_mockup_get_direction; diff --git a/drivers/gpio/gpio-moxtet.c b/drivers/gpio/gpio-moxtet.c index 61f9efd6c64f..4eb9f1a2779b 100644 --- a/drivers/gpio/gpio-moxtet.c +++ b/drivers/gpio/gpio-moxtet.c @@ -52,15 +52,15 @@ static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset)  	return !!(ret & BIT(offset));  } -static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, -				  int val) +static int moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, +				 int val)  {  	struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);  	int state;  	state = moxtet_device_written(chip->dev);  	if (state < 0) -		return; +		return state;  	offset -= MOXTET_GPIO_INPUTS; @@ -69,7 +69,7 @@ static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset,  	else  		state &= ~BIT(offset); -	moxtet_device_write(chip->dev, state); +	return moxtet_device_write(chip->dev, state);  }  static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) @@ -104,13 +104,11 @@ static int moxtet_gpio_direction_output(struct gpio_chip *gc,  	struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);  	if (chip->desc->out_mask & BIT(offset)) -		moxtet_gpio_set_value(gc, offset, val); +		return moxtet_gpio_set_value(gc, offset, val);  	else if (chip->desc->in_mask & BIT(offset))  		return -ENOTSUPP; -	else -		return -EINVAL; -	return 0; +	return -EINVAL;  }  static int moxtet_gpio_probe(struct device *dev) diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c index 091d96f2d682..dad0eca1ca2e 100644 --- a/drivers/gpio/gpio-mpc5200.c +++ b/drivers/gpio/gpio-mpc5200.c @@ -69,7 +69,7 @@ __mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  	out_8(®s->wkup_dvo, chip->shadow_dvo);  } -static void +static int  mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  {  	unsigned long flags; @@ -81,6 +81,8 @@ mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  	spin_unlock_irqrestore(&gpio_lock, flags);  	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + +	return 0;  }  static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) @@ -228,7 +230,7 @@ __mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  	out_be32(®s->simple_dvo, chip->shadow_dvo);  } -static void +static int  mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  {  	unsigned long flags; @@ -240,6 +242,8 @@ mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  	spin_unlock_irqrestore(&gpio_lock, flags);  	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + +	return 0;  }  static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 541517536489..121efdd71e45 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -123,9 +123,12 @@ static irqreturn_t mpc8xxx_gpio_irq_cascade(int irq, void *data)  static void mpc8xxx_irq_unmask(struct irq_data *d)  {  	struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); +	irq_hw_number_t hwirq = irqd_to_hwirq(d);  	struct gpio_chip *gc = &mpc8xxx_gc->gc;  	unsigned long flags; +	gpiochip_enable_irq(gc, hwirq); +  	raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);  	gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, @@ -138,6 +141,7 @@ static void mpc8xxx_irq_unmask(struct irq_data *d)  static void mpc8xxx_irq_mask(struct irq_data *d)  {  	struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); +	irq_hw_number_t hwirq = irqd_to_hwirq(d);  	struct gpio_chip *gc = &mpc8xxx_gc->gc;  	unsigned long flags; @@ -148,6 +152,8 @@ static void mpc8xxx_irq_mask(struct irq_data *d)  		& ~mpc_pin2mask(irqd_to_hwirq(d)));  	raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + +	gpiochip_disable_irq(gc, hwirq);  }  static void mpc8xxx_irq_ack(struct irq_data *d) @@ -244,6 +250,8 @@ static struct irq_chip mpc8xxx_irq_chip = {  	.irq_ack	= mpc8xxx_irq_ack,  	/* this might get overwritten in mpc8xxx_probe() */  	.irq_set_type	= mpc8xxx_irq_set_type, +	.flags = IRQCHIP_IMMUTABLE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS,  };  static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq, diff --git a/drivers/gpio/gpio-mpfs.c b/drivers/gpio/gpio-mpfs.c index 561a961c97a6..82d557a7e5d8 100644 --- a/drivers/gpio/gpio-mpfs.c +++ b/drivers/gpio/gpio-mpfs.c @@ -99,16 +99,19 @@ static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index)  		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) +static int mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value)  {  	struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); +	int ret;  	mpfs_gpio_get(gc, gpio_index); -	regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index), -			   value << gpio_index); +	ret = regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, +				 BIT(gpio_index), value << gpio_index);  	mpfs_gpio_get(gc, gpio_index); + +	return ret;  }  static int mpfs_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 3ea32c5e33d1..9f42bb30b4ec 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -160,8 +160,8 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank)  	return buf;  } -static void gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, -				    unsigned long *bits) +static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, +				   unsigned long *bits)  {  	unsigned long i, bank, bank_mask, bank_bits;  	int ret; @@ -180,11 +180,11 @@ static void gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask,  			ret = gpio_mpsse_set_bank(priv, bank);  			if (ret) -				dev_err(&priv->intf->dev, -					"Couldn't set values for bank %ld!", -					bank); +				return ret;  		}  	} + +	return 0;  }  static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask, @@ -227,7 +227,7 @@ static int gpio_mpsse_gpio_get(struct gpio_chip *chip, unsigned int offset)  		return 0;  } -static void gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset,  			       int value)  {  	unsigned long mask = 0, bits = 0; @@ -236,7 +236,7 @@ static void gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset,  	if (value)  		__set_bit(offset, &bits); -	gpio_mpsse_set_multiple(chip, &mask, &bits); +	return gpio_mpsse_set_multiple(chip, &mask, &bits);  }  static int gpio_mpsse_direction_output(struct gpio_chip *chip, @@ -249,9 +249,7 @@ static int gpio_mpsse_direction_output(struct gpio_chip *chip,  	scoped_guard(mutex, &priv->io_mutex)  		priv->gpio_dir[bank] |= BIT(bank_offset); -	gpio_mpsse_gpio_set(chip, offset, value); - -	return 0; +	return gpio_mpsse_gpio_set(chip, offset, value);  }  static int gpio_mpsse_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index 6db9e469e0dc..b0cccd856840 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -486,7 +486,7 @@ struct msc313_gpio {  	u8 *saved;  }; -static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct msc313_gpio *gpio = gpiochip_get_data(chip);  	u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); @@ -497,6 +497,8 @@ static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int val  		gpioreg &= ~MSC313_GPIO_OUT;  	writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); + +	return 0;  }  static int msc313_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 3604abcb6fec..5e3f54cb8bc4 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -408,9 +408,8 @@ static void mvebu_gpio_irq_ack(struct irq_data *d)  	struct mvebu_gpio_chip *mvchip = gc->private;  	u32 mask = d->mask; -	irq_gc_lock(gc); +	guard(raw_spinlock)(&gc->lock);  	mvebu_gpio_write_edge_cause(mvchip, ~mask); -	irq_gc_unlock(gc);  }  static void mvebu_gpio_edge_irq_mask(struct irq_data *d) @@ -420,10 +419,9 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d)  	struct irq_chip_type *ct = irq_data_get_chip_type(d);  	u32 mask = d->mask; -	irq_gc_lock(gc); +	guard(raw_spinlock)(&gc->lock);  	ct->mask_cache_priv &= ~mask;  	mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); -	irq_gc_unlock(gc);  }  static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) @@ -433,11 +431,10 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)  	struct irq_chip_type *ct = irq_data_get_chip_type(d);  	u32 mask = d->mask; -	irq_gc_lock(gc); +	guard(raw_spinlock)(&gc->lock);  	mvebu_gpio_write_edge_cause(mvchip, ~mask);  	ct->mask_cache_priv |= mask;  	mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); -	irq_gc_unlock(gc);  }  static void mvebu_gpio_level_irq_mask(struct irq_data *d) @@ -447,10 +444,9 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d)  	struct irq_chip_type *ct = irq_data_get_chip_type(d);  	u32 mask = d->mask; -	irq_gc_lock(gc); +	guard(raw_spinlock)(&gc->lock);  	ct->mask_cache_priv &= ~mask;  	mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); -	irq_gc_unlock(gc);  }  static void mvebu_gpio_level_irq_unmask(struct irq_data *d) @@ -460,10 +456,9 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d)  	struct irq_chip_type *ct = irq_data_get_chip_type(d);  	u32 mask = d->mask; -	irq_gc_lock(gc); +	guard(raw_spinlock)(&gc->lock);  	ct->mask_cache_priv |= mask;  	mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); -	irq_gc_unlock(gc);  }  /***************************************************************************** @@ -1173,7 +1168,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)  	mvchip->chip.direction_input = mvebu_gpio_direction_input;  	mvchip->chip.get = mvebu_gpio_get;  	mvchip->chip.direction_output = mvebu_gpio_direction_output; -	mvchip->chip.set_rv = mvebu_gpio_set; +	mvchip->chip.set = mvebu_gpio_set;  	if (have_irqs)  		mvchip->chip.to_irq = mvebu_gpio_to_irq;  	mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK; @@ -1241,8 +1236,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev)  	if (!have_irqs)  		return 0; -	mvchip->domain = -	    irq_domain_add_linear(np, ngpios, &irq_generic_chip_ops, NULL); +	mvchip->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), ngpios, +						  &irq_generic_chip_ops, NULL);  	if (!mvchip->domain) {  		dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",  			mvchip->chip.label); diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 619b6fb9d833..433cbadc3a4c 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -7,6 +7,7 @@  // Authors: Daniel Mack, Juergen Beisert.  // Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. +#include <linux/cleanup.h>  #include <linux/clk.h>  #include <linux/err.h>  #include <linux/init.h> @@ -22,6 +23,7 @@  #include <linux/spinlock.h>  #include <linux/syscore_ops.h>  #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h>  #include <linux/of.h>  #include <linux/bug.h> @@ -64,7 +66,7 @@ struct mxc_gpio_port {  	int irq_high;  	void (*mx_irq_handler)(struct irq_desc *desc);  	struct irq_domain *domain; -	struct gpio_chip gc; +	struct gpio_generic_chip gen_gc;  	struct device *dev;  	u32 both_edges;  	struct mxc_gpio_reg_saved gpio_saved_reg; @@ -161,7 +163,6 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)  {  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);  	struct mxc_gpio_port *port = gc->private; -	unsigned long flags;  	u32 bit, val;  	u32 gpio_idx = d->hwirq;  	int edge; @@ -179,7 +180,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)  		if (GPIO_EDGE_SEL >= 0) {  			edge = GPIO_INT_BOTH_EDGES;  		} else { -			val = port->gc.get(&port->gc, gpio_idx); +			val = port->gen_gc.gc.get(&port->gen_gc.gc, gpio_idx);  			if (val) {  				edge = GPIO_INT_LOW_LEV;  				pr_debug("mxc: set GPIO %d to low trigger\n", gpio_idx); @@ -200,41 +201,38 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)  		return -EINVAL;  	} -	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags); +	scoped_guard(gpio_generic_lock_irqsave, &port->gen_gc) { +		if (GPIO_EDGE_SEL >= 0) { +			val = readl(port->base + GPIO_EDGE_SEL); +			if (edge == GPIO_INT_BOTH_EDGES) +				writel(val | (1 << gpio_idx), +				       port->base + GPIO_EDGE_SEL); +			else +				writel(val & ~(1 << gpio_idx), +				       port->base + GPIO_EDGE_SEL); +		} -	if (GPIO_EDGE_SEL >= 0) { -		val = readl(port->base + GPIO_EDGE_SEL); -		if (edge == GPIO_INT_BOTH_EDGES) -			writel(val | (1 << gpio_idx), -				port->base + GPIO_EDGE_SEL); -		else -			writel(val & ~(1 << gpio_idx), -				port->base + GPIO_EDGE_SEL); -	} +		if (edge != GPIO_INT_BOTH_EDGES) { +			reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */ +			bit = gpio_idx & 0xf; +			val = readl(reg) & ~(0x3 << (bit << 1)); +			writel(val | (edge << (bit << 1)), reg); +		} -	if (edge != GPIO_INT_BOTH_EDGES) { -		reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */ -		bit = gpio_idx & 0xf; -		val = readl(reg) & ~(0x3 << (bit << 1)); -		writel(val | (edge << (bit << 1)), reg); +		writel(1 << gpio_idx, port->base + GPIO_ISR); +		port->pad_type[gpio_idx] = type;  	} -	writel(1 << gpio_idx, port->base + GPIO_ISR); -	port->pad_type[gpio_idx] = type; - -	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags); - -	return port->gc.direction_input(&port->gc, gpio_idx); +	return port->gen_gc.gc.direction_input(&port->gen_gc.gc, gpio_idx);  }  static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)  {  	void __iomem *reg = port->base; -	unsigned long flags;  	u32 bit, val;  	int edge; -	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags); +	guard(gpio_generic_lock_irqsave)(&port->gen_gc);  	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */  	bit = gpio & 0xf; @@ -250,12 +248,9 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)  	} else {  		pr_err("mxc: invalid configuration for GPIO %d: %x\n",  		       gpio, edge); -		goto unlock; +		return;  	}  	writel(val | (edge << (bit << 1)), reg); - -unlock: -	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);  }  /* handle 32 interrupts in one status register */ @@ -420,6 +415,7 @@ static void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enab  static int mxc_gpio_probe(struct platform_device *pdev)  { +	struct gpio_generic_chip_config config = { };  	struct device_node *np = pdev->dev.of_node;  	struct mxc_gpio_port *port;  	int irq_count; @@ -479,20 +475,31 @@ static int mxc_gpio_probe(struct platform_device *pdev)  		port->mx_irq_handler = mx3_gpio_irq_handler;  	mxc_update_irq_chained_handler(port, true); -	err = bgpio_init(&port->gc, &pdev->dev, 4, -			 port->base + GPIO_PSR, -			 port->base + GPIO_DR, NULL, -			 port->base + GPIO_GDIR, NULL, -			 BGPIOF_READ_OUTPUT_REG_SET); + +	config.dev = &pdev->dev; +	config.sz = 4; +	config.dat = port->base + GPIO_PSR; +	config.set = port->base + GPIO_DR; +	config.dirout = port->base + GPIO_GDIR; +	config.flags = BGPIOF_READ_OUTPUT_REG_SET; + +	err = gpio_generic_chip_init(&port->gen_gc, &config);  	if (err)  		goto out_bgio; -	port->gc.request = mxc_gpio_request; -	port->gc.free = mxc_gpio_free; -	port->gc.to_irq = mxc_gpio_to_irq; -	port->gc.base = of_alias_get_id(np, "gpio") * 32; - -	err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port); +	port->gen_gc.gc.request = mxc_gpio_request; +	port->gen_gc.gc.free = mxc_gpio_free; +	port->gen_gc.gc.to_irq = mxc_gpio_to_irq; +	/* +	 * Driver is DT-only, so a fixed base needs only be maintained for legacy +	 * userspace with sysfs interface. +	 */ +	if (IS_ENABLED(CONFIG_GPIO_SYSFS)) +		port->gen_gc.gc.base = of_alias_get_id(np, "gpio") * 32; +	else /* silence boot time warning */ +		port->gen_gc.gc.base = -1; + +	err = devm_gpiochip_add_data(&pdev->dev, &port->gen_gc.gc, port);  	if (err)  		goto out_bgio; @@ -502,8 +509,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)  		goto out_bgio;  	} -	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, -					     &irq_domain_simple_ops, NULL); +	port->domain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), 32, irq_base, 0, +						&irq_domain_simple_ops, NULL);  	if (!port->domain) {  		err = -ENODEV;  		goto out_bgio; @@ -566,7 +573,8 @@ static bool mxc_gpio_generic_config(struct mxc_gpio_port *port,  	if (of_device_is_compatible(np, "fsl,imx8dxl-gpio") ||  	    of_device_is_compatible(np, "fsl,imx8qxp-gpio") ||  	    of_device_is_compatible(np, "fsl,imx8qm-gpio")) -		return (gpiochip_generic_config(&port->gc, offset, conf) == 0); +		return (gpiochip_generic_config(&port->gen_gc.gc, +						offset, conf) == 0);  	return false;  } diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 024ad077e98d..0ea46f3d04e1 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -303,8 +303,8 @@ static int mxs_gpio_probe(struct platform_device *pdev)  		goto out_iounmap;  	} -	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, -					     &irq_domain_simple_ops, NULL); +	port->domain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), 32, irq_base, 0, +						&irq_domain_simple_ops, NULL);  	if (!port->domain) {  		err = -ENODEV;  		goto out_iounmap; diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index fa19a44943fd..bcf4b07dd458 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -347,8 +347,8 @@ static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned int offset)  	return value;  } -static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, -				int val) +static int nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, +			       int val)  {  	struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); @@ -357,6 +357,8 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset,  	__nmk_gpio_set_output(nmk_chip, offset, val);  	clk_disable(nmk_chip->clk); + +	return 0;  }  static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-npcm-sgpio.c b/drivers/gpio/gpio-npcm-sgpio.c index 260570614543..83c77a2c0623 100644 --- a/drivers/gpio/gpio-npcm-sgpio.c +++ b/drivers/gpio/gpio-npcm-sgpio.c @@ -211,9 +211,7 @@ static int npcm_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)  static int npcm_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)  { -	gc->set(gc, offset, val); - -	return 0; +	return gc->set(gc, offset, val);  }  static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) @@ -226,7 +224,7 @@ static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)  	return GPIO_LINE_DIRECTION_IN;  } -static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)  {  	struct npcm_sgpio *gpio = gpiochip_get_data(gc);  	const struct  npcm_sgpio_bank *bank = offset_to_bank(offset); @@ -242,6 +240,8 @@ static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)  		reg &= ~BIT(GPIO_BIT(offset));  	iowrite8(reg, addr); + +	return 0;  }  static int npcm_sgpio_get(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-octeon.c b/drivers/gpio/gpio-octeon.c index afb0e8a791e5..777e20c608dc 100644 --- a/drivers/gpio/gpio-octeon.c +++ b/drivers/gpio/gpio-octeon.c @@ -47,12 +47,15 @@ static int octeon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)  	return 0;  } -static void octeon_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int octeon_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct octeon_gpio *gpio = gpiochip_get_data(chip);  	u64 mask = 1ull << offset;  	u64 reg = gpio->register_base + (value ? TX_SET : TX_CLEAR);  	cvmx_write_csr(reg, mask); + +	return 0;  }  static int octeon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 54c4bfdccf56..a268c76bdca6 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -953,7 +953,7 @@ static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset,  	return ret;  } -static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int omap_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct gpio_bank *bank;  	unsigned long flags; @@ -962,10 +962,12 @@ static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value)  	raw_spin_lock_irqsave(&bank->lock, flags);  	bank->set_dataout(bank, offset, value);  	raw_spin_unlock_irqrestore(&bank->lock, flags); + +	return 0;  } -static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, -				   unsigned long *bits) +static int omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, +				  unsigned long *bits)  {  	struct gpio_bank *bank = gpiochip_get_data(chip);  	void __iomem *reg = bank->base + bank->regs->dataout; @@ -977,6 +979,8 @@ static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,  	writel_relaxed(l, reg);  	bank->context.dataout = l;  	raw_spin_unlock_irqrestore(&bank->lock, flags); + +	return 0;  }  /*---------------------------------------------------------------------*/ diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index 28dba7048509..e377f6dd4ccf 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -54,12 +54,11 @@ static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset)  	return !!(val & BIT(offset));  } -static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, -			int value) +static int palmas_gpio_set(struct gpio_chip *gc, unsigned int offset, +			   int value)  {  	struct palmas_gpio *pg = gpiochip_get_data(gc);  	struct palmas *palmas = pg->palmas; -	int ret;  	unsigned int reg;  	int gpio16 = (offset/8); @@ -71,9 +70,7 @@ static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,  		reg = (value) ?  			PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT; -	ret = palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset)); -	if (ret < 0) -		dev_err(gc->parent, "Reg 0x%02x write failed, %d\n", reg, ret); +	return palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset));  }  static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -89,7 +86,9 @@ static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset,  	reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;  	/* Set the initial value */ -	palmas_gpio_set(gc, offset, value); +	ret = palmas_gpio_set(gc, offset, value); +	if (ret) +		return ret;  	ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg,  				BIT(offset), BIT(offset)); @@ -140,6 +139,7 @@ static const struct of_device_id of_palmas_gpio_match[] = {  	{ .compatible = "ti,tps80036-gpio", .data = &tps80036_dev_data,},  	{ },  }; +MODULE_DEVICE_TABLE(of, of_palmas_gpio_match);  static int palmas_gpio_probe(struct platform_device *pdev)  { @@ -197,3 +197,13 @@ static int __init palmas_gpio_init(void)  	return platform_driver_register(&palmas_gpio_driver);  }  subsys_initcall(palmas_gpio_init); + +static void __exit palmas_gpio_exit(void) +{ +	platform_driver_unregister(&palmas_gpio_driver); +} +module_exit(palmas_gpio_exit); + +MODULE_DESCRIPTION("TI PALMAS series GPIO driver"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 13cc120cf11f..b46927f55038 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -38,6 +38,10 @@  #define PCA953X_INVERT		0x02  #define PCA953X_DIRECTION	0x03 +#define TCA6418_INPUT		0x14 +#define TCA6418_OUTPUT		0x17 +#define TCA6418_DIRECTION	0x23 +  #define REG_ADDR_MASK		GENMASK(5, 0)  #define REG_ADDR_EXT		BIT(6)  #define REG_ADDR_AI		BIT(7) @@ -76,7 +80,8 @@  #define PCA953X_TYPE		BIT(12)  #define PCA957X_TYPE		BIT(13)  #define PCAL653X_TYPE		BIT(14) -#define PCA_TYPE_MASK		GENMASK(15, 12) +#define TCA6418_TYPE		BIT(16) +#define PCA_TYPE_MASK		GENMASK(16, 12)  #define PCA_CHIP_TYPE(x)	((x) & PCA_TYPE_MASK) @@ -115,6 +120,7 @@ static const struct i2c_device_id pca953x_id[] = {  	{ "pca6107", 8  | PCA953X_TYPE | PCA_INT, },  	{ "tca6408", 8  | PCA953X_TYPE | PCA_INT, },  	{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, +	{ "tca6418", 18 | TCA6418_TYPE | PCA_INT, },  	{ "tca6424", 24 | PCA953X_TYPE | PCA_INT, },  	{ "tca9538", 8  | PCA953X_TYPE | PCA_INT, },  	{ "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, @@ -204,6 +210,13 @@ static const struct pca953x_reg_config pca957x_regs = {  	.invert = PCA957X_INVRT,  }; +static const struct pca953x_reg_config tca6418_regs = { +	.direction = TCA6418_DIRECTION, +	.output = TCA6418_OUTPUT, +	.input = TCA6418_INPUT, +	.invert = 0xFF, /* Does not apply */ +}; +  struct pca953x_chip {  	unsigned gpio_start;  	struct mutex i2c_lock; @@ -215,6 +228,8 @@ struct pca953x_chip {  	DECLARE_BITMAP(irq_stat, MAX_LINE);  	DECLARE_BITMAP(irq_trig_raise, MAX_LINE);  	DECLARE_BITMAP(irq_trig_fall, MAX_LINE); +	DECLARE_BITMAP(irq_trig_level_high, MAX_LINE); +	DECLARE_BITMAP(irq_trig_level_low, MAX_LINE);  #endif  	atomic_t wakeup_path; @@ -235,6 +250,22 @@ static int pca953x_bank_shift(struct pca953x_chip *chip)  	return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);  } +/* + * Helper function to get the correct bit mask for a given offset and chip type. + * The TCA6418's input, output, and direction banks have a peculiar bit order: + * the first byte uses reversed bit order, while the second byte uses standard order. + */ +static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned int offset) +{ +	unsigned int bit_pos_in_bank = offset % BANK_SZ; +	int msb = BANK_SZ - 1; + +	if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE && offset <= msb) +		return BIT(msb - bit_pos_in_bank); + +	return BIT(bit_pos_in_bank); +} +  #define PCA953x_BANK_INPUT	BIT(0)  #define PCA953x_BANK_OUTPUT	BIT(1)  #define PCA953x_BANK_POLARITY	BIT(2) @@ -351,18 +382,43 @@ static bool pcal6534_check_register(struct pca953x_chip *chip, unsigned int reg,  	return true;  } +/* TCA6418 breaks the PCA953x register order rule */ +static bool tca6418_check_register(struct pca953x_chip *chip, unsigned int reg, +				   u32 access_type_mask) +{ +	/*  Valid Input Registers - BIT(0) for readable access */ +	if (reg >= TCA6418_INPUT && reg < (TCA6418_INPUT + NBANK(chip))) +		return (access_type_mask & BIT(0)); + +	/*  Valid Output Registers - BIT(1) for writeable access */ +	if (reg >= TCA6418_OUTPUT && reg < (TCA6418_OUTPUT + NBANK(chip))) +		return (access_type_mask & (BIT(0) | BIT(1))); + +	/*  Valid Direction Registers - BIT(2) for volatile access */ +	if (reg >= TCA6418_DIRECTION && reg < (TCA6418_DIRECTION + NBANK(chip))) +		return (access_type_mask & (BIT(0) | BIT(1))); + +	return false; +} +  static bool pca953x_readable_register(struct device *dev, unsigned int reg)  {  	struct pca953x_chip *chip = dev_get_drvdata(dev);  	u32 bank; -	if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { +	switch (PCA_CHIP_TYPE(chip->driver_data)) { +	case PCA957X_TYPE:  		bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT |  		       PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG |  		       PCA957x_BANK_BUSHOLD; -	} else { +		break; +	case TCA6418_TYPE: +		/* BIT(0) to indicate read access */ +		return tca6418_check_register(chip, reg, BIT(0)); +	default:  		bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT |  		       PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; +		break;  	}  	if (chip->driver_data & PCA_PCAL) { @@ -379,12 +435,18 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg)  	struct pca953x_chip *chip = dev_get_drvdata(dev);  	u32 bank; -	if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { +	switch (PCA_CHIP_TYPE(chip->driver_data)) { +	case PCA957X_TYPE:  		bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY |  			PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; -	} else { +		break; +	case TCA6418_TYPE: +		/* BIT(1) for write access */ +		return tca6418_check_register(chip, reg, BIT(1)); +	default:  		bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY |  			PCA953x_BANK_CONFIG; +		break;  	}  	if (chip->driver_data & PCA_PCAL) @@ -399,10 +461,17 @@ static bool pca953x_volatile_register(struct device *dev, unsigned int reg)  	struct pca953x_chip *chip = dev_get_drvdata(dev);  	u32 bank; -	if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) +	switch (PCA_CHIP_TYPE(chip->driver_data)) { +	case PCA957X_TYPE:  		bank = PCA957x_BANK_INPUT; -	else +		break; +	case TCA6418_TYPE: +		/* BIT(2) for volatile access */ +		return tca6418_check_register(chip, reg, BIT(2)); +	default:  		bank = PCA953x_BANK_INPUT; +		break; +	}  	if (chip->driver_data & PCA_PCAL)  		bank |= PCAL9xxx_BANK_IRQ_STAT; @@ -487,6 +556,16 @@ static u8 pcal6534_recalc_addr(struct pca953x_chip *chip, int reg, int off)  	return pinctrl + addr + (off / BANK_SZ);  } +static u8 tca6418_recalc_addr(struct pca953x_chip *chip, int reg_base, int offset) +{ +	/* +	 * reg_base will be TCA6418_INPUT, TCA6418_OUTPUT, or TCA6418_DIRECTION +	 * offset is the global GPIO line offset (0-17) +	 * BANK_SZ is 8 for TCA6418 (8 bits per register bank) +	 */ +	return reg_base + (offset / BANK_SZ); +} +  static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val)  {  	u8 regaddr = chip->recalc_addr(chip, reg, 0); @@ -527,11 +606,14 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)  {  	struct pca953x_chip *chip = gpiochip_get_data(gc);  	u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); -	u8 bit = BIT(off % BANK_SZ); +	u8 bit = pca953x_get_bit_mask(chip, off);  	guard(mutex)(&chip->i2c_lock); -	return regmap_write_bits(chip->regmap, dirreg, bit, bit); +	if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) +		return regmap_update_bits(chip->regmap, dirreg, bit, 0); + +	return regmap_update_bits(chip->regmap, dirreg, bit, bit);  }  static int pca953x_gpio_direction_output(struct gpio_chip *gc, @@ -540,25 +622,31 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,  	struct pca953x_chip *chip = gpiochip_get_data(gc);  	u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);  	u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); -	u8 bit = BIT(off % BANK_SZ); +	u8 bit = pca953x_get_bit_mask(chip, off);  	int ret;  	guard(mutex)(&chip->i2c_lock);  	/* set output level */ -	ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); +	ret = regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0);  	if (ret)  		return ret; -	/* then direction */ -	return regmap_write_bits(chip->regmap, dirreg, bit, 0); +	/* +	 * then direction +	 * (in/out logic is inverted on TCA6418) +	 */ +	if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) +		return regmap_update_bits(chip->regmap, dirreg, bit, bit); + +	return regmap_update_bits(chip->regmap, dirreg, bit, 0);  }  static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)  {  	struct pca953x_chip *chip = gpiochip_get_data(gc);  	u8 inreg = chip->recalc_addr(chip, chip->regs->input, off); -	u8 bit = BIT(off % BANK_SZ); +	u8 bit = pca953x_get_bit_mask(chip, off);  	u32 reg_val;  	int ret; @@ -575,18 +663,18 @@ static int pca953x_gpio_set_value(struct gpio_chip *gc, unsigned int off,  {  	struct pca953x_chip *chip = gpiochip_get_data(gc);  	u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); -	u8 bit = BIT(off % BANK_SZ); +	u8 bit = pca953x_get_bit_mask(chip, off);  	guard(mutex)(&chip->i2c_lock); -	return regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); +	return regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0);  }  static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)  {  	struct pca953x_chip *chip = gpiochip_get_data(gc);  	u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); -	u8 bit = BIT(off % BANK_SZ); +	u8 bit = pca953x_get_bit_mask(chip, off);  	u32 reg_val;  	int ret; @@ -595,7 +683,14 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)  	if (ret < 0)  		return ret; -	if (reg_val & bit) +	/* (in/out logic is inverted on TCA6418) */ +	if (reg_val & bit) { +		if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) +			return GPIO_LINE_DIRECTION_OUT; + +		return GPIO_LINE_DIRECTION_IN; +	} +	if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE)  		return GPIO_LINE_DIRECTION_IN;  	return GPIO_LINE_DIRECTION_OUT; @@ -656,9 +751,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,  	/* Configure pull-up/pull-down */  	if (param == PIN_CONFIG_BIAS_PULL_UP) -		ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); +		ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, bit);  	else if (param == PIN_CONFIG_BIAS_PULL_DOWN) -		ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); +		ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, 0);  	else  		ret = 0;  	if (ret) @@ -666,9 +761,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,  	/* Disable/Enable pull-up/pull-down */  	if (param == PIN_CONFIG_BIAS_DISABLE) -		return regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); +		return regmap_update_bits(chip->regmap, pull_en_reg, bit, 0);  	else -		return regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); +		return regmap_update_bits(chip->regmap, pull_en_reg, bit, bit);  }  static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, @@ -694,10 +789,10 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)  	gc->direction_input  = pca953x_gpio_direction_input;  	gc->direction_output = pca953x_gpio_direction_output;  	gc->get = pca953x_gpio_get_value; -	gc->set_rv = pca953x_gpio_set_value; +	gc->set = pca953x_gpio_set_value;  	gc->get_direction = pca953x_gpio_get_direction;  	gc->get_multiple = pca953x_gpio_get_multiple; -	gc->set_multiple_rv = pca953x_gpio_set_multiple; +	gc->set_multiple = pca953x_gpio_set_multiple;  	gc->set_config = pca953x_gpio_set_config;  	gc->can_sleep = true; @@ -774,6 +869,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)  	pca953x_read_regs(chip, chip->regs->direction, reg_direction);  	bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); +	bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio); +	bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio);  	bitmap_complement(reg_direction, reg_direction, gc->ngpio);  	bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); @@ -791,13 +888,15 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)  	struct device *dev = &chip->client->dev;  	irq_hw_number_t hwirq = irqd_to_hwirq(d); -	if (!(type & IRQ_TYPE_EDGE_BOTH)) { +	if (!(type & IRQ_TYPE_SENSE_MASK)) {  		dev_err(dev, "irq %d: unsupported type %d\n", d->irq, type);  		return -EINVAL;  	}  	assign_bit(hwirq, chip->irq_trig_fall, type & IRQ_TYPE_EDGE_FALLING);  	assign_bit(hwirq, chip->irq_trig_raise, type & IRQ_TYPE_EDGE_RISING); +	assign_bit(hwirq, chip->irq_trig_level_low, type & IRQ_TYPE_LEVEL_LOW); +	assign_bit(hwirq, chip->irq_trig_level_high, type & IRQ_TYPE_LEVEL_HIGH);  	return 0;  } @@ -810,6 +909,8 @@ static void pca953x_irq_shutdown(struct irq_data *d)  	clear_bit(hwirq, chip->irq_trig_raise);  	clear_bit(hwirq, chip->irq_trig_fall); +	clear_bit(hwirq, chip->irq_trig_level_low); +	clear_bit(hwirq, chip->irq_trig_level_high);  }  static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p) @@ -840,6 +941,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin  	DECLARE_BITMAP(cur_stat, MAX_LINE);  	DECLARE_BITMAP(new_stat, MAX_LINE);  	DECLARE_BITMAP(trigger, MAX_LINE); +	DECLARE_BITMAP(edges, MAX_LINE);  	int ret;  	ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); @@ -857,13 +959,26 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin  	bitmap_copy(chip->irq_stat, new_stat, gc->ngpio); -	if (bitmap_empty(trigger, gc->ngpio)) -		return false; +	if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) && +	    bitmap_empty(chip->irq_trig_level_low, gc->ngpio)) { +		if (bitmap_empty(trigger, gc->ngpio)) +			return false; +	}  	bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio);  	bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio); -	bitmap_or(new_stat, old_stat, cur_stat, gc->ngpio); -	bitmap_and(pending, new_stat, trigger, gc->ngpio); +	bitmap_or(edges, old_stat, cur_stat, gc->ngpio); +	bitmap_and(pending, edges, trigger, gc->ngpio); + +	bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio); +	bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); +	bitmap_or(pending, pending, cur_stat, gc->ngpio); + +	bitmap_complement(cur_stat, new_stat, gc->ngpio); +	bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio); +	bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio); +	bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio); +	bitmap_or(pending, pending, old_stat, gc->ngpio);  	return !bitmap_empty(pending, gc->ngpio);  } @@ -952,7 +1067,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base)  					IRQF_ONESHOT | IRQF_SHARED, dev_name(dev),  					chip);  	if (ret) -		return dev_err_probe(dev, client->irq, "failed to request irq\n"); +		return dev_err_probe(dev, ret, "failed to request irq\n");  	return 0;  } @@ -1095,12 +1210,22 @@ static int pca953x_probe(struct i2c_client *client)  		regmap_config = &pca953x_i2c_regmap;  	} -	if (PCA_CHIP_TYPE(chip->driver_data) == PCAL653X_TYPE) { +	switch (PCA_CHIP_TYPE(chip->driver_data)) { +	case PCAL653X_TYPE:  		chip->recalc_addr = pcal6534_recalc_addr;  		chip->check_reg = pcal6534_check_register; -	} else { +		break; +	case TCA6418_TYPE: +		chip->recalc_addr = tca6418_recalc_addr; +		/* +		 * We don't assign chip->check_reg = tca6418_check_register directly here. +		 * Instead, the wrappers handle the dispatch based on PCA_CHIP_TYPE. +		 */ +		break; +	default:  		chip->recalc_addr = pca953x_recalc_addr;  		chip->check_reg = pca953x_check_register; +		break;  	}  	chip->regmap = devm_regmap_init_i2c(client, regmap_config); @@ -1129,15 +1254,22 @@ static int pca953x_probe(struct i2c_client *client)  	lockdep_set_subclass(&chip->i2c_lock,  			     i2c_adapter_depth(client->adapter)); -	/* initialize cached registers from their original values. +	/* +	 * initialize cached registers from their original values.  	 * we can't share this chip with another i2c master.  	 */ -	if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { +	switch (PCA_CHIP_TYPE(chip->driver_data)) { +	case PCA957X_TYPE:  		chip->regs = &pca957x_regs;  		ret = device_pca957x_init(chip); -	} else { +		break; +	case TCA6418_TYPE: +		chip->regs = &tca6418_regs; +		break; +	default:  		chip->regs = &pca953x_regs;  		ret = device_pca95xx_init(chip); +		break;  	}  	if (ret)  		return ret; @@ -1303,6 +1435,7 @@ static const struct of_device_id pca953x_dt_ids[] = {  	{ .compatible = "ti,pca9536", .data = OF_953X( 4, 0), },  	{ .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), },  	{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, +	{ .compatible = "ti,tca6418", .data = (void *)(18 | TCA6418_TYPE | 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), }, @@ -1333,7 +1466,9 @@ static int __init pca953x_init(void)  {  	return i2c_add_driver(&pca953x_driver);  } -/* register after i2c postcore initcall and before + +/* + * register after i2c postcore initcall and before   * subsys initcalls that may rely on these GPIOs   */  subsys_initcall(pca953x_init); diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index d37ba4049368..c5a1287079a0 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -88,7 +88,7 @@ static int pca9570_get(struct gpio_chip *chip, unsigned offset)  	return !!(buffer & BIT(offset));  } -static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value) +static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct pca9570 *gpio = gpiochip_get_data(chip);  	u8 buffer; @@ -110,6 +110,7 @@ static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value)  out:  	mutex_unlock(&gpio->lock); +	return ret;  }  static int pca9570_probe(struct i2c_client *client) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 2e5f5d7f8865..3b9de8c3d924 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -171,21 +171,24 @@ static int pcf857x_output(struct gpio_chip *chip, unsigned int offset, int value  	return status;  } -static void pcf857x_set(struct gpio_chip *chip, unsigned int offset, int value) +static int pcf857x_set(struct gpio_chip *chip, unsigned int offset, int value)  { -	pcf857x_output(chip, offset, value); +	return pcf857x_output(chip, offset, value);  } -static void pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask, -				 unsigned long *bits) +static int pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask, +				unsigned long *bits)  {  	struct pcf857x *gpio = gpiochip_get_data(chip); +	int status;  	mutex_lock(&gpio->lock);  	gpio->out &= ~*mask;  	gpio->out |= *bits & *mask; -	gpio->write(gpio->client, gpio->out); +	status = gpio->write(gpio->client, gpio->out);  	mutex_unlock(&gpio->lock); + +	return status;  }  /*-------------------------------------------------------------------------*/ diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 63f25c72eac2..9925687e05fb 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -99,7 +99,7 @@ struct pch_gpio {  	spinlock_t spinlock;  }; -static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)  {  	u32 reg_val;  	struct pch_gpio *chip =	gpiochip_get_data(gpio); @@ -114,6 +114,8 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)  	iowrite32(reg_val, &chip->reg->po);  	spin_unlock_irqrestore(&chip->spinlock, flags); + +	return 0;  }  static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index e3013e778e15..a69b74866a13 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -67,13 +67,6 @@ static int pisosr_gpio_direction_input(struct gpio_chip *chip,  	return 0;  } -static int pisosr_gpio_direction_output(struct gpio_chip *chip, -					unsigned offset, int value) -{ -	/* This device is input only */ -	return -EINVAL; -} -  static int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset)  {  	struct pisosr_gpio *gpio = gpiochip_get_data(chip); @@ -108,7 +101,6 @@ static const struct gpio_chip template_chip = {  	.owner			= THIS_MODULE,  	.get_direction		= pisosr_gpio_get_direction,  	.direction_input	= pisosr_gpio_direction_input, -	.direction_output	= pisosr_gpio_direction_output,  	.get			= pisosr_gpio_get,  	.get_multiple		= pisosr_gpio_get_multiple,  	.base			= -1, diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 1c273727ffa3..02e4ffcf5a6f 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -115,11 +115,13 @@ static int pl061_get_value(struct gpio_chip *gc, unsigned offset)  	return !!readb(pl061->base + (BIT(offset + 2)));  } -static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) +static int pl061_set_value(struct gpio_chip *gc, unsigned int offset, int value)  {  	struct pl061 *pl061 = gpiochip_get_data(gc);  	writeb(!!value << offset, pl061->base + (BIT(offset + 2))); + +	return 0;  }  static int pl061_irq_type(struct irq_data *d, unsigned trigger) diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index d9b228bea42e..cb015fb5c946 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -109,12 +109,6 @@ static int sprd_pmic_eic_direction_input(struct gpio_chip *chip,  	return 0;  } -static void sprd_pmic_eic_set(struct gpio_chip *chip, unsigned int offset, -			      int value) -{ -	/* EICs are always input, nothing need to do here. */ -} -  static int sprd_pmic_eic_set_debounce(struct gpio_chip *chip,  				      unsigned int offset,  				      unsigned int debounce) @@ -351,7 +345,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev)  	pmic_eic->chip.request = sprd_pmic_eic_request;  	pmic_eic->chip.free = sprd_pmic_eic_free;  	pmic_eic->chip.set_config = sprd_pmic_eic_set_config; -	pmic_eic->chip.set = sprd_pmic_eic_set;  	pmic_eic->chip.get = sprd_pmic_eic_get;  	pmic_eic->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 91cea97255fa..fa22f3faa163 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -315,12 +315,14 @@ static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset)  	return !!(gplr & GPIO_bit(offset));  } -static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int pxa_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	void __iomem *base = gpio_bank_base(chip, offset);  	writel_relaxed(GPIO_bit(offset),  		       base + (value ? GPSR_OFFSET : GPCR_OFFSET)); + +	return 0;  }  #ifdef CONFIG_OF_GPIO @@ -636,9 +638,8 @@ static int pxa_gpio_probe(struct platform_device *pdev)  	if (!pxa_last_gpio)  		return -EINVAL; -	pchip->irqdomain = irq_domain_add_legacy(pdev->dev.of_node, -						 pxa_last_gpio + 1, irq_base, -						 0, &pxa_irq_domain_ops, pchip); +	pchip->irqdomain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), pxa_last_gpio + 1, +						    irq_base, 0, &pxa_irq_domain_ops, pchip);  	if (!pchip->irqdomain)  		return -ENOMEM; diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c index 9d1b95e429f1..40413e06b69c 100644 --- a/drivers/gpio/gpio-raspberrypi-exp.c +++ b/drivers/gpio/gpio-raspberrypi-exp.c @@ -175,7 +175,7 @@ static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off)  	return !!get.state;  } -static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) +static int rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val)  {  	struct rpi_exp_gpio *gpio;  	struct gpio_get_set_state set; @@ -188,10 +188,14 @@ static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val)  	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_STATE,  					 &set, sizeof(set)); -	if (ret || set.gpio != 0) +	if (ret || set.gpio != 0) {  		dev_err(gc->parent,  			"Failed to set GPIO %u state (%d %x)\n", off, ret,  			set.gpio); +		return ret ? ret : -EIO; +	} + +	return 0;  }  static int rpi_exp_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c index c34dcadaee36..5a69e4534591 100644 --- a/drivers/gpio/gpio-rc5t583.c +++ b/drivers/gpio/gpio-rc5t583.c @@ -35,14 +35,20 @@ static int rc5t583_gpio_get(struct gpio_chip *gc, unsigned int offset)  	return !!(val & BIT(offset));  } -static void rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)  {  	struct rc5t583_gpio *rc5t583_gpio = gpiochip_get_data(gc);  	struct device *parent = rc5t583_gpio->rc5t583->dev; +	int ret; +  	if (val) -		rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); +		ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, +				       BIT(offset));  	else -		rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); +		ret = rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, +					 BIT(offset)); + +	return ret;  }  static int rc5t583_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) @@ -66,7 +72,10 @@ static int rc5t583_gpio_dir_output(struct gpio_chip *gc, unsigned offset,  	struct device *parent = rc5t583_gpio->rc5t583->dev;  	int ret; -	rc5t583_gpio_set(gc, offset, value); +	ret = rc5t583_gpio_set(gc, offset, value); +	if (ret) +		return ret; +  	ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset));  	if (ret < 0)  		return ret; diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 18c965ee02c8..86777e097fd8 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -331,14 +331,11 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)  static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask,  				  unsigned long *bits)  { +	u32 bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0);  	struct gpio_rcar_priv *p = gpiochip_get_data(chip); -	u32 bankmask, outputs, m, val = 0; +	u32 outputs, m, val = 0;  	unsigned long flags; -	bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); -	if (!bankmask) -		return 0; -  	if (p->info.has_always_in) {  		bits[0] = gpio_rcar_read(p, INDT) & bankmask;  		return 0; @@ -359,7 +356,7 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask,  	return 0;  } -static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) +static int gpio_rcar_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct gpio_rcar_priv *p = gpiochip_get_data(chip);  	unsigned long flags; @@ -367,18 +364,17 @@ static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value)  	raw_spin_lock_irqsave(&p->lock, flags);  	gpio_rcar_modify_bit(p, OUTDT, offset, value);  	raw_spin_unlock_irqrestore(&p->lock, flags); + +	return 0;  } -static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, -				   unsigned long *bits) +static int gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, +				  unsigned long *bits)  { +	u32 bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0);  	struct gpio_rcar_priv *p = gpiochip_get_data(chip);  	unsigned long flags; -	u32 val, bankmask; - -	bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); -	if (!bankmask) -		return; +	u32 val;  	raw_spin_lock_irqsave(&p->lock, flags);  	val = gpio_rcar_read(p, OUTDT); @@ -386,6 +382,8 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask,  	val |= (bankmask & bits[0]);  	gpio_rcar_write(p, OUTDT, val);  	raw_spin_unlock_irqrestore(&p->lock, flags); + +	return 0;  }  static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, @@ -594,7 +592,6 @@ static void gpio_rcar_remove(struct platform_device *pdev)  	pm_runtime_disable(&pdev->dev);  } -#ifdef CONFIG_PM_SLEEP  static int gpio_rcar_suspend(struct device *dev)  {  	struct gpio_rcar_priv *p = dev_get_drvdata(dev); @@ -653,16 +650,16 @@ static int gpio_rcar_resume(struct device *dev)  	return 0;  } -#endif /* CONFIG_PM_SLEEP*/ -static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume); +static DEFINE_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		= gpio_rcar_remove,  	.driver		= {  		.name	= "gpio_rcar", -		.pm     = &gpio_rcar_pm_ops, +		.pm     = pm_sleep_ptr(&gpio_rcar_pm_ops),  		.of_match_table = gpio_rcar_of_table,  	}  }; diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index ec7fb9220a47..ba62b81aa8ae 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -64,8 +64,8 @@ static void rdc_gpio_set_value_impl(struct gpio_chip *chip,  }  /* set GPIO pin to value */ -static void rdc_gpio_set_value(struct gpio_chip *chip, -				unsigned gpio, int value) +static int rdc_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, +			      int value)  {  	struct rdc321x_gpio *gpch; @@ -73,6 +73,8 @@ static void rdc_gpio_set_value(struct gpio_chip *chip,  	spin_lock(&gpch->lock);  	rdc_gpio_set_value_impl(chip, gpio, value);  	spin_unlock(&gpch->lock); + +	return 0;  }  static int rdc_gpio_config(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-reg.c b/drivers/gpio/gpio-reg.c index 73c7260d89c0..f2238196faf1 100644 --- a/drivers/gpio/gpio-reg.c +++ b/drivers/gpio/gpio-reg.c @@ -57,7 +57,7 @@ static int gpio_reg_direction_input(struct gpio_chip *gc, unsigned offset)  	return r->direction & BIT(offset) ? 0 : -ENOTSUPP;  } -static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value) +static int gpio_reg_set(struct gpio_chip *gc, unsigned int offset, int value)  {  	struct gpio_reg *r = to_gpio_reg(gc);  	unsigned long flags; @@ -72,6 +72,8 @@ static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value)  	r->out = val;  	writel_relaxed(val, r->reg);  	spin_unlock_irqrestore(&r->lock, flags); + +	return 0;  }  static int gpio_reg_get(struct gpio_chip *gc, unsigned offset) @@ -92,8 +94,8 @@ static int gpio_reg_get(struct gpio_chip *gc, unsigned offset)  	return !!(val & mask);  } -static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, -	unsigned long *bits) +static int gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, +				 unsigned long *bits)  {  	struct gpio_reg *r = to_gpio_reg(gc);  	unsigned long flags; @@ -102,6 +104,8 @@ static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask,  	r->out = (r->out & ~*mask) | (*bits & *mask);  	writel_relaxed(r->out, r->reg);  	spin_unlock_irqrestore(&r->lock, flags); + +	return 0;  }  static int gpio_reg_to_irq(struct gpio_chip *gc, unsigned offset) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 87c4225784cf..e8a32dfebdcb 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -260,9 +260,9 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config  	chip->free = gpiochip_generic_free;  	chip->get = gpio_regmap_get;  	if (gpio->reg_set_base && gpio->reg_clr_base) -		chip->set_rv = gpio_regmap_set_with_clear; +		chip->set = gpio_regmap_set_with_clear;  	else if (gpio->reg_set_base) -		chip->set_rv = gpio_regmap_set; +		chip->set = gpio_regmap_set;  	chip->get_direction = gpio_regmap_get_direction;  	if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) { diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 01a3b3dac58b..bcfc323a8315 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -177,8 +177,8 @@ static int rockchip_gpio_set_direction(struct gpio_chip *chip,  	return 0;  } -static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, -			      int value) +static int rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, +			     int value)  {  	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);  	unsigned long flags; @@ -186,6 +186,8 @@ static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset,  	raw_spin_lock_irqsave(&bank->slock, flags);  	rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr);  	raw_spin_unlock_irqrestore(&bank->slock, flags); + +	return 0;  }  static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset) @@ -521,8 +523,8 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)  	struct irq_chip_generic *gc;  	int ret; -	bank->domain = irq_domain_add_linear(bank->of_node, 32, -					&irq_generic_chip_ops, NULL); +	bank->domain = irq_domain_create_linear(dev_fwnode(bank->dev), 32, &irq_generic_chip_ops, +						NULL);  	if (!bank->domain) {  		dev_warn(bank->dev, "could not init irq domain for bank %s\n",  			 bank->name); diff --git a/drivers/gpio/gpio-rtd.c b/drivers/gpio/gpio-rtd.c index bf7f008f58d7..d46b40dd5283 100644 --- a/drivers/gpio/gpio-rtd.c +++ b/drivers/gpio/gpio-rtd.c @@ -275,7 +275,7 @@ static int rtd_gpio_set_config(struct gpio_chip *chip, unsigned int offset,  	}  } -static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct rtd_gpio *data = gpiochip_get_data(chip);  	u32 mask = BIT(offset % 32); @@ -292,6 +292,8 @@ static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)  	else  		val &= ~mask;  	writel_relaxed(val, data->base + dato_reg_offset); + +	return 0;  }  static int rtd_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 242dad763ac4..7f6a62f5d1ee 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -43,11 +43,14 @@ static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset)  		BIT(offset);  } -static void sa1100_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int sa1100_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	int reg = value ? R_GPSR : R_GPCR;  	writel_relaxed(BIT(offset), sa1100_gpio_chip(chip)->membase + reg); + +	return 0;  }  static int sa1100_get_direction(struct gpio_chip *chip, unsigned offset) @@ -319,7 +322,7 @@ void __init sa1100_init_gpio(void)  	gpiochip_add_data(&sa1100_gpio_chip.chip, NULL); -	sa1100_gpio_irqdomain = irq_domain_add_simple(NULL, +	sa1100_gpio_irqdomain = irq_domain_create_simple(NULL,  			28, IRQ_GPIO0,  			&sa1100_gpio_irqdomain_ops, sgc); diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index d770a6f3d846..5005688f6e67 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -169,15 +169,15 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)  /*   * sama5d2_piobu_set() - gpiochip set   */ -static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, -			      int value) +static int sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, +			     int value)  {  	if (!value)  		value = PIOBU_LOW;  	else  		value = PIOBU_HIGH; -	sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value); +	return sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value);  }  static int sama5d2_piobu_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index ff0341b1222f..966d16a6d515 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -117,7 +117,7 @@ static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num)  	return sch_gpio_reg_get(sch, gpio_num, GLV);  } -static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) +static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)  {  	struct sch_gpio *sch = gpiochip_get_data(gc);  	unsigned long flags; @@ -125,6 +125,8 @@ static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)  	spin_lock_irqsave(&sch->lock, flags);  	sch_gpio_reg_set(sch, gpio_num, GLV, val);  	spin_unlock_irqrestore(&sch->lock, flags); + +	return 0;  }  static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, @@ -146,8 +148,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num,  	 * But we cannot prevent a short low pulse if direction is set to high  	 * and an external pull-up is connected.  	 */ -	sch_gpio_set(gc, gpio_num, val); -	return 0; +	return sch_gpio_set(gc, gpio_num, val);  }  static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index ba4fccf3cc94..f95566998d30 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -178,14 +178,16 @@ static void __sch311x_gpio_set(struct sch311x_gpio_block *block,  	outb(data, block->runtime_reg + block->data_reg);  } -static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset, -			     int value) +static int sch311x_gpio_set(struct gpio_chip *chip, unsigned int offset, +			    int value)  {  	struct sch311x_gpio_block *block = gpiochip_get_data(chip);  	spin_lock(&block->lock);  	__sch311x_gpio_set(block, offset, value);  	spin_unlock(&block->lock); + +	return 0;  }  static int sch311x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index f638219a7c4f..050092583f79 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -39,7 +39,7 @@  #include "dev-sync-probe.h"  #define GPIO_SIM_NGPIO_MAX	1024 -#define GPIO_SIM_PROP_MAX	4 /* Max 3 properties + sentinel. */ +#define GPIO_SIM_PROP_MAX	5 /* Max 4 properties + sentinel. */  #define GPIO_SIM_NUM_ATTRS	3 /* value, pull and sentinel */  static DEFINE_IDA(gpio_sim_ida); @@ -486,9 +486,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)  	gc->parent = dev;  	gc->fwnode = swnode;  	gc->get = gpio_sim_get; -	gc->set_rv = gpio_sim_set; +	gc->set = gpio_sim_set;  	gc->get_multiple = gpio_sim_get_multiple; -	gc->set_multiple_rv = gpio_sim_set_multiple; +	gc->set_multiple = gpio_sim_set_multiple;  	gc->direction_output = gpio_sim_direction_output;  	gc->direction_input = gpio_sim_direction_input;  	gc->get_direction = gpio_sim_get_direction; @@ -629,6 +629,7 @@ struct gpio_sim_line {  	unsigned int offset;  	char *name; +	bool valid;  	/* There can only be one hog per line. */  	struct gpio_sim_hog *hog; @@ -744,6 +745,36 @@ gpio_sim_set_line_names(struct gpio_sim_bank *bank, char **line_names)  	}  } +static unsigned int gpio_sim_get_reserved_ranges_size(struct gpio_sim_bank *bank) +{ +	struct gpio_sim_line *line; +	unsigned int size = 0; + +	list_for_each_entry(line, &bank->line_list, siblings) { +		if (line->valid) +			continue; + +		size += 2; +	} + +	return size; +} + +static void gpio_sim_set_reserved_ranges(struct gpio_sim_bank *bank, +					 u32 *ranges) +{ +	struct gpio_sim_line *line; +	int i = 0; + +	list_for_each_entry(line, &bank->line_list, siblings) { +		if (line->valid) +			continue; + +		ranges[i++] = line->offset; +		ranges[i++] = 1; +	} +} +  static void gpio_sim_remove_hogs(struct gpio_sim_device *dev)  {  	struct gpiod_hog *hog; @@ -844,9 +875,10 @@ static struct fwnode_handle *  gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,  			  struct fwnode_handle *parent)  { +	unsigned int prop_idx = 0, line_names_size, ranges_size;  	struct property_entry properties[GPIO_SIM_PROP_MAX]; -	unsigned int prop_idx = 0, line_names_size;  	char **line_names __free(kfree) = NULL; +	u32 *ranges __free(kfree) = NULL;  	memset(properties, 0, sizeof(properties)); @@ -870,6 +902,19 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,  						line_names, line_names_size);  	} +	ranges_size = gpio_sim_get_reserved_ranges_size(bank); +	if (ranges_size) { +		ranges = kcalloc(ranges_size, sizeof(u32), GFP_KERNEL); +		if (!ranges) +			return ERR_PTR(-ENOMEM); + +		gpio_sim_set_reserved_ranges(bank, ranges); + +		properties[prop_idx++] = PROPERTY_ENTRY_U32_ARRAY_LEN( +						"gpio-reserved-ranges", +						ranges, ranges_size); +	} +  	return fwnode_create_software_node(properties, parent);  } @@ -1189,8 +1234,41 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item,  CONFIGFS_ATTR(gpio_sim_line_config_, name); +static ssize_t +gpio_sim_line_config_valid_show(struct config_item *item, char *page) +{ +	struct gpio_sim_line *line = to_gpio_sim_line(item); +	struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + +	guard(mutex)(&dev->lock); + +	return sprintf(page, "%c\n", line->valid ? '1' : '0'); +} + +static ssize_t gpio_sim_line_config_valid_store(struct config_item *item, +						const char *page, size_t count) +{ +	struct gpio_sim_line *line = to_gpio_sim_line(item); +	struct gpio_sim_device *dev = gpio_sim_line_get_device(line); +	bool valid; +	int ret; + +	ret = kstrtobool(page, &valid); +	if (ret) +		return ret; + +	guard(mutex)(&dev->lock); + +	line->valid = valid; + +	return count; +} + +CONFIGFS_ATTR(gpio_sim_line_config_, valid); +  static struct configfs_attribute *gpio_sim_line_config_attrs[] = {  	&gpio_sim_line_config_attr_name, +	&gpio_sim_line_config_attr_valid,  	NULL  }; @@ -1399,6 +1477,7 @@ gpio_sim_bank_config_make_line_group(struct config_group *group,  	line->parent = bank;  	line->offset = offset; +	line->valid = true;  	list_add_tail(&line->siblings, &bank->line_list);  	return &line->group; diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c index 051bc99bdfb2..958034b9f3f3 100644 --- a/drivers/gpio/gpio-siox.c +++ b/drivers/gpio/gpio-siox.c @@ -160,8 +160,8 @@ static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset)  	return ret;  } -static void gpio_siox_set(struct gpio_chip *chip, -			  unsigned int offset, int value) +static int gpio_siox_set(struct gpio_chip *chip, +			 unsigned int offset, int value)  {  	struct gpio_siox_ddata *ddata = gpiochip_get_data(chip);  	u8 mask = 1 << (19 - offset); @@ -174,6 +174,8 @@ static void gpio_siox_set(struct gpio_chip *chip,  		ddata->setdata[0] &= ~mask;  	mutex_unlock(&ddata->lock); + +	return 0;  }  static int gpio_siox_direction_input(struct gpio_chip *chip, @@ -191,8 +193,7 @@ static int gpio_siox_direction_output(struct gpio_chip *chip,  	if (offset < 12)  		return -EINVAL; -	gpio_siox_set(chip, offset, value); -	return 0; +	return gpio_siox_set(chip, offset, value);  }  static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 8cf3b171c599..969dddd3d6fa 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -306,7 +306,7 @@ static void gpio_la_poll_remove(struct platform_device *pdev)  }  static const struct of_device_id gpio_la_poll_of_match[] = { -	{ .compatible = GPIO_LA_NAME }, +	{ .compatible = "gpio-sloppy-logic-analyzer" },  	{ }  };  MODULE_DEVICE_TABLE(of, gpio_la_poll_of_match); diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index c2a2c76c1652..abd13c79ace0 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -169,8 +169,8 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,  			IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST,  			IRQ_LEVEL | IRQ_NOPROBE); -	sd->id = irq_domain_add_legacy(pdev->dev.of_node, SDV_NUM_PUB_GPIOS, -				sd->irq_base, 0, &irq_domain_sdv_ops, sd); +	sd->id = irq_domain_create_legacy(dev_fwnode(&pdev->dev), SDV_NUM_PUB_GPIOS, sd->irq_base, +					  0, &irq_domain_sdv_ops, sd);  	if (!sd->id)  		return -ENODEV; diff --git a/drivers/gpio/gpio-spacemit-k1.c b/drivers/gpio/gpio-spacemit-k1.c new file mode 100644 index 000000000000..3cc75c701ec4 --- /dev/null +++ b/drivers/gpio/gpio-spacemit-k1.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd + * Copyright (C) 2025 Yixun Lan <dlan@gentoo.org> + */ + +#include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> + +/* register offset */ +#define SPACEMIT_GPLR		0x00 /* port level - R */ +#define SPACEMIT_GPDR		0x0c /* port direction - R/W */ +#define SPACEMIT_GPSR		0x18 /* port set - W */ +#define SPACEMIT_GPCR		0x24 /* port clear - W */ +#define SPACEMIT_GRER		0x30 /* port rising edge R/W */ +#define SPACEMIT_GFER		0x3c /* port falling edge R/W */ +#define SPACEMIT_GEDR		0x48 /* edge detect status - R/W1C */ +#define SPACEMIT_GSDR		0x54 /* (set) direction - W */ +#define SPACEMIT_GCDR		0x60 /* (clear) direction - W */ +#define SPACEMIT_GSRER		0x6c /* (set) rising edge detect enable - W */ +#define SPACEMIT_GCRER		0x78 /* (clear) rising edge detect enable - W */ +#define SPACEMIT_GSFER		0x84 /* (set) falling edge detect enable - W */ +#define SPACEMIT_GCFER		0x90 /* (clear) falling edge detect enable - W */ +#define SPACEMIT_GAPMASK	0x9c /* interrupt mask , 0 disable, 1 enable - R/W */ + +#define SPACEMIT_NR_BANKS		4 +#define SPACEMIT_NR_GPIOS_PER_BANK	32 + +#define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc) + +struct spacemit_gpio; + +struct spacemit_gpio_bank { +	struct gpio_chip gc; +	struct spacemit_gpio *sg; +	void __iomem *base; +	u32 irq_mask; +	u32 irq_rising_edge; +	u32 irq_falling_edge; +}; + +struct spacemit_gpio { +	struct device *dev; +	struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; +}; + +static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) +{ +	return (u32)(gb - gb->sg->sgb); +} + +static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id) +{ +	struct spacemit_gpio_bank *gb = dev_id; +	unsigned long pending; +	u32 n, gedr; + +	gedr = readl(gb->base + SPACEMIT_GEDR); +	if (!gedr) +		return IRQ_NONE; +	writel(gedr, gb->base + SPACEMIT_GEDR); + +	pending = gedr & gb->irq_mask; +	if (!pending) +		return IRQ_NONE; + +	for_each_set_bit(n, &pending, BITS_PER_LONG) +		handle_nested_irq(irq_find_mapping(gb->gc.irq.domain, n)); + +	return IRQ_HANDLED; +} + +static void spacemit_gpio_irq_ack(struct irq_data *d) +{ +	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + +	writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR); +} + +static void spacemit_gpio_irq_mask(struct irq_data *d) +{ +	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); +	u32 bit = BIT(irqd_to_hwirq(d)); + +	gb->irq_mask &= ~bit; +	writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); + +	if (bit & gb->irq_rising_edge) +		writel(bit, gb->base + SPACEMIT_GCRER); + +	if (bit & gb->irq_falling_edge) +		writel(bit, gb->base + SPACEMIT_GCFER); +} + +static void spacemit_gpio_irq_unmask(struct irq_data *d) +{ +	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); +	u32 bit = BIT(irqd_to_hwirq(d)); + +	gb->irq_mask |= bit; + +	if (bit & gb->irq_rising_edge) +		writel(bit, gb->base + SPACEMIT_GSRER); + +	if (bit & gb->irq_falling_edge) +		writel(bit, gb->base + SPACEMIT_GSFER); + +	writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); +} + +static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ +	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); +	u32 bit = BIT(irqd_to_hwirq(d)); + +	if (type & IRQ_TYPE_EDGE_RISING) { +		gb->irq_rising_edge |= bit; +		writel(bit, gb->base + SPACEMIT_GSRER); +	} else { +		gb->irq_rising_edge &= ~bit; +		writel(bit, gb->base + SPACEMIT_GCRER); +	} + +	if (type & IRQ_TYPE_EDGE_FALLING) { +		gb->irq_falling_edge |= bit; +		writel(bit, gb->base + SPACEMIT_GSFER); +	} else { +		gb->irq_falling_edge &= ~bit; +		writel(bit, gb->base + SPACEMIT_GCFER); +	} + +	return 0; +} + +static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) +{ +	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data); + +	seq_printf(p, "%s-%d", dev_name(gb->gc.parent), spacemit_gpio_bank_index(gb)); +} + +static struct irq_chip spacemit_gpio_chip = { +	.name		= "k1-gpio-irqchip", +	.irq_ack	= spacemit_gpio_irq_ack, +	.irq_mask	= spacemit_gpio_irq_mask, +	.irq_unmask	= spacemit_gpio_irq_unmask, +	.irq_set_type	= spacemit_gpio_irq_set_type, +	.irq_print_chip	= spacemit_gpio_irq_print_chip, +	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i) +{ +	struct spacemit_gpio_bank *gb = gpiochip_get_data(gc); +	struct spacemit_gpio *sg = gb->sg; + +	if (i >= SPACEMIT_NR_BANKS) +		return false; + +	return (gc == &sg->sgb[i].gc); +} + +static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, +				  void __iomem *regs, +				  int index, int irq) +{ +	struct spacemit_gpio_bank *gb = &sg->sgb[index]; +	struct gpio_chip *gc = &gb->gc; +	struct device *dev = sg->dev; +	struct gpio_irq_chip *girq; +	void __iomem *dat, *set, *clr, *dirin, *dirout; +	int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 }; + +	gb->base = regs + bank_base[index]; + +	dat	= gb->base + SPACEMIT_GPLR; +	set	= gb->base + SPACEMIT_GPSR; +	clr	= gb->base + SPACEMIT_GPCR; +	dirin	= gb->base + SPACEMIT_GCDR; +	dirout	= gb->base + SPACEMIT_GSDR; + +	/* This registers 32 GPIO lines per bank */ +	ret = bgpio_init(gc, dev, 4, dat, set, clr, dirout, dirin, +			 BGPIOF_UNREADABLE_REG_SET | BGPIOF_UNREADABLE_REG_DIR); +	if (ret) +		return dev_err_probe(dev, ret, "failed to init gpio chip\n"); + +	gb->sg = sg; + +	gc->label		= dev_name(dev); +	gc->request		= gpiochip_generic_request; +	gc->free		= gpiochip_generic_free; +	gc->ngpio		= SPACEMIT_NR_GPIOS_PER_BANK; +	gc->base		= -1; +	gc->of_gpio_n_cells	= 3; +	gc->of_node_instance_match = spacemit_of_node_instance_match; + +	girq			= &gc->irq; +	girq->threaded		= true; +	girq->handler		= handle_simple_irq; + +	gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); + +	/* Disable Interrupt */ +	writel(0, gb->base + SPACEMIT_GAPMASK); +	/* Disable Edge Detection Settings */ +	writel(0x0, gb->base + SPACEMIT_GRER); +	writel(0x0, gb->base + SPACEMIT_GFER); +	/* Clear Interrupt */ +	writel(0xffffffff, gb->base + SPACEMIT_GCRER); +	writel(0xffffffff, gb->base + SPACEMIT_GCFER); + +	ret = devm_request_threaded_irq(dev, irq, NULL, +					spacemit_gpio_irq_handler, +					IRQF_ONESHOT | IRQF_SHARED, +					gb->gc.label, gb); +	if (ret < 0) +		return dev_err_probe(dev, ret, "failed to register IRQ\n"); + +	ret = devm_gpiochip_add_data(dev, gc, gb); +	if (ret) +		return ret; + +	/* Distuingish IRQ domain, for selecting threecells mode */ +	irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED); + +	return 0; +} + +static int spacemit_gpio_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct spacemit_gpio *sg; +	struct clk *core_clk, *bus_clk; +	void __iomem *regs; +	int i, irq, ret; + +	sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL); +	if (!sg) +		return -ENOMEM; + +	regs = devm_platform_ioremap_resource(pdev, 0); +	if (IS_ERR(regs)) +		return PTR_ERR(regs); + +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) +		return irq; + +	sg->dev	= dev; + +	core_clk = devm_clk_get_enabled(dev, "core"); +	if (IS_ERR(core_clk)) +		return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n"); + +	bus_clk = devm_clk_get_enabled(dev, "bus"); +	if (IS_ERR(bus_clk)) +		return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); + +	for (i = 0; i < SPACEMIT_NR_BANKS; i++) { +		ret = spacemit_gpio_add_bank(sg, regs, i, irq); +		if (ret) +			return ret; +	} + +	return 0; +} + +static const struct of_device_id spacemit_gpio_dt_ids[] = { +	{ .compatible = "spacemit,k1-gpio" }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); + +static struct platform_driver spacemit_gpio_driver = { +	.probe		= spacemit_gpio_probe, +	.driver		= { +		.name	= "k1-gpio", +		.of_match_table = spacemit_gpio_dt_ids, +	}, +}; +module_platform_driver(spacemit_gpio_driver); + +MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); +MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c index 51539185400d..96a0e1211500 100644 --- a/drivers/gpio/gpio-spear-spics.c +++ b/drivers/gpio/gpio-spear-spics.c @@ -51,13 +51,8 @@ struct spear_spics {  	struct gpio_chip	chip;  }; -/* gpio framework specific routines */ -static int spics_get_value(struct gpio_chip *chip, unsigned offset) -{ -	return -ENXIO; -} - -static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) +static int spics_set_value(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct spear_spics *spics = gpiochip_get_data(chip);  	u32 tmp; @@ -74,18 +69,14 @@ static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value)  	tmp &= ~(0x1 << spics->cs_value_bit);  	tmp |= value << spics->cs_value_bit;  	writel_relaxed(tmp, spics->base + spics->perip_cfg); -} -static int spics_direction_input(struct gpio_chip *chip, unsigned offset) -{ -	return -ENXIO; +	return 0;  }  static int spics_direction_output(struct gpio_chip *chip, unsigned offset,  		int value)  { -	spics_set_value(chip, offset, value); -	return 0; +	return spics_set_value(chip, offset, value);  }  static int spics_request(struct gpio_chip *chip, unsigned offset) @@ -148,9 +139,7 @@ static int spics_gpio_probe(struct platform_device *pdev)  	spics->chip.base = -1;  	spics->chip.request = spics_request;  	spics->chip.free = spics_free; -	spics->chip.direction_input = spics_direction_input;  	spics->chip.direction_output = spics_direction_output; -	spics->chip.get = spics_get_value;  	spics->chip.set = spics_set_value;  	spics->chip.label = dev_name(&pdev->dev);  	spics->chip.parent = &pdev->dev; diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c index c117c11bfb29..413bcd0a4240 100644 --- a/drivers/gpio/gpio-sprd.c +++ b/drivers/gpio/gpio-sprd.c @@ -108,10 +108,12 @@ static int sprd_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return sprd_gpio_read(chip, offset, SPRD_GPIO_DATA);  } -static void sprd_gpio_set(struct gpio_chip *chip, unsigned int offset, -			  int value) +static int sprd_gpio_set(struct gpio_chip *chip, unsigned int offset, +			 int value)  {  	sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value); + +	return 0;  }  static void sprd_gpio_irq_mask(struct irq_data *data) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dce8ff322e47..5dd4c21a8e60 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -54,7 +54,7 @@ static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)  	return !!(ret & mask);  } -static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int stmpe_gpio_set(struct gpio_chip *chip, unsigned int offset, int val)  {  	struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip);  	struct stmpe *stmpe = stmpe_gpio->stmpe; @@ -67,9 +67,9 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)  	 * For them we need to write 0 to clear and 1 to set.  	 */  	if (stmpe->regs[STMPE_IDX_GPSR_LSB] == stmpe->regs[STMPE_IDX_GPCR_LSB]) -		stmpe_set_bits(stmpe, reg, mask, val ? mask : 0); -	else -		stmpe_reg_write(stmpe, reg, mask); +		return stmpe_set_bits(stmpe, reg, mask, val ? mask : 0); + +	return stmpe_reg_write(stmpe, reg, mask);  }  static int stmpe_gpio_get_direction(struct gpio_chip *chip, @@ -98,8 +98,11 @@ static int stmpe_gpio_direction_output(struct gpio_chip *chip,  	struct stmpe *stmpe = stmpe_gpio->stmpe;  	u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)];  	u8 mask = BIT(offset % 8); +	int ret; -	stmpe_gpio_set(chip, offset, val); +	ret = stmpe_gpio_set(chip, offset, val); +	if (ret) +		return ret;  	return stmpe_set_bits(stmpe, reg, mask, mask);  } diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 5a6406d1f03a..493c027afdd6 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -113,7 +113,7 @@ static int xway_stp_get(struct gpio_chip *gc, unsigned int gpio)   *   * Set the shadow value and call ltq_ebu_apply.   */ -static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val) +static int xway_stp_set(struct gpio_chip *gc, unsigned int gpio, int val)  {  	struct xway_stp *chip = gpiochip_get_data(gc); @@ -124,6 +124,8 @@ static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val)  	xway_stp_w32(chip->virt, chip->shadow, XWAY_STP_CPU0);  	if (!chip->reserved)  		xway_stp_w32_mask(chip->virt, 0, XWAY_STP_CON_SWU, XWAY_STP_CON0); + +	return 0;  }  /** @@ -136,9 +138,7 @@ static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val)   */  static int xway_stp_dir_out(struct gpio_chip *gc, unsigned gpio, int val)  { -	xway_stp_set(gc, gpio, val); - -	return 0; +	return xway_stp_set(gc, gpio, val);  }  /** diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 5ab394ec81e6..40064d4cf47f 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -40,8 +40,8 @@ struct syscon_gpio_data {  	unsigned int	bit_count;  	unsigned int	dat_bit_offset;  	unsigned int	dir_bit_offset; -	void		(*set)(struct gpio_chip *chip, -			       unsigned offset, int value); +	int		(*set)(struct gpio_chip *chip, unsigned int offset, +			       int value);  };  struct syscon_gpio_priv { @@ -68,17 +68,17 @@ static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)  	return !!(val & BIT(offs % SYSCON_REG_BITS));  } -static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int syscon_gpio_set(struct gpio_chip *chip, unsigned int offset, int val)  {  	struct syscon_gpio_priv *priv = gpiochip_get_data(chip);  	unsigned int offs;  	offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; -	regmap_update_bits(priv->syscon, -			   (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, -			   BIT(offs % SYSCON_REG_BITS), -			   val ? BIT(offs % SYSCON_REG_BITS) : 0); +	return regmap_update_bits(priv->syscon, +				  (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, +				  BIT(offs % SYSCON_REG_BITS), +				  val ? BIT(offs % SYSCON_REG_BITS) : 0);  }  static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset) @@ -115,9 +115,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)  				   BIT(offs % SYSCON_REG_BITS));  	} -	chip->set(chip, offset, val); - -	return 0; +	return chip->set(chip, offset, val);  }  static const struct syscon_gpio_data clps711x_mctrl_gpio = { @@ -127,8 +125,8 @@ static const struct syscon_gpio_data clps711x_mctrl_gpio = {  	.dat_bit_offset	= 0x40 * 8 + 8,  }; -static void rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, -			      int val) +static int rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, +			     int val)  {  	struct syscon_gpio_priv *priv = gpiochip_get_data(chip);  	unsigned int offs; @@ -144,6 +142,8 @@ static void rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset,  			   data);  	if (ret < 0)  		dev_err(chip->parent, "gpio write failed ret(%d)\n", ret); + +	return ret;  }  static const struct syscon_gpio_data rockchip_rk3328_gpio_mute = { @@ -156,7 +156,8 @@ static const struct syscon_gpio_data rockchip_rk3328_gpio_mute = {  #define KEYSTONE_LOCK_BIT BIT(0) -static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int keystone_gpio_set(struct gpio_chip *chip, unsigned int offset, +			     int val)  {  	struct syscon_gpio_priv *priv = gpiochip_get_data(chip);  	unsigned int offs; @@ -165,7 +166,7 @@ static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val)  	offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;  	if (!val) -		return; +		return 0;  	ret = regmap_update_bits(  			priv->syscon, @@ -174,6 +175,8 @@ static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val)  			BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT);  	if (ret < 0)  		dev_err(chip->parent, "gpio write failed ret(%d)\n", ret); + +	return ret;  }  static const struct syscon_gpio_data keystone_dsp_gpio = { diff --git a/drivers/gpio/gpio-tangier.c b/drivers/gpio/gpio-tangier.c index a415e6d36173..ba5a8ede8912 100644 --- a/drivers/gpio/gpio-tangier.c +++ b/drivers/gpio/gpio-tangier.c @@ -90,7 +90,7 @@ static int tng_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return !!(readl(gplr) & BIT(shift));  } -static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct tng_gpio *priv = gpiochip_get_data(chip);  	void __iomem *reg; @@ -101,6 +101,8 @@ static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)  	guard(raw_spinlock_irqsave)(&priv->lock);  	writel(BIT(shift), reg); + +	return 0;  }  static int tng_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index b6335cde455f..1869ee7f9423 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -183,9 +183,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev)  		if (ret != 0)  			return ret; -		tb10x_gpio->domain = irq_domain_add_linear(np, -						tb10x_gpio->gc.ngpio, -						&irq_generic_chip_ops, NULL); +		tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev), tb10x_gpio->gc.ngpio, +							      &irq_generic_chip_ops, NULL);  		if (!tb10x_gpio->domain) {  			return -ENOMEM;  		} diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index e62ee7e56908..90d048f9da08 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -49,7 +49,7 @@ static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return !!(ret & mask);  } -static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) +static int tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val)  {  	struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip);  	struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; @@ -57,7 +57,7 @@ static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int va  	unsigned int pos = offset % 8;  	u8 data[] = {val ? BIT(pos) : 0, BIT(pos)}; -	tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); +	return tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data);  }  static int tc3589x_gpio_direction_output(struct gpio_chip *chip, @@ -67,8 +67,11 @@ static int tc3589x_gpio_direction_output(struct gpio_chip *chip,  	struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;  	u8 reg = TC3589x_GPIODIR0 + offset / 8;  	unsigned int pos = offset % 8; +	int ret; -	tc3589x_gpio_set(chip, offset, val); +	ret = tc3589x_gpio_set(chip, offset, val); +	if (ret) +		return ret;  	return tc3589x_set_bits(tc3589x, reg, BIT(pos), BIT(pos));  } diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 9ad286adf263..15a5762a82c2 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -146,12 +146,14 @@ static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset)  	tegra_gpio_disable(tgi, offset);  } -static void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, -			   int value) +static int tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, +			  int value)  {  	struct tegra_gpio_info *tgi = gpiochip_get_data(chip);  	tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value); + +	return 0;  }  static int tegra_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index d27bfac6c9f5..5fd3ec3e2c53 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -202,6 +202,28 @@ static int tegra186_init_valid_mask(struct gpio_chip *chip,  	return 0;  } +static int tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, +			     int level) +{ +	struct tegra_gpio *gpio = gpiochip_get_data(chip); +	void __iomem *base; +	u32 value; + +	base = tegra186_gpio_get_base(gpio, offset); +	if (WARN_ON(base == NULL)) +		return -ENODEV; + +	value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); +	if (level == 0) +		value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; +	else +		value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; + +	writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); + +	return 0; +} +  static int tegra186_gpio_get_direction(struct gpio_chip *chip,  				       unsigned int offset)  { @@ -249,9 +271,12 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip,  	struct tegra_gpio *gpio = gpiochip_get_data(chip);  	void __iomem *base;  	u32 value; +	int ret;  	/* configure output level first */ -	chip->set(chip, offset, level); +	ret = tegra186_gpio_set(chip, offset, level); +	if (ret) +		return ret;  	base = tegra186_gpio_get_base(gpio, offset);  	if (WARN_ON(base == NULL)) @@ -359,26 +384,6 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return value & BIT(0);  } -static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, -			      int level) -{ -	struct tegra_gpio *gpio = gpiochip_get_data(chip); -	void __iomem *base; -	u32 value; - -	base = tegra186_gpio_get_base(gpio, offset); -	if (WARN_ON(base == NULL)) -		return; - -	value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); -	if (level == 0) -		value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; -	else -		value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; - -	writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); -} -  static int tegra186_gpio_set_config(struct gpio_chip *chip,  				    unsigned int offset,  				    unsigned long config) diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 5b851e904c11..be96853063ba 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -116,8 +116,8 @@ static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line)  	return 0;  } -static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, -			      int value) +static int thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, +			     int value)  {  	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);  	int bank = line / 64; @@ -127,6 +127,8 @@ static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line,  		(bank * GPIO_2ND_BANK) + (value ? GPIO_TX_SET : GPIO_TX_CLR);  	writeq(BIT_ULL(bank_bit), reg); + +	return 0;  }  static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, @@ -269,9 +271,9 @@ static int thunderx_gpio_get(struct gpio_chip *chip, unsigned int line)  		return masked_bits != 0;  } -static void thunderx_gpio_set_multiple(struct gpio_chip *chip, -				       unsigned long *mask, -				       unsigned long *bits) +static int thunderx_gpio_set_multiple(struct gpio_chip *chip, +				      unsigned long *mask, +				      unsigned long *bits)  {  	int bank;  	u64 set_bits, clear_bits; @@ -283,6 +285,8 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip,  		writeq(set_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET);  		writeq(clear_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_CLR);  	} + +	return 0;  }  static void thunderx_gpio_irq_ack(struct irq_data *d) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index fad979797486..679e27f00ff6 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -80,10 +80,9 @@ static int timbgpio_gpio_direction_output(struct gpio_chip *gpio,  	return timbgpio_update_bit(gpio, nr, TGPIODIR, false);  } -static void timbgpio_gpio_set(struct gpio_chip *gpio, -				unsigned nr, int val) +static int timbgpio_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)  { -	timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); +	return timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0);  }  static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) @@ -103,20 +102,26 @@ static void timbgpio_irq_disable(struct irq_data *d)  {  	struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);  	int offset = d->irq - tgpio->irq_base; +	irq_hw_number_t hwirq = irqd_to_hwirq(d);  	unsigned long flags;  	spin_lock_irqsave(&tgpio->lock, flags);  	tgpio->last_ier &= ~(1UL << offset);  	iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);  	spin_unlock_irqrestore(&tgpio->lock, flags); + +	gpiochip_disable_irq(&tgpio->gpio, hwirq);  }  static void timbgpio_irq_enable(struct irq_data *d)  {  	struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);  	int offset = d->irq - tgpio->irq_base; +	irq_hw_number_t hwirq = irqd_to_hwirq(d);  	unsigned long flags; +	gpiochip_enable_irq(&tgpio->gpio, hwirq); +  	spin_lock_irqsave(&tgpio->lock, flags);  	tgpio->last_ier |= 1UL << offset;  	iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); @@ -205,11 +210,13 @@ static void timbgpio_irq(struct irq_desc *desc)  	iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);  } -static struct irq_chip timbgpio_irqchip = { +static const struct irq_chip timbgpio_irqchip = {  	.name		= "GPIO",  	.irq_enable	= timbgpio_irq_enable,  	.irq_disable	= timbgpio_irq_disable,  	.irq_set_type	= timbgpio_irq_type, +	.flags = IRQCHIP_IMMUTABLE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS,  };  static int timbgpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index effb7b8ff81f..866ff2d436d5 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -25,7 +25,7 @@ struct tpic2810 {  	struct mutex lock;  }; -static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value); +static int tpic2810_set(struct gpio_chip *chip, unsigned int offset, int value);  static int tpic2810_get_direction(struct gpio_chip *chip,  				  unsigned offset) @@ -34,19 +34,11 @@ static int tpic2810_get_direction(struct gpio_chip *chip,  	return GPIO_LINE_DIRECTION_OUT;  } -static int tpic2810_direction_input(struct gpio_chip *chip, -				    unsigned offset) -{ -	/* This device is output only */ -	return -EINVAL; -} -  static int tpic2810_direction_output(struct gpio_chip *chip,  				     unsigned offset, int value)  {  	/* This device always output */ -	tpic2810_set(chip, offset, value); -	return 0; +	return tpic2810_set(chip, offset, value);  }  static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) @@ -68,22 +60,25 @@ static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits)  	mutex_unlock(&gpio->lock);  } -static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value) +static int tpic2810_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	tpic2810_set_mask_bits(chip, BIT(offset), value ? BIT(offset) : 0); + +	return 0;  } -static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, -				  unsigned long *bits) +static int tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, +				 unsigned long *bits)  {  	tpic2810_set_mask_bits(chip, *mask, *bits); + +	return 0;  }  static const struct gpio_chip template_chip = {  	.label			= "tpic2810",  	.owner			= THIS_MODULE,  	.get_direction		= tpic2810_get_direction, -	.direction_input	= tpic2810_direction_input,  	.direction_output	= tpic2810_direction_output,  	.set			= tpic2810_set,  	.set_multiple		= tpic2810_set_multiple, diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 8f5827554e1e..84b17b83476f 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -37,10 +37,8 @@ static int tps65086_gpio_direction_output(struct gpio_chip *chip,  	struct tps65086_gpio *gpio = gpiochip_get_data(chip);  	/* Set the initial value */ -	regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, -			   BIT(4 + offset), value ? BIT(4 + offset) : 0); - -	return 0; +	return regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, +				  BIT(4 + offset), value ? BIT(4 + offset) : 0);  }  static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -55,13 +53,13 @@ static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset)  	return val & BIT(4 + offset);  } -static void tps65086_gpio_set(struct gpio_chip *chip, unsigned offset, -			      int value) +static int tps65086_gpio_set(struct gpio_chip *chip, unsigned int offset, +			     int value)  {  	struct tps65086_gpio *gpio = gpiochip_get_data(chip); -	regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, -			   BIT(4 + offset), value ? BIT(4 + offset) : 0); +	return regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, +				  BIT(4 + offset), value ? BIT(4 + offset) : 0);  }  static const struct gpio_chip template_chip = { diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index d7d9d50dcddf..3b4c41f5ef55 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -34,34 +34,28 @@ static int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset)  	return !!(val & (TPS65218_ENABLE2_GPIO1 << offset));  } -static void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset, -			      int value) +static int tps65218_gpio_set(struct gpio_chip *gc, unsigned int offset, +			     int value)  {  	struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);  	struct tps65218 *tps65218 = tps65218_gpio->tps65218;  	if (value) -		tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, -				  TPS65218_ENABLE2_GPIO1 << offset, -				  TPS65218_ENABLE2_GPIO1 << offset, -				  TPS65218_PROTECT_L1); -	else -		tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, -				    TPS65218_ENABLE2_GPIO1 << offset, -				    TPS65218_PROTECT_L1); +		return tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, +					 TPS65218_ENABLE2_GPIO1 << offset, +					 TPS65218_ENABLE2_GPIO1 << offset, +					 TPS65218_PROTECT_L1); + +	return tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, +				   TPS65218_ENABLE2_GPIO1 << offset, +				   TPS65218_PROTECT_L1);  }  static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset,  				int value)  {  	/* Only drives GPOs */ -	tps65218_gpio_set(gc, offset, value); -	return 0; -} - -static int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset) -{ -	return -EPERM; +	return tps65218_gpio_set(gc, offset, value);  }  static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) @@ -174,7 +168,6 @@ static const struct gpio_chip template_chip = {  	.owner			= THIS_MODULE,  	.request		= tps65218_gpio_request,  	.direction_output	= tps65218_gpio_output, -	.direction_input	= tps65218_gpio_input,  	.get			= tps65218_gpio_get,  	.set			= tps65218_gpio_set,  	.set_config		= tps65218_gpio_set_config, diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 526640c39a11..158f63bcf10c 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -1,8 +1,8 @@  // SPDX-License-Identifier: GPL-2.0  /* - * GPIO driver for TI TPS65219 PMICs + * GPIO driver for TI TPS65214/TPS65215/TPS65219 PMICs   * - * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2022, 2025 Texas Instruments Incorporated - http://www.ti.com/   */  #include <linux/bits.h> @@ -13,20 +13,48 @@  #include <linux/regmap.h>  #define TPS65219_GPIO0_DIR_MASK		BIT(3) -#define TPS65219_GPIO0_OFFSET		2 -#define TPS65219_GPIO0_IDX		0 +#define TPS65214_GPIO0_DIR_MASK		BIT(1) +#define TPS6521X_GPIO0_OFFSET		2 +#define TPS6521X_GPIO0_IDX		0 + +/* + * TPS65214 GPIO mapping + * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2 + * Linux gpio offset 1 -> GPO1 (pin9 ) -> bit_offset 0 + * + * TPS65215 & TPS65219 GPIO mapping + * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2 + * Linux gpio offset 1 -> GPO1 (pin8 ) -> bit_offset 0 + * Linux gpio offset 2 -> GPO2 (pin17) -> bit_offset 1 + */  struct tps65219_gpio { +	int (*change_dir)(struct gpio_chip *gc, unsigned int offset, unsigned int dir);  	struct gpio_chip gpio_chip;  	struct tps65219 *tps;  }; +static int tps65214_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ +	struct tps65219_gpio *gpio = gpiochip_get_data(gc); +	int ret, val; + +	if (offset != TPS6521X_GPIO0_IDX) +		return GPIO_LINE_DIRECTION_OUT; + +	ret = regmap_read(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, &val); +	if (ret) +		return ret; + +	return !(val & TPS65214_GPIO0_DIR_MASK); +} +  static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)  {  	struct tps65219_gpio *gpio = gpiochip_get_data(gc);  	int ret, val; -	if (offset != TPS65219_GPIO0_IDX) +	if (offset != TPS6521X_GPIO0_IDX)  		return GPIO_LINE_DIRECTION_OUT;  	ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); @@ -42,7 +70,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)  	struct device *dev = gpio->tps->dev;  	int ret, val; -	if (offset != TPS65219_GPIO0_IDX) { +	if (offset != TPS6521X_GPIO0_IDX) {  		dev_err(dev, "GPIO%d is output only, cannot get\n", offset);  		return -ENOTSUPP;  	} @@ -65,19 +93,18 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)  	return ret;  } -static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)  {  	struct tps65219_gpio *gpio = gpiochip_get_data(gc); -	struct device *dev = gpio->tps->dev;  	int v, mask, bit; -	bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1; +	bit = (offset == TPS6521X_GPIO0_IDX) ? TPS6521X_GPIO0_OFFSET : offset - 1;  	mask = BIT(bit);  	v = value ? mask : 0; -	if (regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, mask, v)) -		dev_err(dev, "GPIO%d, set to value %d failed.\n", offset, value); +	return regmap_update_bits(gpio->tps->regmap, +				  TPS65219_REG_GENERAL_CONFIG, mask, v);  }  static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, @@ -112,12 +139,39 @@ static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int off  	return -ENOTSUPP;  } +static int tps65214_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, +					  unsigned int direction) +{ +	struct tps65219_gpio *gpio = gpiochip_get_data(gc); +	struct device *dev = gpio->tps->dev; +	int val, ret; + +	/** +	 * Verified if GPIO or GPO in parent function +	 * Masked value: 0 = GPIO, 1 = VSEL +	 */ +	ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); +	if (ret) +		return ret; + +	ret = !!(val & BIT(TPS65219_GPIO0_DIR_MASK)); +	if (ret) +		dev_err(dev, "GPIO%d configured as VSEL, not GPIO\n", offset); + +	ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, +				 TPS65214_GPIO0_DIR_MASK, direction); +	if (ret) +		dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset); + +	return ret; +} +  static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)  {  	struct tps65219_gpio *gpio = gpiochip_get_data(gc);  	struct device *dev = gpio->tps->dev; -	if (offset != TPS65219_GPIO0_IDX) { +	if (offset != TPS6521X_GPIO0_IDX) {  		dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset);  		return -ENOTSUPP;  	} @@ -125,21 +179,36 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs  	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)  		return 0; -	return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_IN); +	return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN);  }  static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)  { +	struct tps65219_gpio *gpio = gpiochip_get_data(gc); +  	tps65219_gpio_set(gc, offset, value); -	if (offset != TPS65219_GPIO0_IDX) +	if (offset != TPS6521X_GPIO0_IDX)  		return 0;  	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)  		return 0; -	return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_OUT); +	return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT);  } +static const struct gpio_chip tps65214_template_chip = { +	.label			= "tps65214-gpio", +	.owner			= THIS_MODULE, +	.get_direction		= tps65214_gpio_get_direction, +	.direction_input	= tps65219_gpio_direction_input, +	.direction_output	= tps65219_gpio_direction_output, +	.get			= tps65219_gpio_get, +	.set			= tps65219_gpio_set, +	.base			= -1, +	.ngpio			= 2, +	.can_sleep		= true, +}; +  static const struct gpio_chip tps65219_template_chip = {  	.label			= "tps65219-gpio",  	.owner			= THIS_MODULE, @@ -155,6 +224,7 @@ static const struct gpio_chip tps65219_template_chip = {  static int tps65219_gpio_probe(struct platform_device *pdev)  { +	enum pmic_id chip = platform_get_device_id(pdev)->driver_data;  	struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);  	struct tps65219_gpio *gpio; @@ -162,22 +232,38 @@ static int tps65219_gpio_probe(struct platform_device *pdev)  	if (!gpio)  		return -ENOMEM; +	if (chip == TPS65214) { +		gpio->gpio_chip = tps65214_template_chip; +		gpio->change_dir = tps65214_gpio_change_direction; +	} else if (chip == TPS65219) { +		gpio->gpio_chip = tps65219_template_chip; +		gpio->change_dir = tps65219_gpio_change_direction; +	} else { +		return -ENODATA; +	} +  	gpio->tps = tps; -	gpio->gpio_chip = tps65219_template_chip;  	gpio->gpio_chip.parent = tps->dev;  	return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio);  } +static const struct platform_device_id tps6521x_gpio_id_table[] = { +	{ "tps65214-gpio", TPS65214 }, +	{ "tps65219-gpio", TPS65219 }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table); +  static struct platform_driver tps65219_gpio_driver = {  	.driver = {  		.name = "tps65219-gpio",  	},  	.probe = tps65219_gpio_probe, +	.id_table = tps6521x_gpio_id_table,  };  module_platform_driver(tps65219_gpio_driver); -MODULE_ALIAS("platform:tps65219-gpio");  MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>"); -MODULE_DESCRIPTION("TPS65219 GPIO driver"); +MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 GPIO driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index d277aa951143..aaacbb54bf5d 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -40,13 +40,13 @@ static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)  	return !!(val & (1 << offset));  } -static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset, -			      int value) +static int tps6586x_gpio_set(struct gpio_chip *gc, unsigned int offset, +			     int value)  {  	struct tps6586x_gpio *tps6586x_gpio = gpiochip_get_data(gc); -	tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2, -			value << offset, 1 << offset); +	return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2, +			       value << offset, 1 << offset);  }  static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -54,8 +54,11 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,  {  	struct tps6586x_gpio *tps6586x_gpio = gpiochip_get_data(gc);  	uint8_t val, mask; +	int ret; -	tps6586x_gpio_set(gc, offset, value); +	ret = tps6586x_gpio_set(gc, offset, value); +	if (ret) +		return ret;  	val = 0x1 << (offset * 2);  	mask = 0x3 << (offset * 2); diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 187d21580573..25e9f41efe78 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -36,18 +36,18 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)  	return 0;  } -static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, -			      int value) +static int tps65910_gpio_set(struct gpio_chip *gc, unsigned int offset, +			     int value)  {  	struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc);  	struct tps65910 *tps65910 = tps65910_gpio->tps65910;  	if (value) -		regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset, -						GPIO_SET_MASK); -	else -		regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset, -						GPIO_SET_MASK); +		return regmap_set_bits(tps65910->regmap, +				       TPS65910_GPIO0 + offset, GPIO_SET_MASK); + +	return regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset, +				 GPIO_SET_MASK);  }  static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -55,9 +55,12 @@ static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset,  {  	struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc);  	struct tps65910 *tps65910 = tps65910_gpio->tps65910; +	int ret;  	/* Set the initial value */ -	tps65910_gpio_set(gc, offset, value); +	ret = tps65910_gpio_set(gc, offset, value); +	if (ret) +		return ret;  	return regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset,  						GPIO_CFG_MASK); diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index fab771cb6a87..7a2c5685c2fd 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -49,10 +49,13 @@ static int tps65912_gpio_direction_output(struct gpio_chip *gc,  					  unsigned offset, int value)  {  	struct tps65912_gpio *gpio = gpiochip_get_data(gc); +	int ret;  	/* Set the initial value */ -	regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, -			   GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); +	ret = regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, +				 GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); +	if (ret) +		return ret;  	return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset,  				  GPIO_CFG_MASK, GPIO_CFG_MASK); @@ -73,13 +76,13 @@ static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)  	return 0;  } -static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, -			      int value) +static int tps65912_gpio_set(struct gpio_chip *gc, unsigned int offset, +			     int value)  {  	struct tps65912_gpio *gpio = gpiochip_get_data(gc); -	regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, -			   GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); +	return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, +				  GPIO_SET_MASK, value ? GPIO_SET_MASK : 0);  }  static const struct gpio_chip template_chip = { diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c index 532deaddfd4e..d4fbdf90e190 100644 --- a/drivers/gpio/gpio-tps68470.c +++ b/drivers/gpio/gpio-tps68470.c @@ -70,8 +70,8 @@ static int tps68470_gpio_get_direction(struct gpio_chip *gc,  						    GPIO_LINE_DIRECTION_IN;  } -static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, -				int value) +static int tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, +			     int value)  {  	struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc);  	struct regmap *regmap = tps68470_gpio->tps68470_regmap; @@ -82,7 +82,8 @@ static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset,  		offset -= TPS68470_N_REGULAR_GPIO;  	} -	regmap_update_bits(regmap, reg, BIT(offset), value ? BIT(offset) : 0); +	return regmap_update_bits(regmap, reg, BIT(offset), +				  value ? BIT(offset) : 0);  }  static int tps68470_gpio_output(struct gpio_chip *gc, unsigned int offset, @@ -90,9 +91,12 @@ static int tps68470_gpio_output(struct gpio_chip *gc, unsigned int offset,  {  	struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc);  	struct regmap *regmap = tps68470_gpio->tps68470_regmap; +	int ret;  	/* Set the initial value */ -	tps68470_gpio_set(gc, offset, value); +	ret = tps68470_gpio_set(gc, offset, value); +	if (ret) +		return ret;  	/* rest are always outputs */  	if (offset >= TPS68470_N_REGULAR_GPIO) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 18f523a15b3c..27dd09273292 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -93,14 +93,16 @@ static void _tqmx86_gpio_set(struct tqmx86_gpio_data *gpio, unsigned int offset,  	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) +static int tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);  	guard(raw_spinlock_irqsave)(&gpio->spinlock);  	_tqmx86_gpio_set(gpio, offset, value); + +	return 0;  }  static int tqmx86_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index 5c806140fdf0..d9ee8fc77ccd 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -95,16 +95,16 @@ static int ts4900_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return !!(reg & priv->input_bit);  } -static void ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, -			    int value) +static int ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);  	if (value) -		regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, -				   TS4900_GPIO_OUT); -	else -		regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0); +		return regmap_update_bits(priv->regmap, offset, +					  TS4900_GPIO_OUT, TS4900_GPIO_OUT); + +	return regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0);  }  static const struct regmap_config ts4900_regmap_config = { diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index 61cbec5c06a7..3c7f2efe10fd 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -244,7 +244,7 @@ static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)  	return 0;  } -static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)  {  	struct ts5500_priv *priv = gpiochip_get_data(chip);  	const struct ts5500_dio line = priv->pinout[offset]; @@ -256,6 +256,8 @@ static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)  	else  		ts5500_clear_mask(line.value_mask, line.value_addr);  	spin_unlock_irqrestore(&priv->lock, flags); + +	return 0;  }  static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index bcd692229c7c..a33dc7c7e7a0 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -120,7 +120,7 @@ static u8 cached_leden;   * external pullup is needed.  We could also expose the integrated PWM   * as a LED brightness control; we initialize it as "always on".   */ -static void twl4030_led_set_value(int led, int value) +static int twl4030_led_set_value(int led, int value)  {  	u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM; @@ -132,8 +132,8 @@ static void twl4030_led_set_value(int led, int value)  	else  		cached_leden |= mask; -	WARN_ON_ONCE(twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, -				      TWL4030_LED_LEDEN_REG)); +	return twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, +				TWL4030_LED_LEDEN_REG);  }  static int twl4030_set_gpio_direction(int gpio, int is_input) @@ -278,7 +278,7 @@ static void twl_free(struct gpio_chip *chip, unsigned offset)  	mutex_lock(&priv->mutex);  	if (offset >= TWL4030_GPIO_MAX) { -		twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); +		WARN_ON_ONCE(twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1));  		goto out;  	} @@ -334,15 +334,16 @@ out:  	return ret;  } -static void twl_set(struct gpio_chip *chip, unsigned offset, int value) +static int twl_set(struct gpio_chip *chip, unsigned int offset, int value)  {  	struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); +	int ret;  	mutex_lock(&priv->mutex);  	if (offset < TWL4030_GPIO_MAX) -		twl4030_set_gpio_dataout(offset, value); +		ret = twl4030_set_gpio_dataout(offset, value);  	else -		twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); +		ret = twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value);  	if (value)  		priv->out_state |= BIT(offset); @@ -350,6 +351,8 @@ static void twl_set(struct gpio_chip *chip, unsigned offset, int value)  		priv->out_state &= ~BIT(offset);  	mutex_unlock(&priv->mutex); + +	return ret;  }  static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) @@ -373,9 +376,7 @@ static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)  	priv->direction |= BIT(offset);  	mutex_unlock(&priv->mutex); -	twl_set(chip, offset, value); - -	return ret; +	return twl_set(chip, offset, value);  }  static int twl_get_direction(struct gpio_chip *chip, unsigned offset) @@ -502,7 +503,6 @@ static void gpio_twl4030_power_off_action(void *data)  static int gpio_twl4030_probe(struct platform_device *pdev)  {  	struct twl4030_gpio_platform_data *pdata; -	struct device_node *node = pdev->dev.of_node;  	struct gpio_twl4030_priv *priv;  	int ret, irq_base; @@ -524,8 +524,8 @@ static int gpio_twl4030_probe(struct platform_device *pdev)  		return irq_base;  	} -	irq_domain_add_legacy(node, TWL4030_GPIO_MAX, irq_base, 0, -			      &irq_domain_simple_ops, NULL); +	irq_domain_create_legacy(dev_fwnode(&pdev->dev), TWL4030_GPIO_MAX, irq_base, 0, +				 &irq_domain_simple_ops, NULL);  	ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base);  	if (ret < 0) diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index b9171bf66168..4ec9bcd40439 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -37,14 +37,8 @@ static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset)  	return GPIO_LINE_DIRECTION_OUT;  } -static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, -				    int value) -{ -	/* This only drives GPOs, and can't change direction */ -	return 0; -} - -static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) +static int twl6040gpo_set(struct gpio_chip *chip, unsigned int offset, +			  int value)  {  	struct twl6040 *twl6040 = gpiochip_get_data(chip);  	int ret; @@ -52,14 +46,21 @@ static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value)  	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);  	if (ret < 0) -		return; +		return ret;  	if (value)  		gpoctl = ret | BIT(offset);  	else  		gpoctl = ret & ~BIT(offset); -	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); +	return twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); +} + +static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned int offset, +				    int value) +{ +	/* This only drives GPOs, and can't change direction */ +	return twl6040gpo_set(chip, offset, value);  }  static struct gpio_chip twl6040gpo_chip = { diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index d738da8718f9..197bb1d22b3c 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -138,14 +138,16 @@ static int uniphier_gpio_get(struct gpio_chip *chip, unsigned int offset)  	return uniphier_gpio_offset_read(chip, offset, UNIPHIER_GPIO_PORT_DATA);  } -static void uniphier_gpio_set(struct gpio_chip *chip, -			      unsigned int offset, int val) +static int uniphier_gpio_set(struct gpio_chip *chip, +			     unsigned int offset, int val)  {  	uniphier_gpio_offset_write(chip, offset, UNIPHIER_GPIO_PORT_DATA, val); + +	return 0;  } -static void uniphier_gpio_set_multiple(struct gpio_chip *chip, -				       unsigned long *mask, unsigned long *bits) +static int uniphier_gpio_set_multiple(struct gpio_chip *chip, +				      unsigned long *mask, unsigned long *bits)  {  	unsigned long i, bank, bank_mask, bank_bits; @@ -156,6 +158,8 @@ static void uniphier_gpio_set_multiple(struct gpio_chip *chip,  		uniphier_gpio_bank_write(chip, bank, UNIPHIER_GPIO_PORT_DATA,  					 bank_mask, bank_bits);  	} + +	return 0;  }  static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 4dad7ce0c4dc..7de0d5b53d56 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -345,4 +345,6 @@ static struct platform_driver vf610_gpio_driver = {  	.probe		= vf610_gpio_probe,  }; -builtin_platform_driver(vf610_gpio_driver); +module_platform_driver(vf610_gpio_driver); +MODULE_DESCRIPTION("VF610 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index e55d28a8a66f..15e495c109d2 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -128,45 +128,50 @@ static int vprbrd_gpioa_get(struct gpio_chip *chip,  	return answer;  } -static void vprbrd_gpioa_set(struct gpio_chip *chip, -		unsigned int offset, int value) +static int vprbrd_gpioa_set(struct gpio_chip *chip, unsigned int offset, +			    int value)  { -	int ret; +	int ret = 0;  	struct vprbrd_gpio *gpio = gpiochip_get_data(chip);  	struct vprbrd *vb = gpio->vb;  	struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; -	if (gpio->gpioa_out & (1 << offset)) { -		if (value) -			gpio->gpioa_val |= (1 << offset); -		else -			gpio->gpioa_val &= ~(1 << offset); - -		mutex_lock(&vb->lock); - -		gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; -		gamsg->clk = 0x00; -		gamsg->offset = offset; -		gamsg->t1 = 0x00; -		gamsg->t2 = 0x00; -		gamsg->invert = 0x00; -		gamsg->pwmlevel = 0x00; -		gamsg->outval = value; -		gamsg->risefall = 0x00; -		gamsg->answer = 0x00; -		gamsg->__fill = 0x00; - -		ret = usb_control_msg(vb->usb_dev, -			usb_sndctrlpipe(vb->usb_dev, 0), -			VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, -			0x0000,	0x0000, gamsg, -			sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS); - -		mutex_unlock(&vb->lock); - -		if (ret != sizeof(struct vprbrd_gpioa_msg)) -			dev_err(chip->parent, "usb error setting pin value\n"); +	if (!(gpio->gpioa_out & (1 << offset))) +		return 0; + +	if (value) +		gpio->gpioa_val |= (1 << offset); +	else +		gpio->gpioa_val &= ~(1 << offset); + +	mutex_lock(&vb->lock); + +	gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; +	gamsg->clk = 0x00; +	gamsg->offset = offset; +	gamsg->t1 = 0x00; +	gamsg->t2 = 0x00; +	gamsg->invert = 0x00; +	gamsg->pwmlevel = 0x00; +	gamsg->outval = value; +	gamsg->risefall = 0x00; +	gamsg->answer = 0x00; +	gamsg->__fill = 0x00; + +	ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), +			      VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, +			      0x0000, 0x0000, gamsg, +			      sizeof(struct vprbrd_gpioa_msg), +			      VPRBRD_USB_TIMEOUT_MS); + +	mutex_unlock(&vb->lock); + +	if (ret != sizeof(struct vprbrd_gpioa_msg)) { +		dev_err(chip->parent, "usb error setting pin value\n"); +		return -EREMOTEIO;  	} + +	return 0;  }  static int vprbrd_gpioa_direction_input(struct gpio_chip *chip, @@ -304,37 +309,42 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip,  	return (gpio->gpiob_val >> offset) & 0x1;  } -static void vprbrd_gpiob_set(struct gpio_chip *chip, -		unsigned int offset, int value) +static int vprbrd_gpiob_set(struct gpio_chip *chip, unsigned int offset, +			    int value)  {  	int ret;  	struct vprbrd_gpio *gpio = gpiochip_get_data(chip);  	struct vprbrd *vb = gpio->vb;  	struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; -	if (gpio->gpiob_out & (1 << offset)) { -		if (value) -			gpio->gpiob_val |= (1 << offset); -		else -			gpio->gpiob_val &= ~(1 << offset); +	if (!(gpio->gpiob_out & (1 << offset))) +		return 0; + +	if (value) +		gpio->gpiob_val |= (1 << offset); +	else +		gpio->gpiob_val &= ~(1 << offset); -		mutex_lock(&vb->lock); +	mutex_lock(&vb->lock); -		gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; -		gbmsg->val = cpu_to_be16(value << offset); -		gbmsg->mask = cpu_to_be16(0x0001 << offset); +	gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; +	gbmsg->val = cpu_to_be16(value << offset); +	gbmsg->mask = cpu_to_be16(0x0001 << offset); -		ret = usb_control_msg(vb->usb_dev, -			usb_sndctrlpipe(vb->usb_dev, 0), -			VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, -			0x0000,	0x0000, gbmsg, -			sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS); +	ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), +			      VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, +			      0x0000, 0x0000, gbmsg, +			      sizeof(struct vprbrd_gpiob_msg), +			      VPRBRD_USB_TIMEOUT_MS); -		mutex_unlock(&vb->lock); +	mutex_unlock(&vb->lock); -		if (ret != sizeof(struct vprbrd_gpiob_msg)) -			dev_err(chip->parent, "usb error setting pin value\n"); +	if (ret != sizeof(struct vprbrd_gpiob_msg)) { +		dev_err(chip->parent, "usb error setting pin value\n"); +		return -EREMOTEIO;  	} + +	return 0;  }  static int vprbrd_gpiob_direction_input(struct gpio_chip *chip, @@ -368,16 +378,14 @@ static int vprbrd_gpiob_direction_output(struct gpio_chip *chip,  	gpio->gpiob_out |= (1 << offset);  	mutex_lock(&vb->lock); -  	ret = vprbrd_gpiob_setdir(vb, offset, 1); -	if (ret) -		dev_err(chip->parent, "usb error setting pin to output\n"); -  	mutex_unlock(&vb->lock); +	if (ret) { +		dev_err(chip->parent, "usb error setting pin to output\n"); +		return ret; +	} -	vprbrd_gpiob_set(chip, offset, value); - -	return ret; +	return vprbrd_gpiob_set(chip, offset, value);  }  /* ----- end of gpio b chip ---------------------------------------------- */ diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c index ac39da17a29b..17e040991e46 100644 --- a/drivers/gpio/gpio-virtio.c +++ b/drivers/gpio/gpio-virtio.c @@ -194,11 +194,12 @@ static int virtio_gpio_get(struct gpio_chip *gc, unsigned int gpio)  	return ret ? ret : value;  } -static void virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +static int virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)  {  	struct virtio_gpio *vgpio = gpiochip_get_data(gc); -	virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, NULL); +	return virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, +			       NULL);  }  /* Interrupt handling */ @@ -526,7 +527,6 @@ static const char **virtio_gpio_get_names(struct virtio_gpio *vgpio,  static int virtio_gpio_probe(struct virtio_device *vdev)  { -	struct virtio_gpio_config config;  	struct device *dev = &vdev->dev;  	struct virtio_gpio *vgpio;  	struct irq_chip *gpio_irq_chip; @@ -539,9 +539,11 @@ static int virtio_gpio_probe(struct virtio_device *vdev)  		return -ENOMEM;  	/* Read configuration */ -	virtio_cread_bytes(vdev, 0, &config, sizeof(config)); -	gpio_names_size = le32_to_cpu(config.gpio_names_size); -	ngpio = le16_to_cpu(config.ngpio); +	gpio_names_size = +		virtio_cread32(vdev, offsetof(struct virtio_gpio_config, +					      gpio_names_size)); +	ngpio =  virtio_cread16(vdev, offsetof(struct virtio_gpio_config, +					       ngpio));  	if (!ngpio) {  		dev_err(dev, "Number of GPIOs can't be zero\n");  		return -EINVAL; diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index eab6726953b4..a10eab7d2617 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -215,9 +215,7 @@ static int gpio_virtuser_set_array_value(struct gpio_descs *descs,  	struct gpio_virtuser_irq_work_context ctx;  	if (!atomic) -		return gpiod_set_array_value_cansleep(descs->ndescs, -						      descs->desc, -						      descs->info, values); +		return gpiod_multi_set_value_cansleep(descs, values);  	gpio_virtuser_init_irq_work_context(&ctx);  	ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_set_value_array_atomic); diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 8fd6c3913d69..84b3a973a503 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -127,8 +127,7 @@ static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr)  	return ret;  } -static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, -			  int val) +static int vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)  {  	struct vx855_gpio *vg = gpiochip_get_data(gpio);  	unsigned long flags; @@ -136,7 +135,7 @@ static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr,  	/* True GPI cannot be switched to output mode */  	if (nr < NR_VX855_GPI) -		return; +		return -EPERM;  	spin_lock_irqsave(&vg->lock, flags);  	reg_out = inl(vg->io_gpo); @@ -153,6 +152,8 @@ static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr,  	}  	outl(reg_out, vg->io_gpo);  	spin_unlock_irqrestore(&vg->lock, flags); + +	return 0;  }  static int vx855gpio_direction_output(struct gpio_chip *gpio, diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c index 2bba27b13947..4af504c23e6f 100644 --- a/drivers/gpio/gpio-wcd934x.c +++ b/drivers/gpio/gpio-wcd934x.c @@ -46,9 +46,12 @@ static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,  				     int val)  {  	struct wcd_gpio_data *data = gpiochip_get_data(chip); +	int ret; -	regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, -			   WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); +	ret = regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, +				 WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); +	if (ret) +		return ret;  	return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET,  				  WCD_PIN_MASK(pin), @@ -65,12 +68,13 @@ static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)  	return !!(value & WCD_PIN_MASK(pin));  } -static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) +static int wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val)  {  	struct wcd_gpio_data *data = gpiochip_get_data(chip); -	regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, -			   WCD_PIN_MASK(pin), val ? WCD_PIN_MASK(pin) : 0); +	return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, +				  WCD_PIN_MASK(pin), +				  val ? WCD_PIN_MASK(pin) : 0);  }  static int wcd_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 1ec24f6f9300..4a5e20e936a9 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -200,18 +200,15 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio)  	return val & 0x1;  } -static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) +static int wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value)  {  	struct wcove_gpio *wg = gpiochip_get_data(chip);  	int reg = to_reg(gpio, CTRL_OUT);  	if (reg < 0) -		return; +		return 0; -	if (value) -		regmap_set_bits(wg->regmap, reg, 1); -	else -		regmap_clear_bits(wg->regmap, reg, 1); +	return regmap_assign_bits(wg->regmap, reg, 1, value);  }  static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, diff --git a/drivers/gpio/gpio-winbond.c b/drivers/gpio/gpio-winbond.c index 4b61d975cc0e..dcfda738fd69 100644 --- a/drivers/gpio/gpio-winbond.c +++ b/drivers/gpio/gpio-winbond.c @@ -458,17 +458,19 @@ static int winbond_gpio_direction_out(struct gpio_chip *gc,  	return 0;  } -static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, -			     int val) +static int winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, +			    int val)  {  	unsigned long *base = gpiochip_get_data(gc);  	const struct winbond_gpio_info *info; +	int ret;  	if (!winbond_gpio_get_info(&offset, &info)) -		return; +		return -EACCES; -	if (winbond_sio_enter(*base) != 0) -		return; +	ret = winbond_sio_enter(*base); +	if (ret) +		return ret;  	winbond_sio_select_logical(*base, info->dev); @@ -481,6 +483,8 @@ static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset,  		winbond_sio_reg_bclear(*base, info->datareg, offset);  	winbond_sio_leave(*base); + +	return 0;  }  static struct gpio_chip winbond_gpio_chip = { diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 61bb83a1e8ae..f03c0e808fab 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -58,13 +58,14 @@ static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset)  		return 0;  } -static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm831x_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip);  	struct wm831x *wm831x = wm831x_gpio->wm831x; -	wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, -			value << offset); +	return wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, +			       value << offset);  }  static int wm831x_gpio_direction_out(struct gpio_chip *chip, @@ -85,9 +86,7 @@ static int wm831x_gpio_direction_out(struct gpio_chip *chip,  		return ret;  	/* Can only set GPIO state once it's in output mode */ -	wm831x_gpio_set(chip, offset, value); - -	return 0; +	return wm831x_gpio_set(chip, offset, value);  }  static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c index 2421cf606ed6..46923b23a72e 100644 --- a/drivers/gpio/gpio-wm8350.c +++ b/drivers/gpio/gpio-wm8350.c @@ -48,15 +48,16 @@ static int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset)  		return 0;  } -static void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8350_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip);  	struct wm8350 *wm8350 = wm8350_gpio->wm8350;  	if (value) -		wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); -	else -		wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); +		return wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); + +	return wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset);  }  static int wm8350_gpio_direction_out(struct gpio_chip *chip, @@ -72,9 +73,7 @@ static int wm8350_gpio_direction_out(struct gpio_chip *chip,  		return ret;  	/* Don't have an atomic direction/value setup */ -	wm8350_gpio_set(chip, offset, value); - -	return 0; +	return wm8350_gpio_set(chip, offset, value);  }  static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index bf05c9b5882b..df47a27f508d 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -89,7 +89,8 @@ static int wm8994_gpio_direction_out(struct gpio_chip *chip,  			       WM8994_GPN_DIR | WM8994_GPN_LVL, value);  } -static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8994_gpio_set(struct gpio_chip *chip, unsigned int offset, +			   int value)  {  	struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip);  	struct wm8994 *wm8994 = wm8994_gpio->wm8994; @@ -97,7 +98,8 @@ static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value)  	if (value)  		value = WM8994_GPN_LVL; -	wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value); +	return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, +			       value);  }  static int wm8994_gpio_set_config(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index 48b829733b15..b51b1fa726bb 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -103,12 +103,32 @@ static int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type)  		return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);  } -static struct irq_chip xgene_gpio_sb_irq_chip = { +static void xgene_gpio_sb_irq_mask(struct irq_data *d) +{ +	struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d); + +	irq_chip_mask_parent(d); + +	gpiochip_disable_irq(&priv->gc, d->hwirq); +} + +static void xgene_gpio_sb_irq_unmask(struct irq_data *d) +{ +	struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d); + +	gpiochip_enable_irq(&priv->gc, d->hwirq); + +	irq_chip_unmask_parent(d); +} + +static const struct irq_chip xgene_gpio_sb_irq_chip = {  	.name           = "sbgpio",  	.irq_eoi	= irq_chip_eoi_parent, -	.irq_mask       = irq_chip_mask_parent, -	.irq_unmask     = irq_chip_unmask_parent, +	.irq_mask       = xgene_gpio_sb_irq_mask, +	.irq_unmask     = xgene_gpio_sb_irq_unmask,  	.irq_set_type   = xgene_gpio_sb_irq_set_type, +	.flags = IRQCHIP_IMMUTABLE, +	GPIOCHIP_IRQ_RESOURCE_HELPERS,  };  static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio) diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c index fb4b0c67aeef..4f627de3f56c 100644 --- a/drivers/gpio/gpio-xgene.c +++ b/drivers/gpio/gpio-xgene.c @@ -62,7 +62,7 @@ static void __xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)  	iowrite32(setval, chip->base + bank_offset);  } -static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)  {  	struct xgene_gpio *chip = gpiochip_get_data(gc);  	unsigned long flags; @@ -70,6 +70,8 @@ static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)  	spin_lock_irqsave(&chip->lock, flags);  	__xgene_gpio_set(gc, offset, val);  	spin_unlock_irqrestore(&chip->lock, flags); + +	return 0;  }  static int xgene_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index c58a7e1349b4..83675ac81077 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -148,7 +148,7 @@ static int xgpio_get(struct gpio_chip *gc, unsigned int gpio)   * This function writes the specified value in to the specified signal of the   * GPIO device.   */ -static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  {  	unsigned long flags;  	struct xgpio_instance *chip = gpiochip_get_data(gc); @@ -162,6 +162,8 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)  	xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state);  	raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + +	return 0;  }  /** @@ -173,8 +175,8 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)   * This function writes the specified values into the specified signals of the   * GPIO devices.   */ -static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, -			       unsigned long *bits) +static int xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, +			      unsigned long *bits)  {  	DECLARE_BITMAP(hw_mask, 64);  	DECLARE_BITMAP(hw_bits, 64); @@ -194,6 +196,8 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,  	bitmap_copy(chip->state, state, 64);  	raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + +	return 0;  }  /** diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index b4b52213bcd9..aede6324387f 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -206,7 +206,6 @@ static int xlp_gpio_dir_output(struct gpio_chip *gc, unsigned gpio, int state)  {  	struct xlp_gpio_priv *priv = gpiochip_get_data(gc); -	BUG_ON(gpio >= gc->ngpio);  	xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x1);  	return 0; @@ -216,7 +215,6 @@ static int xlp_gpio_dir_input(struct gpio_chip *gc, unsigned gpio)  {  	struct xlp_gpio_priv *priv = gpiochip_get_data(gc); -	BUG_ON(gpio >= gc->ngpio);  	xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x0);  	return 0; @@ -226,16 +224,16 @@ static int xlp_gpio_get(struct gpio_chip *gc, unsigned gpio)  {  	struct xlp_gpio_priv *priv = gpiochip_get_data(gc); -	BUG_ON(gpio >= gc->ngpio);  	return xlp_gpio_get_reg(priv->gpio_paddrv, gpio);  } -static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state) +static int xlp_gpio_set(struct gpio_chip *gc, unsigned int gpio, int state)  {  	struct xlp_gpio_priv *priv = gpiochip_get_data(gc); -	BUG_ON(gpio >= gc->ngpio);  	xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state); + +	return 0;  }  static int xlp_gpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 842cf875bb92..faadcb4b0b2d 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -102,16 +102,13 @@ static int xra1403_get(struct gpio_chip *chip, unsigned int offset)  	return !!(val & BIT(offset % 8));  } -static void xra1403_set(struct gpio_chip *chip, unsigned int offset, int value) +static int xra1403_set(struct gpio_chip *chip, unsigned int offset, int value)  { -	int ret;  	struct xra1403 *xra = gpiochip_get_data(chip); -	ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), -			BIT(offset % 8), value ? BIT(offset % 8) : 0); -	if (ret) -		dev_err(chip->parent, "Failed to set pin: %d, ret: %d\n", -				offset, ret); +	return regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), +				  BIT(offset % 8), +				  value ? BIT(offset % 8) : 0);  }  #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index c8af34a6368f..4418947a10e5 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -86,12 +86,6 @@ static int xtensa_impwire_get_value(struct gpio_chip *gc, unsigned offset)  	return !!(impwire & BIT(offset));  } -static void xtensa_impwire_set_value(struct gpio_chip *gc, unsigned offset, -				    int value) -{ -	BUG(); /* output only; should never be called */ -} -  static int xtensa_expstate_get_direction(struct gpio_chip *gc, unsigned offset)  {  	return GPIO_LINE_DIRECTION_OUT; /* output only */ @@ -109,7 +103,7 @@ static int xtensa_expstate_get_value(struct gpio_chip *gc, unsigned offset)  	return !!(expstate & BIT(offset));  } -static void xtensa_expstate_set_value(struct gpio_chip *gc, unsigned offset, +static int xtensa_expstate_set_value(struct gpio_chip *gc, unsigned int offset,  				     int value)  {  	unsigned long flags, saved_cpenable; @@ -120,6 +114,8 @@ static void xtensa_expstate_set_value(struct gpio_chip *gc, unsigned offset,  	__asm__ __volatile__("wrmsk_expstate %0, %1"  			     :: "a" (val), "a" (mask));  	disable_cp(flags, saved_cpenable); + +	return 0;  }  static struct gpio_chip impwire_chip = { @@ -128,7 +124,6 @@ static struct gpio_chip impwire_chip = {  	.ngpio		= 32,  	.get_direction	= xtensa_impwire_get_direction,  	.get		= xtensa_impwire_get_value, -	.set		= xtensa_impwire_set_value,  };  static struct gpio_chip expstate_chip = { diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index d7230fd83f5d..29375bea2289 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -91,7 +91,7 @@ static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin)  	return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1;  } -static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) +static int zevio_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)  {  	struct zevio_gpio *controller = gpiochip_get_data(chip);  	u32 val; @@ -105,6 +105,8 @@ static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value)  	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);  	spin_unlock(&controller->lock); + +	return 0;  }  static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 3dae63f3ea21..0ffd76e8951f 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -265,8 +265,8 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin)   * upper 16 bits) based on the given pin number and sets the state of a   * gpio pin to the specified value. The state is either 0 or non-zero.   */ -static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, -				int state) +static int zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, +			       int state)  {  	unsigned int reg_offset, bank_num, bank_pin_num;  	struct zynq_gpio *gpio = gpiochip_get_data(chip); @@ -290,6 +290,8 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,  		((state << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK);  	writel_relaxed(state, gpio->base_addr + reg_offset); + +	return 0;  }  /** diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c index 2f3c9ebfa78d..5e651482e985 100644 --- a/drivers/gpio/gpio-zynqmp-modepin.c +++ b/drivers/gpio/gpio-zynqmp-modepin.c @@ -57,8 +57,8 @@ static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin)   *   * Return:	None.   */ -static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, -				   int state) +static int modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, +				  int state)  {  	u32 bootpin_val = 0;  	int ret; @@ -77,6 +77,8 @@ static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin,  	ret = zynqmp_pm_bootmode_write(bootpin_val);  	if (ret)  		pr_err("modepin: set value error %d for pin %d\n", ret, pin); + +	return ret;  }  /** @@ -102,7 +104,7 @@ static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)  static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,  				int state)  { -	return 0; +	return modepin_gpio_set_value(chip, pin, state);  }  /** diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi-core.c index 69caa35c58df..12b24a717e43 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -23,29 +23,6 @@  #include "gpiolib.h"  #include "gpiolib-acpi.h" -static int run_edge_events_on_boot = -1; -module_param(run_edge_events_on_boot, int, 0444); -MODULE_PARM_DESC(run_edge_events_on_boot, -		 "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); - -static char *ignore_wake; -module_param(ignore_wake, charp, 0444); -MODULE_PARM_DESC(ignore_wake, -		 "controller@pin combos on which to ignore the ACPI wake flag " -		 "ignore_wake=controller@pin[,controller@pin[,...]]"); - -static char *ignore_interrupt; -module_param(ignore_interrupt, charp, 0444); -MODULE_PARM_DESC(ignore_interrupt, -		 "controller@pin combos on which to ignore interrupt " -		 "ignore_interrupt=controller@pin[,controller@pin[,...]]"); - -struct acpi_gpiolib_dmi_quirk { -	bool no_edge_events_on_boot; -	char *ignore_wake; -	char *ignore_interrupt; -}; -  /**   * struct acpi_gpio_event - ACPI GPIO event handler data   * @@ -96,10 +73,10 @@ struct acpi_gpio_chip {   * @adev: reference to ACPI device which consumes GPIO resource   * @flags: GPIO initialization flags   * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo + * @wake_capable: wake capability as provided by ACPI   * @pin_config: pin bias as provided by ACPI   * @polarity: interrupt polarity as provided by ACPI   * @triggering: triggering type as provided by ACPI - * @wake_capable: wake capability as provided by ACPI   * @debounce: debounce timeout as provided by ACPI   * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping   */ @@ -107,25 +84,14 @@ struct acpi_gpio_info {  	struct acpi_device *adev;  	enum gpiod_flags flags;  	bool gpioint; +	bool wake_capable;  	int pin_config;  	int polarity;  	int triggering; -	bool wake_capable;  	unsigned int debounce;  	unsigned int quirks;  }; -/* - * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init - * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a - * late_initcall_sync() handler, so that other builtin drivers can register their - * OpRegions before the event handlers can run. This list contains GPIO chips - * for which the acpi_gpiochip_request_irqs() call has been deferred. - */ -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, const void *data)  {  	/* First check the actual GPIO device */ @@ -268,7 +234,7 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,  	event->irq_requested = true;  	/* Make sure we trigger the initial state of edge-triggered IRQs */ -	if (run_edge_events_on_boot && +	if (acpi_gpio_need_run_edge_events_on_boot() &&  	    (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) {  		value = gpiod_get_raw_value_cansleep(event->desc);  		if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || @@ -350,42 +316,6 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,  	return desc;  } -static bool acpi_gpio_in_ignore_list(const char *ignore_list, const char *controller_in, -				     unsigned int pin_in) -{ -	const char *controller, *pin_str; -	unsigned int pin; -	char *endp; -	int len; - -	controller = ignore_list; -	while (controller) { -		pin_str = strchr(controller, '@'); -		if (!pin_str) -			goto err; - -		len = pin_str - controller; -		if (len == strlen(controller_in) && -		    strncmp(controller, controller_in, len) == 0) { -			pin = simple_strtoul(pin_str + 1, &endp, 10); -			if (*endp != 0 && *endp != ',') -				goto err; - -			if (pin == pin_in) -				return true; -		} - -		controller = strchr(controller, ','); -		if (controller) -			controller++; -	} - -	return false; -err: -	pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list); -	return false; -} -  static bool acpi_gpio_irq_is_wake(struct device *parent,  				  const struct acpi_resource_gpio *agpio)  { @@ -394,7 +324,7 @@ static bool acpi_gpio_irq_is_wake(struct device *parent,  	if (agpio->wake_capable != ACPI_WAKE_CAPABLE)  		return false; -	if (acpi_gpio_in_ignore_list(ignore_wake, dev_name(parent), pin)) { +	if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_WAKE, dev_name(parent), pin)) {  		dev_info(parent, "Ignoring wakeup on pin %u\n", pin);  		return false;  	} @@ -437,7 +367,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,  	if (!handler)  		return AE_OK; -	if (acpi_gpio_in_ignore_list(ignore_interrupt, dev_name(chip->parent), pin)) { +	if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_INTERRUPT, dev_name(chip->parent), pin)) {  		dev_info(chip->parent, "Ignoring interrupt on pin %u\n", pin);  		return AE_OK;  	} @@ -525,7 +455,6 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)  	struct acpi_gpio_chip *acpi_gpio;  	acpi_handle handle;  	acpi_status status; -	bool defer;  	if (!chip->parent || !chip->to_irq)  		return; @@ -544,14 +473,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)  	acpi_walk_resources(handle, METHOD_NAME__AEI,  			    acpi_gpiochip_alloc_event, acpi_gpio); -	mutex_lock(&acpi_gpio_deferred_req_irqs_lock); -	defer = !acpi_gpio_deferred_req_irqs_done; -	if (defer) -		list_add(&acpi_gpio->deferred_req_irqs_list_entry, -			 &acpi_gpio_deferred_req_irqs_list); -	mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); - -	if (defer) +	if (acpi_gpio_add_to_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry))  		return;  	acpi_gpiochip_request_irqs(acpi_gpio); @@ -583,10 +505,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)  	if (ACPI_FAILURE(status))  		return; -	mutex_lock(&acpi_gpio_deferred_req_irqs_lock); -	if (!list_empty(&acpi_gpio->deferred_req_irqs_list_entry)) -		list_del_init(&acpi_gpio->deferred_req_irqs_list_entry); -	mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); +	acpi_gpio_remove_from_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry);  	list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) {  		if (event->irq_requested) { @@ -604,6 +523,14 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)  }  EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts); +void __init acpi_gpio_process_deferred_list(struct list_head *list) +{ +	struct acpi_gpio_chip *acpi_gpio, *tmp; + +	list_for_each_entry_safe(acpi_gpio, tmp, list, deferred_req_irqs_list_entry) +		acpi_gpiochip_request_irqs(acpi_gpio); +} +  int acpi_dev_add_driver_gpios(struct acpi_device *adev,  			      const struct acpi_gpio_mapping *gpios)  { @@ -653,12 +580,12 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev,  	for (gm = adev->driver_gpios; gm->name; gm++)  		if (!strcmp(name, gm->name) && gm->data && index < gm->size) { -			const struct acpi_gpio_params *par = gm->data + index; +			const struct acpi_gpio_params *params = gm->data + index;  			args->fwnode = acpi_fwnode_handle(adev); -			args->args[0] = par->crs_entry_index; -			args->args[1] = par->line_index; -			args->args[2] = par->active_low; +			args->args[0] = params->crs_entry_index; +			args->args[1] = params->line_index; +			args->args[2] = params->active_low;  			args->nargs = 3;  			*quirks = gm->quirks; @@ -743,10 +670,8 @@ static int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,  }  struct acpi_gpio_lookup { -	struct acpi_gpio_info info; -	int index; -	u16 pin_index; -	bool active_low; +	struct acpi_gpio_params params; +	struct acpi_gpio_info *info;  	struct gpio_desc *desc;  	int n;  }; @@ -754,6 +679,8 @@ struct acpi_gpio_lookup {  static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)  {  	struct acpi_gpio_lookup *lookup = data; +	struct acpi_gpio_params *params = &lookup->params; +	struct acpi_gpio_info *info = lookup->info;  	if (ares->type != ACPI_RESOURCE_TYPE_GPIO)  		return 1; @@ -764,26 +691,26 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)  		struct gpio_desc *desc;  		u16 pin_index; -		if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) -			lookup->index++; +		if (info->quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) +			params->crs_entry_index++; -		if (lookup->n++ != lookup->index) +		if (lookup->n++ != params->crs_entry_index)  			return 1; -		pin_index = lookup->pin_index; +		pin_index = params->line_index;  		if (pin_index >= agpio->pin_table_length)  			return 1; -		if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER) +		if (info->quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER)  			desc = gpio_to_desc(agpio->pin_table[pin_index]);  		else  			desc = acpi_get_gpiod(agpio->resource_source.string_ptr,  					      agpio->pin_table[pin_index]);  		lookup->desc = desc; -		lookup->info.pin_config = agpio->pin_config; -		lookup->info.debounce = agpio->debounce_timeout; -		lookup->info.gpioint = gpioint; -		lookup->info.wake_capable = acpi_gpio_irq_is_wake(&lookup->info.adev->dev, agpio); +		info->pin_config = agpio->pin_config; +		info->debounce = agpio->debounce_timeout; +		info->gpioint = gpioint; +		info->wake_capable = acpi_gpio_irq_is_wake(&info->adev->dev, agpio);  		/*  		 * Polarity and triggering are only specified for GpioInt @@ -792,23 +719,23 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)  		 * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW  		 * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH  		 */ -		if (lookup->info.gpioint) { -			lookup->info.polarity = agpio->polarity; -			lookup->info.triggering = agpio->triggering; +		if (info->gpioint) { +			info->polarity = agpio->polarity; +			info->triggering = agpio->triggering;  		} else { -			lookup->info.polarity = lookup->active_low; +			info->polarity = params->active_low;  		} -		lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); +		info->flags = acpi_gpio_to_gpiod_flags(agpio, info->polarity);  	}  	return 1;  } -static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, -				     struct acpi_gpio_info *info) +static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup)  { -	struct acpi_device *adev = lookup->info.adev; +	struct acpi_gpio_info *info = lookup->info; +	struct acpi_device *adev = info->adev;  	struct list_head res_list;  	int ret; @@ -825,22 +752,22 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup,  	if (!lookup->desc)  		return -ENOENT; -	if (info) -		*info = lookup->info;  	return 0;  } -static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, -				     const char *propname, int index, +static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, const char *propname,  				     struct acpi_gpio_lookup *lookup)  {  	struct fwnode_reference_args args; +	struct acpi_gpio_params *params = &lookup->params; +	struct acpi_gpio_info *info = lookup->info; +	unsigned int index = params->crs_entry_index;  	unsigned int quirks = 0;  	int ret;  	memset(&args, 0, sizeof(args)); -	ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, -						 &args); + +	ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, &args);  	if (ret) {  		struct acpi_device *adev; @@ -857,12 +784,12 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,  	if (args.nargs != 3)  		return -EPROTO; -	lookup->index = args.args[0]; -	lookup->pin_index = args.args[1]; -	lookup->active_low = !!args.args[2]; +	params->crs_entry_index = args.args[0]; +	params->line_index = args.args[1]; +	params->active_low = !!args.args[2]; -	lookup->info.adev = to_acpi_device_node(args.fwnode); -	lookup->info.quirks = quirks; +	info->adev = to_acpi_device_node(args.fwnode); +	info->quirks = quirks;  	return 0;  } @@ -871,96 +798,83 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,   * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources   * @adev: pointer to a ACPI device to get GPIO from   * @propname: Property name of the GPIO (optional) - * @index: index of GpioIo/GpioInt resource (starting from %0) - * @info: info pointer to fill in (optional) + * @lookup: pointer to struct acpi_gpio_lookup to fill in   * - * Function goes through ACPI resources for @adev and based on @index looks + * Function goes through ACPI resources for @adev and based on @lookup.index looks   * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, - * and returns it. @index matches GpioIo/GpioInt resources only so if there - * are total %3 GPIO resources, the index goes from %0 to %2. + * and returns it. @lookup.index matches GpioIo/GpioInt resources only so if there + * are total 3 GPIO resources, the index goes from 0 to 2.   *   * If @propname is specified the GPIO is looked using device property. In   * that case @index is used to select the GPIO entry in the property value   * (in case of multiple).   *   * 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. + * 0 on success, negative errno on failure. + * + * The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API. + * If the GPIO cannot be translated an error will be returned.   *   * Note: if the GPIO resource has multiple entries in the pin list, this   * function only returns the first.   */ -static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, -						 const char *propname, -						 int index, -						 struct acpi_gpio_info *info) +static int acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, +				   struct acpi_gpio_lookup *lookup)  { -	struct acpi_gpio_lookup lookup; +	struct acpi_gpio_params *params = &lookup->params; +	struct acpi_gpio_info *info = lookup->info;  	int ret; -	memset(&lookup, 0, sizeof(lookup)); -	lookup.index = index; -  	if (propname) {  		dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); -		ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), -						propname, index, &lookup); +		ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), propname, lookup);  		if (ret) -			return ERR_PTR(ret); +			return ret; -		dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %u %u\n", -			dev_name(&lookup.info.adev->dev), lookup.index, -			lookup.pin_index, lookup.active_low); +		dev_dbg(&adev->dev, "GPIO: _DSD returned %s %u %u %u\n", +			dev_name(&info->adev->dev), +			params->crs_entry_index, params->line_index, params->active_low);  	} else { -		dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); -		lookup.info.adev = adev; +		dev_dbg(&adev->dev, "GPIO: looking up %u in _CRS\n", params->crs_entry_index); +		info->adev = adev;  	} -	ret = acpi_gpio_resource_lookup(&lookup, info); -	return ret ? ERR_PTR(ret) : lookup.desc; +	return acpi_gpio_resource_lookup(lookup);  }  /**   * acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node   * @fwnode: pointer to an ACPI firmware node to get the GPIO information from   * @propname: Property name of the GPIO - * @index: index of GpioIo/GpioInt resource (starting from %0) - * @info: info pointer to fill in (optional) + * @lookup: pointer to struct acpi_gpio_lookup to fill in   *   * This function uses the property-based GPIO lookup to get to the GPIO   * 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. + * 0 on success, negative errno on failure. + * + * The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API. + * If the GPIO cannot be translated an error will be returned.   */ -static struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, -						  const char *propname, -						  int index, -						  struct acpi_gpio_info *info) +static int acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, const char *propname, +				    struct acpi_gpio_lookup *lookup)  { -	struct acpi_gpio_lookup lookup;  	int ret;  	if (!is_acpi_data_node(fwnode)) -		return ERR_PTR(-ENODEV); +		return -ENODEV;  	if (!propname) -		return ERR_PTR(-EINVAL); - -	memset(&lookup, 0, sizeof(lookup)); -	lookup.index = index; +		return -EINVAL; -	ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); +	ret = acpi_gpio_property_lookup(fwnode, propname, lookup);  	if (ret) -		return ERR_PTR(ret); +		return ret; -	ret = acpi_gpio_resource_lookup(&lookup, info); -	return ret ? ERR_PTR(ret) : lookup.desc; +	return acpi_gpio_resource_lookup(lookup);  }  static bool acpi_can_fallback_to_crs(struct acpi_device *adev, @@ -982,17 +896,25 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int  		 bool can_fallback, struct acpi_gpio_info *info)  {  	struct acpi_device *adev = to_acpi_device_node(fwnode); +	struct acpi_gpio_lookup lookup;  	struct gpio_desc *desc;  	char propname[32]; +	int ret; + +	memset(&lookup, 0, sizeof(lookup)); +	lookup.params.crs_entry_index = idx; +	lookup.info = info;  	/* Try first from _DSD */  	for_each_gpio_property_name(propname, con_id) {  		if (adev) -			desc = acpi_get_gpiod_by_index(adev, -						       propname, idx, info); +			ret = acpi_get_gpiod_by_index(adev, propname, &lookup);  		else -			desc = acpi_get_gpiod_from_data(fwnode, -							propname, idx, info); +			ret = acpi_get_gpiod_from_data(fwnode, propname, &lookup); +		if (ret) +			continue; + +		desc = lookup.desc;  		if (PTR_ERR(desc) == -EPROBE_DEFER)  			return desc; @@ -1001,8 +923,13 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int  	}  	/* Then from plain _CRS GPIOs */ -	if (can_fallback) -		return acpi_get_gpiod_by_index(adev, NULL, idx, info); +	if (can_fallback) { +		ret = acpi_get_gpiod_by_index(adev, NULL, &lookup); +		if (ret) +			return ERR_PTR(ret); + +		return lookup.desc; +	}  	return ERR_PTR(-ENOENT);  } @@ -1488,248 +1415,3 @@ int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)  	}  	return count ? count : -ENOENT;  } - -/* Run deferred acpi_gpiochip_request_irqs() */ -static int __init acpi_gpio_handle_deferred_request_irqs(void) -{ -	struct acpi_gpio_chip *acpi_gpio, *tmp; - -	mutex_lock(&acpi_gpio_deferred_req_irqs_lock); -	list_for_each_entry_safe(acpi_gpio, tmp, -				 &acpi_gpio_deferred_req_irqs_list, -				 deferred_req_irqs_list_entry) -		acpi_gpiochip_request_irqs(acpi_gpio); - -	acpi_gpio_deferred_req_irqs_done = true; -	mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); - -	return 0; -} -/* We must use _sync so that this runs after the first deferred_probe run */ -late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); - -static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { -	{ -		/* -		 * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for -		 * a non existing micro-USB-B connector which puts the HDMI -		 * DDC pins in GPIO mode, breaking HDMI support. -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), -			DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.no_edge_events_on_boot = true, -		}, -	}, -	{ -		/* -		 * The Terra Pad 1061 has a micro-USB-B id-pin handler, which -		 * instead of controlling the actual micro-USB-B turns the 5V -		 * boost for its USB-A connector off. The actual micro-USB-B -		 * connector is wired for charging only. -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), -			DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.no_edge_events_on_boot = true, -		}, -	}, -	{ -		/* -		 * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an -		 * external embedded-controller connected via I2C + an ACPI GPIO -		 * event handler on INT33FFC:02 pin 12, causing spurious wakeups. -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), -			DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_wake = "INT33FC:02@12", -		}, -	}, -	{ -		/* -		 * HP X2 10 models with Cherry Trail SoC + TI PMIC use an -		 * external embedded-controller connected via I2C + an ACPI GPIO -		 * event handler on INT33FF:01 pin 0, causing spurious wakeups. -		 * When suspending by closing the LID, the power to the USB -		 * keyboard is turned off, causing INT0002 ACPI events to -		 * trigger once the XHCI controller notices the keyboard is -		 * gone. So INT0002 events cause spurious wakeups too. Ignoring -		 * EC wakes breaks wakeup when opening the lid, the user needs -		 * to press the power-button to wakeup the system. The -		 * alternative is suspend simply not working, which is worse. -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "HP"), -			DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_wake = "INT33FF:01@0,INT0002:00@2", -		}, -	}, -	{ -		/* -		 * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an -		 * external embedded-controller connected via I2C + an ACPI GPIO -		 * event handler on INT33FC:02 pin 28, causing spurious wakeups. -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), -			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), -			DMI_MATCH(DMI_BOARD_NAME, "815D"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_wake = "INT33FC:02@28", -		}, -	}, -	{ -		/* -		 * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an -		 * external embedded-controller connected via I2C + an ACPI GPIO -		 * event handler on INT33FF:01 pin 0, causing spurious wakeups. -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "HP"), -			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), -			DMI_MATCH(DMI_BOARD_NAME, "813E"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_wake = "INT33FF:01@0", -		}, -	}, -	{ -		/* -		 * Interrupt storm caused from edge triggered floating pin -		 * Found in BIOS UX325UAZ.300 -		 * https://bugzilla.kernel.org/show_bug.cgi?id=216208 -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), -			DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_interrupt = "AMDI0030:00@18", -		}, -	}, -	{ -		/* -		 * Spurious wakeups from TP_ATTN# pin -		 * Found in BIOS 1.7.8 -		 * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 -		 */ -		.matches = { -			DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_wake = "ELAN0415:00@9", -		}, -	}, -	{ -		/* -		 * Spurious wakeups from TP_ATTN# pin -		 * Found in BIOS 1.7.8 -		 * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 -		 */ -		.matches = { -			DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_wake = "ELAN0415:00@9", -		}, -	}, -	{ -		/* -		 * Spurious wakeups from TP_ATTN# pin -		 * Found in BIOS 1.7.7 -		 */ -		.matches = { -			DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_wake = "SYNA1202:00@16", -		}, -	}, -	{ -		/* -		 * On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to -		 * a "dolby" button. At the ACPI level an _AEI event-handler -		 * is connected which sets an ACPI variable to 1 on both -		 * edges. This variable can be polled + cleared to 0 using -		 * WMI. But since the variable is set on both edges the WMI -		 * interface is pretty useless even when polling. -		 * So instead the x86-android-tablets code instantiates -		 * a gpio-keys platform device for it. -		 * Ignore the _AEI handler for the pin, so that it is not busy. -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), -			DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.ignore_interrupt = "INT33FC:00@3", -		}, -	}, -	{ -		/* -		 * Spurious wakeups from TP_ATTN# pin -		 * Found in BIOS 0.35 -		 * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 -		 */ -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "GPD"), -			DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), -		}, -		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { -			.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 */ -}; - -static int __init acpi_gpio_setup_params(void) -{ -	const struct acpi_gpiolib_dmi_quirk *quirk = NULL; -	const struct dmi_system_id *id; - -	id = dmi_first_match(gpiolib_acpi_quirks); -	if (id) -		quirk = id->driver_data; - -	if (run_edge_events_on_boot < 0) { -		if (quirk && quirk->no_edge_events_on_boot) -			run_edge_events_on_boot = 0; -		else -			run_edge_events_on_boot = 1; -	} - -	if (ignore_wake == NULL && quirk && quirk->ignore_wake) -		ignore_wake = quirk->ignore_wake; - -	if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt) -		ignore_interrupt = quirk->ignore_interrupt; - -	return 0; -} - -/* Directly after dmi_setup() which runs as core_initcall() */ -postcore_initcall(acpi_gpio_setup_params); diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c new file mode 100644 index 000000000000..c13545dce349 --- /dev/null +++ b/drivers/gpio/gpiolib-acpi-quirks.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ACPI quirks for GPIO ACPI helpers + * + * Author: Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/dmi.h> +#include <linux/kstrtox.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/printk.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "gpiolib-acpi.h" + +static int run_edge_events_on_boot = -1; +module_param(run_edge_events_on_boot, int, 0444); +MODULE_PARM_DESC(run_edge_events_on_boot, +		 "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); + +static char *ignore_wake; +module_param(ignore_wake, charp, 0444); +MODULE_PARM_DESC(ignore_wake, +		 "controller@pin combos on which to ignore the ACPI wake flag " +		 "ignore_wake=controller@pin[,controller@pin[,...]]"); + +static char *ignore_interrupt; +module_param(ignore_interrupt, charp, 0444); +MODULE_PARM_DESC(ignore_interrupt, +		 "controller@pin combos on which to ignore interrupt " +		 "ignore_interrupt=controller@pin[,controller@pin[,...]]"); + +/* + * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init + * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a + * late_initcall_sync() handler, so that other builtin drivers can register their + * OpRegions before the event handlers can run. This list contains GPIO chips + * for which the acpi_gpiochip_request_irqs() call has been deferred. + */ +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; + +bool acpi_gpio_add_to_deferred_list(struct list_head *list) +{ +	bool defer; + +	mutex_lock(&acpi_gpio_deferred_req_irqs_lock); +	defer = !acpi_gpio_deferred_req_irqs_done; +	if (defer) +		list_add(list, &acpi_gpio_deferred_req_irqs_list); +	mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); + +	return defer; +} + +void acpi_gpio_remove_from_deferred_list(struct list_head *list) +{ +	mutex_lock(&acpi_gpio_deferred_req_irqs_lock); +	if (!list_empty(list)) +		list_del_init(list); +	mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); +} + +int acpi_gpio_need_run_edge_events_on_boot(void) +{ +	return run_edge_events_on_boot; +} + +bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list, +			      const char *controller_in, unsigned int pin_in) +{ +	const char *ignore_list, *controller, *pin_str; +	unsigned int pin; +	char *endp; +	int len; + +	switch (list) { +	case ACPI_GPIO_IGNORE_WAKE: +		ignore_list = ignore_wake; +		break; +	case ACPI_GPIO_IGNORE_INTERRUPT: +		ignore_list = ignore_interrupt; +		break; +	default: +		return false; +	} + +	controller = ignore_list; +	while (controller) { +		pin_str = strchr(controller, '@'); +		if (!pin_str) +			goto err; + +		len = pin_str - controller; +		if (len == strlen(controller_in) && +		    strncmp(controller, controller_in, len) == 0) { +			pin = simple_strtoul(pin_str + 1, &endp, 10); +			if (*endp != 0 && *endp != ',') +				goto err; + +			if (pin == pin_in) +				return true; +		} + +		controller = strchr(controller, ','); +		if (controller) +			controller++; +	} + +	return false; +err: +	pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list); +	return false; +} + +/* Run deferred acpi_gpiochip_request_irqs() */ +static int __init acpi_gpio_handle_deferred_request_irqs(void) +{ +	mutex_lock(&acpi_gpio_deferred_req_irqs_lock); +	acpi_gpio_process_deferred_list(&acpi_gpio_deferred_req_irqs_list); +	acpi_gpio_deferred_req_irqs_done = true; +	mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); + +	return 0; +} +/* We must use _sync so that this runs after the first deferred_probe run */ +late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); + +struct acpi_gpiolib_dmi_quirk { +	bool no_edge_events_on_boot; +	char *ignore_wake; +	char *ignore_interrupt; +}; + +static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { +	{ +		/* +		 * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for +		 * a non existing micro-USB-B connector which puts the HDMI +		 * DDC pins in GPIO mode, breaking HDMI support. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.no_edge_events_on_boot = true, +		}, +	}, +	{ +		/* +		 * The Terra Pad 1061 has a micro-USB-B id-pin handler, which +		 * instead of controlling the actual micro-USB-B turns the 5V +		 * boost for its USB-A connector off. The actual micro-USB-B +		 * connector is wired for charging only. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), +			DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.no_edge_events_on_boot = true, +		}, +	}, +	{ +		/* +		 * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an +		 * external embedded-controller connected via I2C + an ACPI GPIO +		 * event handler on INT33FFC:02 pin 12, causing spurious wakeups. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), +			DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_wake = "INT33FC:02@12", +		}, +	}, +	{ +		/* +		 * HP X2 10 models with Cherry Trail SoC + TI PMIC use an +		 * external embedded-controller connected via I2C + an ACPI GPIO +		 * event handler on INT33FF:01 pin 0, causing spurious wakeups. +		 * When suspending by closing the LID, the power to the USB +		 * keyboard is turned off, causing INT0002 ACPI events to +		 * trigger once the XHCI controller notices the keyboard is +		 * gone. So INT0002 events cause spurious wakeups too. Ignoring +		 * EC wakes breaks wakeup when opening the lid, the user needs +		 * to press the power-button to wakeup the system. The +		 * alternative is suspend simply not working, which is worse. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "HP"), +			DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_wake = "INT33FF:01@0,INT0002:00@2", +		}, +	}, +	{ +		/* +		 * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an +		 * external embedded-controller connected via I2C + an ACPI GPIO +		 * event handler on INT33FC:02 pin 28, causing spurious wakeups. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), +			DMI_MATCH(DMI_BOARD_NAME, "815D"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_wake = "INT33FC:02@28", +		}, +	}, +	{ +		/* +		 * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an +		 * external embedded-controller connected via I2C + an ACPI GPIO +		 * event handler on INT33FF:01 pin 0, causing spurious wakeups. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "HP"), +			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), +			DMI_MATCH(DMI_BOARD_NAME, "813E"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_wake = "INT33FF:01@0", +		}, +	}, +	{ +		/* +		 * Interrupt storm caused from edge triggered floating pin +		 * Found in BIOS UX325UAZ.300 +		 * https://bugzilla.kernel.org/show_bug.cgi?id=216208 +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), +			DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_interrupt = "AMDI0030:00@18", +		}, +	}, +	{ +		/* +		 * Spurious wakeups from TP_ATTN# pin +		 * Found in BIOS 1.7.8 +		 * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 +		 */ +		.matches = { +			DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_wake = "ELAN0415:00@9", +		}, +	}, +	{ +		/* +		 * Spurious wakeups from TP_ATTN# pin +		 * Found in BIOS 1.7.8 +		 * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 +		 */ +		.matches = { +			DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_wake = "ELAN0415:00@9", +		}, +	}, +	{ +		/* +		 * Spurious wakeups from TP_ATTN# pin +		 * Found in BIOS 1.7.7 +		 */ +		.matches = { +			DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_wake = "SYNA1202:00@16", +		}, +	}, +	{ +		/* +		 * On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to +		 * a "dolby" button. At the ACPI level an _AEI event-handler +		 * is connected which sets an ACPI variable to 1 on both +		 * edges. This variable can be polled + cleared to 0 using +		 * WMI. But since the variable is set on both edges the WMI +		 * interface is pretty useless even when polling. +		 * So instead the x86-android-tablets code instantiates +		 * a gpio-keys platform device for it. +		 * Ignore the _AEI handler for the pin, so that it is not busy. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), +			DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_interrupt = "INT33FC:00@3", +		}, +	}, +	{ +		/* +		 * Spurious wakeups from TP_ATTN# pin +		 * Found in BIOS 0.35 +		 * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "GPD"), +			DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.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", +		}, +	}, +	{ +		/* +		 * Wakeup only works when keyboard backlight is turned off +		 * https://gitlab.freedesktop.org/drm/amd/-/issues/4169 +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +			DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 15"), +		}, +		.driver_data = &(struct acpi_gpiolib_dmi_quirk) { +			.ignore_interrupt = "AMDI0030:00@8", +		}, +	}, +	{} /* Terminating entry */ +}; + +static int __init acpi_gpio_setup_params(void) +{ +	const struct acpi_gpiolib_dmi_quirk *quirk = NULL; +	const struct dmi_system_id *id; + +	id = dmi_first_match(gpiolib_acpi_quirks); +	if (id) +		quirk = id->driver_data; + +	if (run_edge_events_on_boot < 0) { +		if (quirk && quirk->no_edge_events_on_boot) +			run_edge_events_on_boot = 0; +		else +			run_edge_events_on_boot = 1; +	} + +	if (ignore_wake == NULL && quirk && quirk->ignore_wake) +		ignore_wake = quirk->ignore_wake; + +	if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt) +		ignore_interrupt = quirk->ignore_interrupt; + +	return 0; +} + +/* Directly after dmi_setup() which runs as core_initcall() */ +postcore_initcall(acpi_gpio_setup_params); diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h index 7e1c51d04040..a90267470a4e 100644 --- a/drivers/gpio/gpiolib-acpi.h +++ b/drivers/gpio/gpiolib-acpi.h @@ -58,4 +58,19 @@ static inline int acpi_gpio_count(const struct fwnode_handle *fwnode,  }  #endif +void acpi_gpio_process_deferred_list(struct list_head *list); + +bool acpi_gpio_add_to_deferred_list(struct list_head *list); +void acpi_gpio_remove_from_deferred_list(struct list_head *list); + +int acpi_gpio_need_run_edge_events_on_boot(void); + +enum acpi_gpio_ignore_list { +	ACPI_GPIO_IGNORE_WAKE, +	ACPI_GPIO_IGNORE_INTERRUPT, +}; + +bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list, +			      const char *controller_in, unsigned int pin_in); +  #endif /* GPIOLIB_ACPI_H */ diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 107d75558b5a..e6a289fa0f8f 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1366,9 +1366,6 @@ static long linereq_set_values(struct linereq *lr, void __user *ip)  	/* scan requested lines to determine the subset to be set */  	for (num_set = 0, i = 0; i < lr->num_lines; i++) {  		if (lv.mask & BIT_ULL(i)) { -			/* setting inputs is not allowed */ -			if (!test_bit(FLAG_IS_OUT, &lr->lines[i].desc->flags)) -				return -EPERM;  			/* add to compacted values */  			if (lv.bits & BIT_ULL(i))  				__set_bit(num_set, vals); diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 120d1ec5af3b..72422c5db364 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -6,7 +6,7 @@   * Copyright (c) 2011 John Crispin <john@phrozen.org>   */ -#include <linux/device.h> +#include <linux/device/devres.h>  #include <linux/err.h>  #include <linux/export.h>  #include <linux/gfp.h> @@ -19,32 +19,14 @@  struct fwnode_handle;  struct lock_class_key; -static void devm_gpiod_release(struct device *dev, void *res) +static void devm_gpiod_release(void *desc)  { -	struct gpio_desc **desc = res; - -	gpiod_put(*desc); -} - -static int devm_gpiod_match(struct device *dev, void *res, void *data) -{ -	struct gpio_desc **this = res, **gpio = data; - -	return *this == *gpio; +	gpiod_put(desc);  } -static void devm_gpiod_release_array(struct device *dev, void *res) +static void devm_gpiod_release_array(void *descs)  { -	struct gpio_descs **descs = res; - -	gpiod_put_array(*descs); -} - -static int devm_gpiod_match_array(struct device *dev, void *res, void *data) -{ -	struct gpio_descs **this = res, **gpios = data; - -	return *this == *gpios; +	gpiod_put_array(descs);  }  /** @@ -114,8 +96,8 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev,  						    unsigned int idx,  						    enum gpiod_flags flags)  { -	struct gpio_desc **dr;  	struct gpio_desc *desc; +	int ret;  	desc = gpiod_get_index(dev, con_id, idx, flags);  	if (IS_ERR(desc)) @@ -126,23 +108,16 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev,  	 * already under resource management by this device.  	 */  	if (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { -		struct devres *dres; +		bool dres; -		dres = devres_find(dev, devm_gpiod_release, -				   devm_gpiod_match, &desc); +		dres = devm_is_action_added(dev, devm_gpiod_release, desc);  		if (dres)  			return desc;  	} -	dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), -			  GFP_KERNEL); -	if (!dr) { -		gpiod_put(desc); -		return ERR_PTR(-ENOMEM); -	} - -	*dr = desc; -	devres_add(dev, dr); +	ret = devm_add_action_or_reset(dev, devm_gpiod_release, desc); +	if (ret) +		return ERR_PTR(ret);  	return desc;  } @@ -171,22 +146,16 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev,  					      enum gpiod_flags flags,  					      const char *label)  { -	struct gpio_desc **dr;  	struct gpio_desc *desc; - -	dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), -			  GFP_KERNEL); -	if (!dr) -		return ERR_PTR(-ENOMEM); +	int ret;  	desc = gpiod_find_and_request(dev, fwnode, con_id, index, flags, label, false); -	if (IS_ERR(desc)) { -		devres_free(dr); +	if (IS_ERR(desc))  		return desc; -	} -	*dr = desc; -	devres_add(dev, dr); +	ret = devm_add_action_or_reset(dev, devm_gpiod_release, desc); +	if (ret) +		return ERR_PTR(ret);  	return desc;  } @@ -244,22 +213,16 @@ struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,  						     const char *con_id,  						     enum gpiod_flags flags)  { -	struct gpio_descs **dr;  	struct gpio_descs *descs; - -	dr = devres_alloc(devm_gpiod_release_array, -			  sizeof(struct gpio_descs *), GFP_KERNEL); -	if (!dr) -		return ERR_PTR(-ENOMEM); +	int ret;  	descs = gpiod_get_array(dev, con_id, flags); -	if (IS_ERR(descs)) { -		devres_free(dr); +	if (IS_ERR(descs))  		return descs; -	} -	*dr = descs; -	devres_add(dev, dr); +	ret = devm_add_action_or_reset(dev, devm_gpiod_release_array, descs); +	if (ret) +		return ERR_PTR(ret);  	return descs;  } @@ -307,8 +270,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get_array_optional);   */  void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)  { -	WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match, -		&desc)); +	devm_release_action(dev, devm_gpiod_release, desc);  }  EXPORT_SYMBOL_GPL(devm_gpiod_put); @@ -332,13 +294,13 @@ void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc)  	if (IS_ERR_OR_NULL(desc))  		return; -	ret = devres_destroy(dev, devm_gpiod_release, -			     devm_gpiod_match, &desc); +  	/*  	 * If the GPIO descriptor is requested as nonexclusive, we  	 * may call this function several times on the same descriptor  	 * so it is OK if devres_destroy() returns -ENOENT.  	 */ +	ret = devm_remove_action_nowarn(dev, devm_gpiod_release, desc);  	if (ret == -ENOENT)  		return;  	/* Anything else we should warn about */ @@ -357,8 +319,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_unhinge);   */  void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)  { -	WARN_ON(devres_release(dev, devm_gpiod_release_array, -			       devm_gpiod_match_array, &descs)); +	devm_release_action(dev, devm_gpiod_release_array, descs);  }  EXPORT_SYMBOL_GPL(devm_gpiod_put_array); diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index aeae6df8bec9..3bc93ccadb5b 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -86,44 +86,6 @@ static void devm_gpio_release(struct device *dev, void *res)  }  /** - * devm_gpio_request - request a GPIO for a managed device - * @dev: device to request the GPIO for - * @gpio: GPIO to allocate - * @label: the name of the requested GPIO - * - * Except for the extra @dev argument, this function takes the - * same arguments and performs the same function as gpio_request(). - * GPIOs requested with this function will be automatically freed - * on driver detach. - * - * **DEPRECATED** This function is deprecated and must not be used in new code. - * - * Returns: - * 0 on success, or negative errno on failure. - */ -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) -{ -	unsigned *dr; -	int rc; - -	dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); -	if (!dr) -		return -ENOMEM; - -	rc = gpio_request(gpio, label); -	if (rc) { -		devres_free(dr); -		return rc; -	} - -	*dr = gpio; -	devres_add(dev, dr); - -	return 0; -} -EXPORT_SYMBOL_GPL(devm_gpio_request); - -/**   * devm_gpio_request_one - request a single GPIO with initial setup   * @dev: device to request for   * @gpio: the GPIO number diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 65f6a7177b78..37ab78243fab 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -224,6 +224,15 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np,  		 */  		{ "lantiq,pci-xway",	"gpio-reset",	false },  #endif +#if IS_ENABLED(CONFIG_REGULATOR_S5M8767) +		/* +		 * According to S5M8767, the DVS and DS pin are +		 * active-high signals. However, exynos5250-spring.dts use +		 * active-low setting. +		 */ +		{ "samsung,s5m8767-pmic", "s5m8767,pmic-buck-dvs-gpios", true }, +		{ "samsung,s5m8767-pmic", "s5m8767,pmic-buck-ds-gpios", true }, +#endif  #if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2005)  		/*  		 * DTS for Nokia N900 incorrectly specified "active high" @@ -699,7 +708,7 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,  			       unsigned int idx, unsigned long *flags)  {  	char propname[32]; /* 32 is max size of property name */ -	enum of_gpio_flags of_flags; +	enum of_gpio_flags of_flags = 0;  	const of_find_gpio_quirk *q;  	struct gpio_desc *desc; @@ -1278,3 +1287,11 @@ void of_gpiochip_remove(struct gpio_chip *chip)  {  	of_node_put(dev_of_node(&chip->gpiodev->dev));  } + +bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index) +{ +	if (gc->of_node_instance_match) +		return gc->of_node_instance_match(gc, index); + +	return false; +} diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h index 16d6ac8cb156..2257f7a498a1 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -8,7 +8,7 @@  #include <linux/notifier.h> -struct device; +struct device_node;  struct fwnode_handle;  struct gpio_chip; @@ -22,6 +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); +bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index);  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, @@ -33,6 +34,11 @@ 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 bool of_gpiochip_instance_match(struct gpio_chip *gc, +					      unsigned int index) +{ +	return false; +}  static inline int of_gpio_count(const struct fwnode_handle *fwnode,  				const char *con_id)  { diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 1acfa43bf1ab..b64106f1cb7b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -3,7 +3,6 @@  #include <linux/bitops.h>  #include <linux/cleanup.h>  #include <linux/device.h> -#include <linux/idr.h>  #include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/kdev_t.h> @@ -12,7 +11,6 @@  #include <linux/mutex.h>  #include <linux/printk.h>  #include <linux/slab.h> -#include <linux/spinlock.h>  #include <linux/string.h>  #include <linux/srcu.h>  #include <linux/sysfs.h> @@ -26,6 +24,8 @@  #include "gpiolib.h"  #include "gpiolib-sysfs.h" +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +  struct kernfs_node;  #define GPIO_IRQF_TRIGGER_NONE		0 @@ -34,15 +34,64 @@ struct kernfs_node;  #define GPIO_IRQF_TRIGGER_BOTH		(GPIO_IRQF_TRIGGER_FALLING | \  					 GPIO_IRQF_TRIGGER_RISING) +enum { +	GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION = 0, +	GPIO_SYSFS_LINE_CLASS_ATTR_VALUE, +	GPIO_SYSFS_LINE_CLASS_ATTR_EDGE, +	GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW, +	GPIO_SYSFS_LINE_CLASS_ATTR_SENTINEL, +	GPIO_SYSFS_LINE_CLASS_ATTR_SIZE, +}; + +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + +enum { +	GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION = 0, +	GPIO_SYSFS_LINE_CHIP_ATTR_VALUE, +	GPIO_SYSFS_LINE_CHIP_ATTR_SENTINEL, +	GPIO_SYSFS_LINE_CHIP_ATTR_SIZE, +}; +  struct gpiod_data { +	struct list_head list; +  	struct gpio_desc *desc; +	struct device *dev;  	struct mutex mutex; +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)  	struct kernfs_node *value_kn;  	int irq;  	unsigned char irq_flags; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */  	bool direction_can_change; + +	struct kobject *parent; +	struct device_attribute dir_attr; +	struct device_attribute val_attr; + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +	struct device_attribute edge_attr; +	struct device_attribute active_low_attr; + +	struct attribute *class_attrs[GPIO_SYSFS_LINE_CLASS_ATTR_SIZE]; +	struct attribute_group class_attr_group; +	const struct attribute_group *class_attr_groups[2]; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + +	struct attribute *chip_attrs[GPIO_SYSFS_LINE_CHIP_ATTR_SIZE]; +	struct attribute_group chip_attr_group; +	const struct attribute_group *chip_attr_groups[2]; +}; + +struct gpiodev_data { +	struct list_head exported_lines; +	struct gpio_device *gdev; +	struct device *cdev_id; /* Class device by GPIO device ID */ +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +	struct device *cdev_base; /* Class device by GPIO base */ +#endif /* CONFIG_GPIO_SYSFS_LEGACY */  };  /* @@ -73,9 +122,10 @@ static DEFINE_MUTEX(sysfs_lock);   */  static ssize_t direction_show(struct device *dev, -		struct device_attribute *attr, char *buf) +			      struct device_attribute *attr, char *buf)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       dir_attr);  	struct gpio_desc *desc = data->desc;  	int value; @@ -88,11 +138,13 @@ static ssize_t direction_show(struct device *dev,  }  static ssize_t direction_store(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t size) +			       struct device_attribute *attr, const char *buf, +			       size_t size)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       dir_attr);  	struct gpio_desc *desc = data->desc; -	ssize_t			status; +	ssize_t status;  	guard(mutex)(&data->mutex); @@ -107,14 +159,14 @@ static ssize_t direction_store(struct device *dev,  	return status ? : size;  } -static DEVICE_ATTR_RW(direction); -static ssize_t value_show(struct device *dev, -		struct device_attribute *attr, char *buf) +static ssize_t value_show(struct device *dev, struct device_attribute *attr, +			  char *buf)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       val_attr);  	struct gpio_desc *desc = data->desc; -	ssize_t			status; +	ssize_t status;  	scoped_guard(mutex, &data->mutex)  		status = gpiod_get_value_cansleep(desc); @@ -125,30 +177,29 @@ static ssize_t value_show(struct device *dev,  	return sysfs_emit(buf, "%zd\n", status);  } -static ssize_t value_store(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t size) +static ssize_t value_store(struct device *dev, struct device_attribute *attr, +			   const char *buf, size_t size)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       val_attr);  	struct gpio_desc *desc = data->desc;  	ssize_t status;  	long value;  	status = kstrtol(buf, 0, &value); +	if (status) +		return status;  	guard(mutex)(&data->mutex); -	if (!test_bit(FLAG_IS_OUT, &desc->flags)) -		return -EPERM; - +	status = gpiod_set_value_cansleep(desc, value);  	if (status)  		return status; -	gpiod_set_value_cansleep(desc, value); -  	return size;  } -static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)  static irqreturn_t gpio_sysfs_irq(int irq, void *priv)  {  	struct gpiod_data *data = priv; @@ -159,9 +210,8 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv)  }  /* Caller holds gpiod-data mutex. */ -static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) +static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)  { -	struct gpiod_data *data = dev_get_drvdata(dev);  	struct gpio_desc *desc = data->desc;  	unsigned long irq_flags;  	int ret; @@ -174,33 +224,29 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)  	if (data->irq < 0)  		return -EIO; -	data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); -	if (!data->value_kn) -		return -ENODEV; -  	irq_flags = IRQF_SHARED;  	if (flags & GPIO_IRQF_TRIGGER_FALLING) {  		irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? -			IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; +				IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;  		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; +				IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;  		set_bit(FLAG_EDGE_RISING, &desc->flags);  	}  	/*  	 * FIXME: This should be done in the irq_request_resources callback -	 *        when the irq is requested, but a few drivers currently fail -	 *        to do so. +	 * when the irq is requested, but a few drivers currently fail to do +	 * so.  	 * -	 *        Remove this redundant call (along with the corresponding -	 *        unlock) when those drivers have been fixed. +	 * Remove this redundant call (along with the corresponding unlock) +	 * when those drivers have been fixed.  	 */  	ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc));  	if (ret < 0) -		goto err_put_kn; +		goto err_clr_bits;  	ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags,  				"gpiolib", data); @@ -213,10 +259,9 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)  err_unlock:  	gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); -err_put_kn: +err_clr_bits:  	clear_bit(FLAG_EDGE_RISING, &desc->flags);  	clear_bit(FLAG_EDGE_FALLING, &desc->flags); -	sysfs_put(data->value_kn);  	return ret;  } @@ -225,9 +270,8 @@ err_put_kn:   * Caller holds gpiod-data mutex (unless called after class-device   * deregistration).   */ -static void gpio_sysfs_free_irq(struct device *dev) +static void gpio_sysfs_free_irq(struct gpiod_data *data)  { -	struct gpiod_data *data = dev_get_drvdata(dev);  	struct gpio_desc *desc = data->desc;  	CLASS(gpio_chip_guard, guard)(desc); @@ -239,20 +283,20 @@ static void gpio_sysfs_free_irq(struct device *dev)  	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);  } -static const char * const trigger_names[] = { +static const char *const trigger_names[] = {  	[GPIO_IRQF_TRIGGER_NONE]	= "none",  	[GPIO_IRQF_TRIGGER_FALLING]	= "falling",  	[GPIO_IRQF_TRIGGER_RISING]	= "rising",  	[GPIO_IRQF_TRIGGER_BOTH]	= "both",  }; -static ssize_t edge_show(struct device *dev, -		struct device_attribute *attr, char *buf) +static ssize_t edge_show(struct device *dev, struct device_attribute *attr, +			 char *buf)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       edge_attr);  	int flags;  	scoped_guard(mutex, &data->mutex) @@ -264,10 +308,11 @@ static ssize_t edge_show(struct device *dev,  	return sysfs_emit(buf, "%s\n", trigger_names[flags]);  } -static ssize_t edge_store(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t size) +static ssize_t edge_store(struct device *dev, struct device_attribute *attr, +			  const char *buf, size_t size)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       edge_attr);  	ssize_t status = size;  	int flags; @@ -281,12 +326,12 @@ static ssize_t edge_store(struct device *dev,  		return size;  	if (data->irq_flags) -		gpio_sysfs_free_irq(dev); +		gpio_sysfs_free_irq(data);  	if (!flags)  		return size; -	status = gpio_sysfs_request_irq(dev, flags); +	status = gpio_sysfs_request_irq(data, flags);  	if (status)  		return status; @@ -294,17 +339,14 @@ static ssize_t edge_store(struct device *dev,  	return size;  } -static DEVICE_ATTR_RW(edge);  /* Caller holds gpiod-data mutex. */ -static int gpio_sysfs_set_active_low(struct device *dev, int value) +static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value)  { -	struct gpiod_data *data = dev_get_drvdata(dev);  	unsigned int flags = data->irq_flags;  	struct gpio_desc *desc = data->desc;  	int status = 0; -  	if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)  		return 0; @@ -312,9 +354,9 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value)  	/* reconfigure poll(2) support if enabled on one edge only */  	if (flags == GPIO_IRQF_TRIGGER_FALLING || -					flags == GPIO_IRQF_TRIGGER_RISING) { -		gpio_sysfs_free_irq(dev); -		status = gpio_sysfs_request_irq(dev, flags); +	    flags == GPIO_IRQF_TRIGGER_RISING) { +		gpio_sysfs_free_irq(data); +		status = gpio_sysfs_request_irq(data, flags);  	}  	gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); @@ -323,9 +365,10 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value)  }  static ssize_t active_low_show(struct device *dev, -		struct device_attribute *attr, char *buf) +			       struct device_attribute *attr, char *buf)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       active_low_attr);  	struct gpio_desc *desc = data->desc;  	int value; @@ -336,9 +379,11 @@ static ssize_t active_low_show(struct device *dev,  }  static ssize_t active_low_store(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t size) +				struct device_attribute *attr, +				const char *buf, size_t size)  { -	struct gpiod_data *data = dev_get_drvdata(dev); +	struct gpiod_data *data = container_of(attr, struct gpiod_data, +					       active_low_attr);  	ssize_t status;  	long value; @@ -348,84 +393,189 @@ static ssize_t active_low_store(struct device *dev,  	guard(mutex)(&data->mutex); -	return gpio_sysfs_set_active_low(dev, value) ?: size; +	return gpio_sysfs_set_active_low(data, value) ?: size;  } -static DEVICE_ATTR_RW(active_low); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */  static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,  			       int n)  { -	struct device *dev = kobj_to_dev(kobj); -	struct gpiod_data *data = dev_get_drvdata(dev); -	struct gpio_desc *desc = data->desc; +	struct device_attribute *dev_attr = container_of(attr, +						struct device_attribute, attr);  	umode_t mode = attr->mode; -	bool show_direction = data->direction_can_change; +	struct gpiod_data *data; + +	if (strcmp(attr->name, "direction") == 0) { +		data = container_of(dev_attr, struct gpiod_data, dir_attr); -	if (attr == &dev_attr_direction.attr) { -		if (!show_direction) +		if (!data->direction_can_change)  			mode = 0; -	} else if (attr == &dev_attr_edge.attr) { -		if (gpiod_to_irq(desc) < 0) +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +	} else if (strcmp(attr->name, "edge") == 0) { +		data = container_of(dev_attr, struct gpiod_data, edge_attr); + +		if (gpiod_to_irq(data->desc) < 0)  			mode = 0; -		if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) + +		if (!data->direction_can_change && +		    test_bit(FLAG_IS_OUT, &data->desc->flags))  			mode = 0; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */  	}  	return mode;  } -static struct attribute *gpio_attrs[] = { -	&dev_attr_direction.attr, -	&dev_attr_edge.attr, -	&dev_attr_value.attr, -	&dev_attr_active_low.attr, -	NULL, -}; - -static const struct attribute_group gpio_group = { -	.attrs = gpio_attrs, -	.is_visible = gpio_is_visible, -}; - -static const struct attribute_group *gpio_groups[] = { -	&gpio_group, -	NULL -}; -  /*   * /sys/class/gpio/gpiochipN/   *   /base ... matching gpio_chip.base (N)   *   /label ... matching gpio_chip.label   *   /ngpio ... matching gpio_chip.ngpio + * + * AND + * + * /sys/class/gpio/chipX/ + *   /export ... export GPIO at given offset + *   /unexport ... unexport GPIO at given offset + *   /label ... matching gpio_chip.label + *   /ngpio ... matching gpio_chip.ngpio   */ -static ssize_t base_show(struct device *dev, -			       struct device_attribute *attr, char *buf) +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +static ssize_t base_show(struct device *dev, struct device_attribute *attr, +			 char *buf)  { -	const struct gpio_device *gdev = dev_get_drvdata(dev); +	const struct gpiodev_data *data = dev_get_drvdata(dev); -	return sysfs_emit(buf, "%u\n", gdev->base); +	return sysfs_emit(buf, "%u\n", data->gdev->base);  }  static DEVICE_ATTR_RO(base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ -static ssize_t label_show(struct device *dev, -			       struct device_attribute *attr, char *buf) +static ssize_t label_show(struct device *dev, struct device_attribute *attr, +			  char *buf)  { -	const struct gpio_device *gdev = dev_get_drvdata(dev); +	const struct gpiodev_data *data = dev_get_drvdata(dev); -	return sysfs_emit(buf, "%s\n", gdev->label); +	return sysfs_emit(buf, "%s\n", data->gdev->label);  }  static DEVICE_ATTR_RO(label); -static ssize_t ngpio_show(struct device *dev, -			       struct device_attribute *attr, char *buf) +static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, +			  char *buf)  { -	const struct gpio_device *gdev = dev_get_drvdata(dev); +	const struct gpiodev_data *data = dev_get_drvdata(dev); -	return sysfs_emit(buf, "%u\n", gdev->ngpio); +	return sysfs_emit(buf, "%u\n", data->gdev->ngpio);  }  static DEVICE_ATTR_RO(ngpio); +static int export_gpio_desc(struct gpio_desc *desc) +{ +	int offset, ret; + +	CLASS(gpio_chip_guard, guard)(desc); +	if (!guard.gc) +		return -ENODEV; + +	offset = gpio_chip_hwgpio(desc); +	if (!gpiochip_line_is_valid(guard.gc, offset)) { +		pr_debug_ratelimited("%s: GPIO %d masked\n", __func__, +				     gpio_chip_hwgpio(desc)); +		return -EINVAL; +	} + +	/* +	 * No extra locking here; FLAG_SYSFS just signifies that the +	 * request and export were done by on behalf of userspace, so +	 * they may be undone on its behalf too. +	 */ + +	ret = gpiod_request_user(desc, "sysfs"); +	if (ret) +		return ret; + +	ret = gpiod_set_transitory(desc, false); +	if (ret) { +		gpiod_free(desc); +		return ret; +	} + +	ret = gpiod_export(desc, true); +	if (ret < 0) { +		gpiod_free(desc); +	} else { +		set_bit(FLAG_SYSFS, &desc->flags); +		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); +	} + +	return ret; +} + +static int unexport_gpio_desc(struct gpio_desc *desc) +{ +	/* +	 * No extra locking here; FLAG_SYSFS just signifies that the +	 * request and export were done by on behalf of userspace, so +	 * they may be undone on its behalf too. +	 */ +	if (!test_and_clear_bit(FLAG_SYSFS, &desc->flags)) +		return -EINVAL; + +	gpiod_unexport(desc); +	gpiod_free(desc); + +	return 0; +} + +static ssize_t do_chip_export_store(struct device *dev, +				    struct device_attribute *attr, +				    const char *buf, ssize_t size, +				    int (*handler)(struct gpio_desc *desc)) +{ +	struct gpiodev_data *data = dev_get_drvdata(dev); +	struct gpio_device *gdev = data->gdev; +	struct gpio_desc *desc; +	unsigned int gpio; +	int ret; + +	ret = kstrtouint(buf, 0, &gpio); +	if (ret) +		return ret; + +	desc = gpio_device_get_desc(gdev, gpio); +	if (IS_ERR(desc)) +		return PTR_ERR(desc); + +	ret = handler(desc); +	if (ret) +		return ret; + +	return size; +} + +static ssize_t chip_export_store(struct device *dev, +				 struct device_attribute *attr, +				 const char *buf, size_t size) +{ +	return do_chip_export_store(dev, attr, buf, size, export_gpio_desc); +} + +static struct device_attribute dev_attr_export = __ATTR(export, 0200, NULL, +							chip_export_store); + +static ssize_t chip_unexport_store(struct device *dev, +				   struct device_attribute *attr, +				   const char *buf, size_t size) +{ +	return do_chip_export_store(dev, attr, buf, size, unexport_gpio_desc); +} + +static struct device_attribute dev_attr_unexport = __ATTR(unexport, 0200, +							  NULL, +							  chip_unexport_store); + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)  static struct attribute *gpiochip_attrs[] = {  	&dev_attr_base.attr,  	&dev_attr_label.attr, @@ -433,7 +583,18 @@ static struct attribute *gpiochip_attrs[] = {  	NULL,  };  ATTRIBUTE_GROUPS(gpiochip); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ +static struct attribute *gpiochip_ext_attrs[] = { +	&dev_attr_label.attr, +	&dev_attr_ngpio.attr, +	&dev_attr_export.attr, +	&dev_attr_unexport.attr, +	NULL +}; +ATTRIBUTE_GROUPS(gpiochip_ext); + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)  /*   * /sys/class/gpio/export ... write-only   *	integer N ... number of GPIO to export (full access) @@ -441,11 +602,11 @@ ATTRIBUTE_GROUPS(gpiochip);   *	integer N ... number of GPIO to unexport   */  static ssize_t export_store(const struct class *class, -				const struct class_attribute *attr, -				const char *buf, size_t len) +			    const struct class_attribute *attr, +			    const char *buf, size_t len)  {  	struct gpio_desc *desc; -	int status, offset; +	int status;  	long gpio;  	status = kstrtol(buf, 0, &gpio); @@ -459,40 +620,7 @@ static ssize_t export_store(const struct class *class,  		return -EINVAL;  	} -	CLASS(gpio_chip_guard, guard)(desc); -	if (!guard.gc) -		return -ENODEV; - -	offset = gpio_chip_hwgpio(desc); -	if (!gpiochip_line_is_valid(guard.gc, offset)) { -		pr_debug_ratelimited("%s: GPIO %ld masked\n", __func__, gpio); -		return -EINVAL; -	} - -	/* No extra locking here; FLAG_SYSFS just signifies that the -	 * request and export were done by on behalf of userspace, so -	 * they may be undone on its behalf too. -	 */ - -	status = gpiod_request_user(desc, "sysfs"); -	if (status) -		goto done; - -	status = gpiod_set_transitory(desc, false); -	if (status) { -		gpiod_free(desc); -		goto done; -	} - -	status = gpiod_export(desc, true); -	if (status < 0) { -		gpiod_free(desc); -	} else { -		set_bit(FLAG_SYSFS, &desc->flags); -		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); -	} - -done: +	status = export_gpio_desc(desc);  	if (status)  		pr_debug("%s: status %d\n", __func__, status);  	return status ? : len; @@ -500,8 +628,8 @@ done:  static CLASS_ATTR_WO(export);  static ssize_t unexport_store(const struct class *class, -				const struct class_attribute *attr, -				const char *buf, size_t len) +			      const struct class_attribute *attr, +			      const char *buf, size_t len)  {  	struct gpio_desc *desc;  	int status; @@ -509,7 +637,7 @@ static ssize_t unexport_store(const struct class *class,  	status = kstrtol(buf, 0, &gpio);  	if (status < 0) -		goto done; +		return status;  	desc = gpio_to_desc(gpio);  	/* reject bogus commands (gpiod_unexport() ignores them) */ @@ -518,18 +646,7 @@ static ssize_t unexport_store(const struct class *class,  		return -EINVAL;  	} -	status = -EINVAL; - -	/* No extra locking here; FLAG_SYSFS just signifies that the -	 * request and export were done by on behalf of userspace, so -	 * they may be undone on its behalf too. -	 */ -	if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { -		gpiod_unexport(desc); -		gpiod_free(desc); -		status = 0; -	} -done: +	status = unexport_gpio_desc(desc);  	if (status)  		pr_debug("%s: status %d\n", __func__, status);  	return status ? : len; @@ -542,12 +659,55 @@ static struct attribute *gpio_class_attrs[] = {  	NULL,  };  ATTRIBUTE_GROUPS(gpio_class); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */  static const struct class gpio_class = {  	.name =		"gpio", +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)  	.class_groups =	gpio_class_groups, +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ +}; + +static int match_gdev(struct device *dev, const void *desc) +{ +	struct gpiodev_data *data = dev_get_drvdata(dev); +	const struct gpio_device *gdev = desc; + +	return data && data->gdev == gdev; +} + +static struct gpiodev_data * +gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock) +{ +	/* +	 * Find the first device in GPIO class that matches. Whether that's +	 * the one indexed by GPIO base or device ID doesn't matter, it has +	 * the same address set as driver data. +	 */ +	struct device *cdev __free(put_device) = class_find_device(&gpio_class, +								   NULL, gdev, +								   match_gdev); +	if (!cdev) +		return NULL; + +	return dev_get_drvdata(cdev);  }; +static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name, +			    ssize_t (*show)(struct device *dev, +					    struct device_attribute *attr, +					    char *buf), +			    ssize_t (*store)(struct device *dev, +					     struct device_attribute *attr, +					     const char *buf, size_t count)) +{ +	sysfs_attr_init(&dev_attr->attr); +	dev_attr->attr.name = name; +	dev_attr->attr.mode = 0644; +	dev_attr->show = show; +	dev_attr->store = store; +} +  /**   * gpiod_export - export a GPIO through sysfs   * @desc: GPIO to make available, already requested @@ -566,9 +726,11 @@ static const struct class gpio_class = {   */  int gpiod_export(struct gpio_desc *desc, bool direction_may_change)  { +	char *path __free(kfree) = NULL; +	struct gpiodev_data *gdev_data; +	struct gpiod_data *desc_data;  	struct gpio_device *gdev; -	struct gpiod_data *data; -	struct device *dev; +	struct attribute **attrs;  	int status;  	/* can't export until sysfs is available ... */ @@ -593,43 +755,116 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)  	guard(mutex)(&sysfs_lock); -	/* check if chip is being removed */ -	if (!gdev->mockdev) { -		status = -ENODEV; -		goto err_clear_bit; -	} -  	if (!test_bit(FLAG_REQUESTED, &desc->flags)) {  		gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__);  		status = -EPERM;  		goto err_clear_bit;  	} -	data = kzalloc(sizeof(*data), GFP_KERNEL); -	if (!data) { +	desc_data = kzalloc(sizeof(*desc_data), GFP_KERNEL); +	if (!desc_data) {  		status = -ENOMEM;  		goto err_clear_bit;  	} -	data->desc = desc; -	mutex_init(&data->mutex); +	desc_data->desc = desc; +	mutex_init(&desc_data->mutex);  	if (guard.gc->direction_input && guard.gc->direction_output) -		data->direction_can_change = direction_may_change; +		desc_data->direction_can_change = direction_may_change;  	else -		data->direction_can_change = false; +		desc_data->direction_can_change = false; -	dev = device_create_with_groups(&gpio_class, &gdev->dev, -					MKDEV(0, 0), data, gpio_groups, -					"gpio%u", desc_to_gpio(desc)); -	if (IS_ERR(dev)) { -		status = PTR_ERR(dev); +	gpiod_attr_init(&desc_data->dir_attr, "direction", +			direction_show, direction_store); +	gpiod_attr_init(&desc_data->val_attr, "value", value_show, value_store); + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +	gpiod_attr_init(&desc_data->edge_attr, "edge", edge_show, edge_store); +	gpiod_attr_init(&desc_data->active_low_attr, "active_low", +			active_low_show, active_low_store); + +	attrs = desc_data->class_attrs; +	desc_data->class_attr_group.is_visible = gpio_is_visible; +	attrs[GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION] = &desc_data->dir_attr.attr; +	attrs[GPIO_SYSFS_LINE_CLASS_ATTR_VALUE] = &desc_data->val_attr.attr; +	attrs[GPIO_SYSFS_LINE_CLASS_ATTR_EDGE] = &desc_data->edge_attr.attr; +	attrs[GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW] = &desc_data->active_low_attr.attr; + +	desc_data->class_attr_group.attrs = desc_data->class_attrs; +	desc_data->class_attr_groups[0] = &desc_data->class_attr_group; + +	/* +	 * Note: we need to continue passing desc_data here as there's still +	 * at least one known user of gpiod_export_link() in the tree. This +	 * function still uses class_find_device() internally. +	 */ +	desc_data->dev = device_create_with_groups(&gpio_class, &gdev->dev, +						   MKDEV(0, 0), desc_data, +						   desc_data->class_attr_groups, +						   "gpio%u", +						   desc_to_gpio(desc)); +	if (IS_ERR(desc_data->dev)) { +		status = PTR_ERR(desc_data->dev);  		goto err_free_data;  	} +	desc_data->value_kn = sysfs_get_dirent(desc_data->dev->kobj.sd, +						       "value"); +	if (!desc_data->value_kn) { +		status = -ENODEV; +		goto err_unregister_device; +	} +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + +	gdev_data = gdev_get_data(gdev); +	if (!gdev_data) { +		status = -ENODEV; +		goto err_put_dirent; +	} + +	desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u", +						    gpio_chip_hwgpio(desc)); +	if (!desc_data->chip_attr_group.name) { +		status = -ENOMEM; +		goto err_put_dirent; +	} + +	attrs = desc_data->chip_attrs; +	desc_data->chip_attr_group.is_visible = gpio_is_visible; +	attrs[GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION] = &desc_data->dir_attr.attr; +	attrs[GPIO_SYSFS_LINE_CHIP_ATTR_VALUE] = &desc_data->val_attr.attr; + +	desc_data->chip_attr_group.attrs = attrs; +	desc_data->chip_attr_groups[0] = &desc_data->chip_attr_group; + +	desc_data->parent = &gdev_data->cdev_id->kobj; +	status = sysfs_create_groups(desc_data->parent, +				     desc_data->chip_attr_groups); +	if (status) +		goto err_free_name; + +	path = kasprintf(GFP_KERNEL, "gpio%u/value", gpio_chip_hwgpio(desc)); +	if (!path) { +		status = -ENOMEM; +		goto err_remove_groups; +	} + +	list_add(&desc_data->list, &gdev_data->exported_lines); +  	return 0; +err_remove_groups: +	sysfs_remove_groups(desc_data->parent, desc_data->chip_attr_groups); +err_free_name: +	kfree(desc_data->chip_attr_group.name); +err_put_dirent: +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +	sysfs_put(desc_data->value_kn); +err_unregister_device: +	device_unregister(desc_data->dev);  err_free_data: -	kfree(data); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ +	kfree(desc_data);  err_clear_bit:  	clear_bit(FLAG_EXPORT, &desc->flags);  	gpiod_dbg(desc, "%s: status %d\n", __func__, status); @@ -637,12 +872,14 @@ err_clear_bit:  }  EXPORT_SYMBOL_GPL(gpiod_export); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)  static int match_export(struct device *dev, const void *desc)  {  	struct gpiod_data *data = dev_get_drvdata(dev); -	return data->desc == desc; +	return gpiod_is_equal(data->desc, desc);  } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */  /**   * gpiod_export_link - create a sysfs link to an exported GPIO node @@ -659,6 +896,7 @@ static int match_export(struct device *dev, const void *desc)  int gpiod_export_link(struct device *dev, const char *name,  		      struct gpio_desc *desc)  { +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)  	struct device *cdev;  	int ret; @@ -675,6 +913,9 @@ int gpiod_export_link(struct device *dev, const char *name,  	put_device(cdev);  	return ret; +#else +	return -EOPNOTSUPP; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */  }  EXPORT_SYMBOL_GPL(gpiod_export_link); @@ -686,8 +927,9 @@ EXPORT_SYMBOL_GPL(gpiod_export_link);   */  void gpiod_unexport(struct gpio_desc *desc)  { -	struct gpiod_data *data; -	struct device *dev; +	struct gpiod_data *tmp, *desc_data = NULL; +	struct gpiodev_data *gdev_data; +	struct gpio_device *gdev;  	if (!desc) {  		pr_warn("%s: invalid GPIO\n", __func__); @@ -698,32 +940,50 @@ void gpiod_unexport(struct gpio_desc *desc)  		if (!test_bit(FLAG_EXPORT, &desc->flags))  			return; -		dev = class_find_device(&gpio_class, NULL, desc, match_export); -		if (!dev) +		gdev = gpiod_to_gpio_device(desc); +		gdev_data = gdev_get_data(gdev); +		if (!gdev_data)  			return; -		data = dev_get_drvdata(dev); +		list_for_each_entry(tmp, &gdev_data->exported_lines, list) { +			if (gpiod_is_equal(desc, tmp->desc)) { +				desc_data = tmp; +				break; +			} +		} + +		if (!desc_data) +			return; + +		list_del(&desc_data->list);  		clear_bit(FLAG_EXPORT, &desc->flags); -		device_unregister(dev); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +		sysfs_put(desc_data->value_kn); +		device_unregister(desc_data->dev);  		/*  		 * Release irq after deregistration to prevent race with  		 * edge_store.  		 */ -		if (data->irq_flags) -			gpio_sysfs_free_irq(dev); +		if (desc_data->irq_flags) +			gpio_sysfs_free_irq(desc_data); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + +		sysfs_remove_groups(desc_data->parent, +				    desc_data->chip_attr_groups);  	} -	put_device(dev); -	kfree(data); +	mutex_destroy(&desc_data->mutex); +	kfree(desc_data);  }  EXPORT_SYMBOL_GPL(gpiod_unexport);  int gpiochip_sysfs_register(struct gpio_device *gdev)  { +	struct gpiodev_data *data;  	struct gpio_chip *chip;  	struct device *parent; -	struct device *dev; +	int err;  	/*  	 * Many systems add gpio chips for SOC support very early, @@ -749,32 +1009,61 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)  	else  		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), gdev, -					gpiochip_groups, GPIOCHIP_NAME "%d", -					chip->base); -	if (IS_ERR(dev)) -		return PTR_ERR(dev); +	data = kmalloc(sizeof(*data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	data->gdev = gdev; +	INIT_LIST_HEAD(&data->exported_lines);  	guard(mutex)(&sysfs_lock); -	gdev->mockdev = dev; + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +	/* use chip->base for the ID; it's already known to be unique */ +	data->cdev_base = device_create_with_groups(&gpio_class, parent, +						    MKDEV(0, 0), data, +						    gpiochip_groups, +						    GPIOCHIP_NAME "%d", +						    chip->base); +	if (IS_ERR(data->cdev_base)) { +		err = PTR_ERR(data->cdev_base); +		kfree(data); +		return err; +	} +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + +	data->cdev_id = device_create_with_groups(&gpio_class, parent, +						  MKDEV(0, 0), data, +						  gpiochip_ext_groups, +						  "chip%d", gdev->id); +	if (IS_ERR(data->cdev_id)) { +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +		device_unregister(data->cdev_base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ +		err = PTR_ERR(data->cdev_id); +		kfree(data); +		return err; +	}  	return 0;  }  void gpiochip_sysfs_unregister(struct gpio_device *gdev)  { +	struct gpiodev_data *data;  	struct gpio_desc *desc;  	struct gpio_chip *chip;  	scoped_guard(mutex, &sysfs_lock) { -		if (!gdev->mockdev) +		data = gdev_get_data(gdev); +		if (!data)  			return; -		device_unregister(gdev->mockdev); - -		/* prevent further gpiod exports */ -		gdev->mockdev = NULL; +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) +		device_unregister(data->cdev_base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ +		device_unregister(data->cdev_id); +		kfree(data);  	}  	guard(srcu)(&gdev->srcu); @@ -800,9 +1089,6 @@ 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); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 113c5d90f2df..0d2b470a252e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -75,6 +75,19 @@ static const struct bus_type gpio_bus_type = {  };  /* + * At the end we want all GPIOs to be dynamically allocated from 0. + * However, some legacy drivers still perform fixed allocation. + * Until they are all fixed, leave 0-512 space for them. + */ +#define GPIO_DYNAMIC_BASE	512 +/* + * Define the maximum of the possible GPIO in the global numberspace. + * While the GPIO base and numbers are positive, we limit it with signed + * maximum as a lot of code is using negative values for special cases. + */ +#define GPIO_DYNAMIC_MAX	INT_MAX + +/*   * Number of GPIOs to use for the fast path in set array   */  #define FASTPATH_NGPIO CONFIG_GPIOLIB_FASTPATH_LIMIT @@ -342,6 +355,52 @@ static int gpiochip_find_base_unlocked(u16 ngpio)  	}  } +/* + * This descriptor validation needs to be inserted verbatim into each + * function taking a descriptor, so we need to use a preprocessor + * macro to avoid endless duplication. If the desc is NULL it is an + * optional GPIO and calls should just bail out. + */ +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: %pe)\n", func, desc); +		return PTR_ERR(desc); +	} + +	return 1; +} + +#define VALIDATE_DESC(desc) do { \ +	int __valid = validate_desc(desc, __func__); \ +	if (__valid <= 0) \ +		return __valid; \ +	} while (0) + +#define VALIDATE_DESC_VOID(desc) do { \ +	int __valid = validate_desc(desc, __func__); \ +	if (__valid <= 0) \ +		return; \ +	} while (0) + +/** + * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. + * @desc: Descriptor to compare. + * @other: The second descriptor to compare against. + * + * Returns: + * True if the descriptors refer to the same physical pin. False otherwise. + */ +bool gpiod_is_equal(const struct gpio_desc *desc, const struct gpio_desc *other) +{ +	return validate_desc(desc, __func__) > 0 && +	       !IS_ERR_OR_NULL(other) && desc == other; +} +EXPORT_SYMBOL_GPL(gpiod_is_equal); +  static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset)  {  	int ret; @@ -376,11 +435,8 @@ int gpiod_get_direction(struct gpio_desc *desc)  	unsigned int offset;  	int ret; -	/* -	 * 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)) +	ret = validate_desc(desc, __func__); +	if (ret <= 0)  		return -EINVAL;  	CLASS(gpio_chip_guard, guard)(desc); @@ -880,14 +936,12 @@ static void machine_gpiochip_add(struct gpio_chip *gc)  {  	struct gpiod_hog *hog; -	mutex_lock(&gpio_machine_hogs_mutex); +	guard(mutex)(&gpio_machine_hogs_mutex);  	list_for_each_entry(hog, &gpio_machine_hogs, list) {  		if (!strcmp(gc->label, hog->chip_label))  			gpiochip_machine_hog(gc, hog);  	} - -	mutex_unlock(&gpio_machine_hogs_mutex);  }  static void gpiochip_setup_devs(void) @@ -981,12 +1035,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,  	struct gpio_device *gdev;  	unsigned int desc_index;  	int base = 0; -	int ret = 0; - -	/* Only allow one set() and one set_multiple(). */ -	if ((gc->set && gc->set_rv) || -	    (gc->set_multiple && gc->set_multiple_rv)) -		return -EINVAL; +	int ret;  	/*  	 * First: allocate and populate the internal stat container, and @@ -1006,11 +1055,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,  	device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); -	gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); -	if (gdev->id < 0) { -		ret = gdev->id; +	ret = ida_alloc(&gpio_ida, GFP_KERNEL); +	if (ret < 0)  		goto err_free_gdev; -	} +	gdev->id = ret;  	ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);  	if (ret) @@ -1513,9 +1561,8 @@ static int gpiochip_hierarchy_irq_domain_translate(struct irq_domain *d,  						   unsigned int *type)  {  	/* We support standard DT translation */ -	if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { -		return irq_domain_translate_twocell(d, fwspec, hwirq, type); -	} +	if (is_of_node(fwspec->fwnode)) +		return irq_domain_translate_twothreecell(d, fwspec, hwirq, type);  	/* This is for board files and others not using DT */  	if (is_fwnode_irqchip(fwspec->fwnode)) { @@ -1817,11 +1864,26 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)  	irq_set_chip_data(irq, NULL);  } +static int gpiochip_irq_select(struct irq_domain *d, struct irq_fwspec *fwspec, +			       enum irq_domain_bus_token bus_token) +{ +	struct fwnode_handle *fwnode = fwspec->fwnode; +	struct gpio_chip *gc = d->host_data; +	unsigned int index = fwspec->param[0]; + +	if (fwspec->param_count == 3 && is_of_node(fwnode)) +		return of_gpiochip_instance_match(gc, index); + +	/* Fallback for twocells */ +	return (fwnode && (d->fwnode == fwnode) && (d->bus_token == bus_token)); +} +  static const struct irq_domain_ops gpiochip_domain_ops = {  	.map	= gpiochip_irq_map,  	.unmap	= gpiochip_irq_unmap, +	.select	= gpiochip_irq_select,  	/* Virtually all GPIO irqchips are twocell:ed */ -	.xlate	= irq_domain_xlate_twocell, +	.xlate	= irq_domain_xlate_twothreecell,  };  static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc) @@ -1841,7 +1903,6 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset)  {  	struct irq_domain *domain = gc->irq.domain; -#ifdef CONFIG_GPIOLIB_IRQCHIP  	/*  	 * Avoid race condition with other code, which tries to lookup  	 * an IRQ before the irqchip has been properly registered, @@ -1849,7 +1910,6 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset)  	 */  	if (!gc->irq.initialized)  		return -EPROBE_DEFER; -#endif  	if (!gpiochip_irqchip_irq_valid(gc, offset))  		return -ENXIO; @@ -2411,37 +2471,6 @@ out_clear_bit:  	return ret;  } -/* - * This descriptor validation needs to be inserted verbatim into each - * function taking a descriptor, so we need to use a preprocessor - * macro to avoid endless duplication. If the desc is NULL it is an - * optional GPIO and calls should just bail out. - */ -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); -	} - -	return 1; -} - -#define VALIDATE_DESC(desc) do { \ -	int __valid = validate_desc(desc, __func__); \ -	if (__valid <= 0) \ -		return __valid; \ -	} while (0) - -#define VALIDATE_DESC_VOID(desc) do { \ -	int __valid = validate_desc(desc, __func__); \ -	if (__valid <= 0) \ -		return; \ -	} while (0) -  int gpiod_request(struct gpio_desc *desc, const char *label)  {  	int ret = -EPROBE_DEFER; @@ -2857,19 +2886,14 @@ static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value)  	lockdep_assert_held(&gc->gpiodev->srcu); -	if (WARN_ON(unlikely(!gc->set && !gc->set_rv))) +	if (WARN_ON(unlikely(!gc->set)))  		return -EOPNOTSUPP; -	if (gc->set_rv) { -		ret = gc->set_rv(gc, offset, value); -		if (ret > 0) -			ret = -EBADE; - -		return ret; -	} +	ret = gc->set(gc, offset, value); +	if (ret > 0) +		ret = -EBADE; -	gc->set(gc, offset, value); -	return 0; +	return ret;  }  static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) @@ -2885,7 +2909,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)  	 * output-only, but if there is then not even a .set() operation it  	 * is pretty tricky to drive the output line.  	 */ -	if (!guard.gc->set && !guard.gc->set_rv && !guard.gc->direction_output) { +	if (!guard.gc->set && !guard.gc->direction_output) {  		gpiod_warn(desc,  			   "%s: missing set() and direction_output() operations\n",  			   __func__); @@ -3051,7 +3075,7 @@ set_output_flag:   */  int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)  { -	int ret = 0; +	int ret;  	VALIDATE_DESC(desc); @@ -3084,7 +3108,7 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);   */  int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)  { -	int ret = 0; +	int ret;  	VALIDATE_DESC(desc); @@ -3277,14 +3301,15 @@ 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)  { -	int ret; -	  	lockdep_assert_held(&gc->gpiodev->srcu);  	if (gc->get_multiple) { +		int ret; +  		ret = gc->get_multiple(gc, mask, bits);  		if (ret > 0)  			return -EBADE; +		return ret;  	}  	if (gc->get) { @@ -3599,6 +3624,9 @@ static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)  static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)  { +	if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags))) +		return -EPERM; +  	CLASS(gpio_chip_guard, guard)(desc);  	if (!guard.gc)  		return -ENODEV; @@ -3627,19 +3655,14 @@ static int gpiochip_set_multiple(struct gpio_chip *gc,  	lockdep_assert_held(&gc->gpiodev->srcu); -	if (gc->set_multiple_rv) { -		ret = gc->set_multiple_rv(gc, mask, bits); +	if (gc->set_multiple) { +		ret = gc->set_multiple(gc, mask, bits);  		if (ret > 0)  			ret = -EBADE;  		return ret;  	} -	if (gc->set_multiple) { -		gc->set_multiple(gc, mask, bits); -		return 0; -	} -  	/* set outputs if the corresponding mask bit is set */  	for_each_set_bit(i, mask, gc->ngpio) {  		ret = gpiochip_set(gc, i, test_bit(i, bits)); @@ -3670,6 +3693,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,  		if (!can_sleep)  			WARN_ON(array_info->gdev->can_sleep); +		for (i = 0; i < array_size; i++) { +			if (unlikely(!test_bit(FLAG_IS_OUT, +					       &desc_array[i]->flags))) +				return -EPERM; +		} +  		guard(srcu)(&array_info->gdev->srcu);  		gc = srcu_dereference(array_info->gdev->chip,  				      &array_info->gdev->srcu); @@ -3729,6 +3758,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,  			int hwgpio = gpio_chip_hwgpio(desc);  			int value = test_bit(i, value_bitmap); +			if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags))) +				return -EPERM; +  			/*  			 * Pins applicable for fast input but not for  			 * fast output processing may have been already @@ -3950,13 +3982,10 @@ int gpiod_to_irq(const struct gpio_desc *desc)  	struct gpio_device *gdev;  	struct gpio_chip *gc;  	int offset; +	int ret; -	/* -	 * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics -	 * requires this function to not return zero on an invalid descriptor -	 * but rather a negative error number. -	 */ -	if (IS_ERR_OR_NULL(desc)) +	ret = validate_desc(desc, __func__); +	if (ret <= 0)  		return -EINVAL;  	gdev = desc->gdev; @@ -3968,13 +3997,12 @@ int gpiod_to_irq(const struct gpio_desc *desc)  	offset = gpio_chip_hwgpio(desc);  	if (gc->to_irq) { -		int retirq = gc->to_irq(gc, offset); +		ret = gc->to_irq(gc, offset); +		if (ret) +			return ret;  		/* Zero means NO_IRQ */ -		if (!retirq) -			return -ENXIO; - -		return retirq; +		return -ENXIO;  	}  #ifdef CONFIG_GPIOLIB_IRQCHIP  	if (gc->irq.chip) { @@ -4329,12 +4357,10 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)  {  	unsigned int i; -	mutex_lock(&gpio_lookup_lock); +	guard(mutex)(&gpio_lookup_lock);  	for (i = 0; i < n; i++)  		list_add_tail(&tables[i]->list, &gpio_lookup_list); - -	mutex_unlock(&gpio_lookup_lock);  }  /** @@ -4393,11 +4419,9 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)  	if (!table)  		return; -	mutex_lock(&gpio_lookup_lock); +	guard(mutex)(&gpio_lookup_lock);  	list_del(&table->list); - -	mutex_unlock(&gpio_lookup_lock);  }  EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); @@ -4409,7 +4433,7 @@ void gpiod_add_hogs(struct gpiod_hog *hogs)  {  	struct gpiod_hog *hog; -	mutex_lock(&gpio_machine_hogs_mutex); +	guard(mutex)(&gpio_machine_hogs_mutex);  	for (hog = &hogs[0]; hog->chip_label; hog++) {  		list_add_tail(&hog->list, &gpio_machine_hogs); @@ -4423,8 +4447,6 @@ void gpiod_add_hogs(struct gpiod_hog *hogs)  		if (gdev)  			gpiochip_machine_hog(gpio_device_get_chip(gdev), hog);  	} - -	mutex_unlock(&gpio_machine_hogs_mutex);  }  EXPORT_SYMBOL_GPL(gpiod_add_hogs); @@ -4432,10 +4454,10 @@ void gpiod_remove_hogs(struct gpiod_hog *hogs)  {  	struct gpiod_hog *hog; -	mutex_lock(&gpio_machine_hogs_mutex); +	guard(mutex)(&gpio_machine_hogs_mutex); +  	for (hog = &hogs[0]; hog->chip_label; hog++)  		list_del(&hog->list); -	mutex_unlock(&gpio_machine_hogs_mutex);  }  EXPORT_SYMBOL_GPL(gpiod_remove_hogs); @@ -5114,8 +5136,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional);   */  void gpiod_put(struct gpio_desc *desc)  { -	if (desc) -		gpiod_free(desc); +	gpiod_free(desc);  }  EXPORT_SYMBOL_GPL(gpiod_put); @@ -5199,8 +5220,8 @@ core_initcall(gpiolib_dev_init);  static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)  {  	bool active_low, is_irq, is_out; -	unsigned int gpio = gdev->base;  	struct gpio_desc *desc; +	unsigned int gpio = 0;  	struct gpio_chip *gc;  	unsigned long flags;  	int value; @@ -5304,8 +5325,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)  		return 0;  	} -	seq_printf(s, "%s: GPIOs %u-%u", dev_name(&gdev->dev), gdev->base, -		   gdev->base + gdev->ngpio - 1); +	seq_printf(s, "%s: %u GPIOs", dev_name(&gdev->dev), gdev->ngpio);  	parent = gc->parent;  	if (parent)  		seq_printf(s, ", parent: %s/%s", diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 58f64056de77..9b74738a9ca5 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -27,8 +27,6 @@   * @dev: the GPIO device struct   * @chrdev: character device for the GPIO device   * @id: numerical ID number for the GPIO chip - * @mockdev: class device used by the deprecated sysfs interface (may be - * NULL)   * @owner: helps prevent removal of modules exporting active GPIOs   * @chip: pointer to the corresponding gpiochip, holding static   * data for this device @@ -65,7 +63,6 @@ struct gpio_device {  	struct device		dev;  	struct cdev		chrdev;  	int			id; -	struct device		*mockdev;  	struct module		*owner;  	struct gpio_chip __rcu	*chip;  	struct gpio_desc	*descs;  | 
