summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-13 20:10:58 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-13 20:10:58 -0700
commit1334d2a3b3235d062e5e1f51aebe7a64ed57cf72 (patch)
treed15ea79b8884902e196dc76210829594d70a1fb7 /drivers/gpio
parentfbfb6bd927c9ac6ea155471cc7ced8e16b37c2cb (diff)
parentca13ab654064fee86d6e7c9e87d0af7789561509 (diff)
Merge tag 'gpio-updates-for-v7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski: "For this merge window we have two new drivers: support for GPIO-signalled ACPI events on Intel platforms and a generic GPIO-over-pinctrl driver using the ARM SCMI protocol for controlling pins. Several things have been reworked in GPIO core: we unduplicated GPIO hog handling, reduced the number of SRCU locks and dereferences, improved support for software-node-based lookup and removed more legacy code after converting remaining users to modern alternatives. There's also a number of driver reworks and refactoring, documentation updates, some bug-fixes and new tests. GPIO core: - defer probe on software node lookups when the remote software node exists but has not been registered as a firmware node yet - unify GPIO hog handling by moving code duplicated in OF and ACPI modules into GPIO core and allow setting up hogs with software nodes - allow matching GPIO controllers by secondary firmware node if matching by primary does not succeed - demote deferral warnings to debug level as they are quite normal when using software nodes which don't support fw_devlink yet - disable the legacy GPIO character device uAPI v1 supprt in Kconfig by default - rework several core functions in preparation for the upcoming Revocable helper library for protecting resources against sudden removal, this reduces the number of SRCU dereferences in GPIO core - simplify file descriptor logic in GPIO character device code by using FD_PREPARE() - introduce a header defining symbols used by both GPIO consumers and providers to avoid having to include provider-specific headers from drivers which only consume GPIOs - replace snprintf() with strscpy() where formatting is not required New drivers: - add the gpio-by-pinctrl generic driver using the ARM SCMI protocol to control GPIOs (along with SCMI changes pulled from the pinctrl tree) - add a driver providing support for handling of platform events via GPIO-signalled ACPI events (used on Intel Nova Lake and later platforms) Driver changes: - extend the gpio-kempld driver with support for more recent models, interrupts and setting/getting multiple values at once - improve interrupt handling in gpio-brcmstb - add support for multi-SoC systems in gpio-tegra186 - make sure we return correct values from the .get() callbacks in several GPIO drivers by normalizing any values other than 0, 1 or negative error numbers - use flexible arrays in several drivers to reduce the number of required memory allocations - simplify synchronous waiting for virtual drivers to probe and remove the dedicated, a bit overengineered helper library dev-sync-probe - remove unneeded Kconfig dependencies on OF_GPIO in several drivers and subsystems - convert the two remaining users of of_get_named_gpio() to using GPIO descriptors and remove the (no longer used) function along with the header that declares it - add missing includes in gpio-mmio - shrink and simplify code in gpio-max732x by using guard(mutex) - remove duplicated code handling the 'ngpios' property from gpio-ts4800, it's already handled in GPIO core - use correct variable type in gpio-aspeed - add support for a new model in gpio-realtek-otto - allow to specify the active-low setting of simulated hogs over the configfs interface (in addition to existing devicetree support) in gpio-sim Bug fixes: - clear the OF_POPULATED flag on hog nodes in GPIO chip remove path on OF systems - fix resource leaks in error path in gpiochip_add_data_with_key() - drop redundant device reference in gpio-mpsse Tests: - add selftests for use-after-free cases in GPIO character device code DT bindings: - add a DT binding document for SCMI based, gpio-over-pinctrl devices - fix interrupt description in microchip,mpfs-gpio - add new compatible for gpio-realtek-otto - describe the resets of the mpfs-gpio controller - fix maintainer's email in gpio-delay bindings - remove the binding document for cavium,thunder-8890 as the corresponding device is bound over PCI and not firmware nodes Documentation: - update the recommended way of converting legacy boards to using software nodes for GPIO description - describe GPIO line value semantics - misc updates to kerneldocs Misc: - convert OMAP1 ams-delta board to using GPIO hogs described with software nodes" * tag 'gpio-updates-for-v7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (79 commits) gpio: swnode: defer probe on references to unregistered software nodes dt-bindings: gpio: cavium,thunder-8890: Remove DT binding Documentation: gpio: update the preferred method for using software node lookup gpio: gpio-by-pinctrl: s/used to do/is used to do/ gpio: aspeed: fix unsigned long int declaration gpio: rockchip: convert to dynamic GPIO base allocation gpio: remove dev-sync-probe gpio: virtuser: stop using dev-sync-probe gpio: aggregator: stop using dev-sync-probe gpio: sim: stop using dev-sync-probe gpio: Add Intel Nova Lake ACPI GPIO events driver gpiolib: Make deferral warnings debug messages gpiolib: fix hogs with multiple lines gpio: fix up CONFIG_OF dependencies gpio: gpio-by-pinctrl: add pinctrl based generic GPIO driver gpio: dt-bindings: Add GPIO on top of generic pin control firmware: arm_scmi: Allow PINCTRL_REQUEST to return EOPNOTSUPP pinctrl: scmi: ignore PIN_CONFIG_PERSIST_STATE pinctrl: scmi: Delete PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS support pinctrl: scmi: Add SCMI_PIN_INPUT_VALUE ...
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig108
-rw-r--r--drivers/gpio/Makefile5
-rw-r--r--drivers/gpio/TODO28
-rw-r--r--drivers/gpio/dev-sync-probe.c97
-rw-r--r--drivers/gpio/dev-sync-probe.h25
-rw-r--r--drivers/gpio/gpio-aggregator.c38
-rw-r--r--drivers/gpio/gpio-aspeed.c2
-rw-r--r--drivers/gpio/gpio-bcm-kona.c37
-rw-r--r--drivers/gpio/gpio-bd9571mwv.c2
-rw-r--r--drivers/gpio/gpio-brcmstb.c125
-rw-r--r--drivers/gpio/gpio-by-pinctrl.c101
-rw-r--r--drivers/gpio/gpio-cgbc.c4
-rw-r--r--drivers/gpio/gpio-cs5535.c48
-rw-r--r--drivers/gpio/gpio-da9055.c2
-rw-r--r--drivers/gpio/gpio-dwapb.c19
-rw-r--r--drivers/gpio/gpio-htc-egpio.c13
-rw-r--r--drivers/gpio/gpio-kempld.c289
-rw-r--r--drivers/gpio/gpio-lp873x.c2
-rw-r--r--drivers/gpio/gpio-max732x.c24
-rw-r--r--drivers/gpio/gpio-mmio.c4
-rw-r--r--drivers/gpio/gpio-mpsse.c13
-rw-r--r--drivers/gpio/gpio-novalake-events.c323
-rw-r--r--drivers/gpio/gpio-realtek-otto.c4
-rw-r--r--drivers/gpio/gpio-rockchip.c2
-rw-r--r--drivers/gpio/gpio-sim.c241
-rw-r--r--drivers/gpio/gpio-stp-xway.c2
-rw-r--r--drivers/gpio/gpio-tegra186.c45
-rw-r--r--drivers/gpio/gpio-tps65086.c2
-rw-r--r--drivers/gpio/gpio-ts4800.c10
-rw-r--r--drivers/gpio/gpio-viperboard.c2
-rw-r--r--drivers/gpio/gpio-virtuser.c30
-rw-r--r--drivers/gpio/gpiolib-acpi-core.c70
-rw-r--r--drivers/gpio/gpiolib-cdev.c159
-rw-r--r--drivers/gpio/gpiolib-cdev.h2
-rw-r--r--drivers/gpio/gpiolib-of.c183
-rw-r--r--drivers/gpio/gpiolib-of.h10
-rw-r--r--drivers/gpio/gpiolib-swnode.c8
-rw-r--r--drivers/gpio/gpiolib-sysfs.c30
-rw-r--r--drivers/gpio/gpiolib-sysfs.h8
-rw-r--r--drivers/gpio/gpiolib.c222
-rw-r--r--drivers/gpio/gpiolib.h6
41 files changed, 1364 insertions, 981 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b45fb799e36c..c5ede0e4a32a 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -95,7 +95,6 @@ config GPIO_CDEV
config GPIO_CDEV_V1
bool "Support GPIO ABI Version 1"
- default y
depends on GPIO_CDEV
help
Say Y here to support version 1 of the GPIO CDEV ABI.
@@ -103,8 +102,6 @@ config GPIO_CDEV_V1
This ABI version is deprecated.
Please use the latest ABI for new developments.
- If unsure, say Y.
-
config GPIO_GENERIC
depends on HAS_IOMEM # Only for IOMEM drivers
tristate
@@ -145,7 +142,7 @@ menu "Memory mapped GPIO drivers"
config GPIO_74XX_MMIO
tristate "GPIO driver for 74xx-ICs with MMIO access"
- depends on OF_GPIO
+ depends on OF
select GPIO_GENERIC
help
Say yes here to support GPIO functionality for 74xx-compatible ICs
@@ -175,14 +172,14 @@ config GPIO_AMDPT
config GPIO_ASPEED
tristate "Aspeed GPIO support"
- depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO
+ depends on ARCH_ASPEED || COMPILE_TEST
select GPIOLIB_IRQCHIP
help
Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers.
config GPIO_ASPEED_SGPIO
bool "Aspeed SGPIO support"
- depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO
+ depends on ARCH_ASPEED || COMPILE_TEST
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
@@ -201,7 +198,6 @@ config GPIO_ATH79
config GPIO_RASPBERRYPI_EXP
tristate "Raspberry Pi 3 GPIO Expander"
default RASPBERRYPI_FIRMWARE
- depends on OF_GPIO
# Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only
# happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE.
depends on (ARCH_BCM2835 && RASPBERRYPI_FIRMWARE) || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
@@ -218,7 +214,7 @@ config GPIO_BCM_KONA
config GPIO_BCM_XGS_IPROC
tristate "BRCM XGS iProc GPIO support"
- depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST)
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
default ARCH_BCM_IPROC
@@ -229,7 +225,6 @@ 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
@@ -240,15 +235,29 @@ config GPIO_BLZP1600
config GPIO_BRCMSTB
tristate "BRCMSTB GPIO support"
default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
- depends on OF_GPIO && (ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST)
+ depends on ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST
+ depends on OF
select GPIO_GENERIC
select IRQ_DOMAIN
help
Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
+config GPIO_BY_PINCTRL
+ tristate "GPIO support based on a pure pin control backend"
+ depends on GPIOLIB
+ help
+ Support for generic GPIO handling based on top of pin control.
+ Traditionally, firmware creates a GPIO interface or a pin
+ controller interface and we have a driver to support it. But
+ in SCMI, the pin control interface is generic and we can
+ create a simple GPIO device based on the pin control interface
+ without doing anything custom.
+
+ This driver is used to access GPIOs over the ARM SCMI protocol.
+
config GPIO_CADENCE
tristate "Cadence GPIO support"
- depends on OF_GPIO
+ depends on OF
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
@@ -280,14 +289,13 @@ config GPIO_DWAPB
config GPIO_EIC_SPRD
tristate "Spreadtrum EIC support"
depends on ARCH_SPRD || COMPILE_TEST
- depends on OF_GPIO
select GPIOLIB_IRQCHIP
help
Say yes here to support Spreadtrum EIC device.
config GPIO_EM
tristate "Emma Mobile GPIO"
- depends on (ARCH_EMEV2 || COMPILE_TEST) && OF_GPIO
+ depends on ARCH_EMEV2 || COMPILE_TEST
help
Say yes here to support GPIO on Renesas Emma Mobile SoCs.
@@ -329,7 +337,7 @@ config GPIO_GE_FPGA
config GPIO_FTGPIO010
bool "Faraday FTGPIO010 GPIO"
- depends on OF_GPIO
+ depends on OF
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
default (ARCH_GEMINI || ARCH_MOXART)
@@ -383,7 +391,7 @@ config GPIO_HISI
config GPIO_HLWD
tristate "Nintendo Wii (Hollywood) GPIO"
- depends on OF_GPIO
+ depends on OF
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
@@ -434,7 +442,6 @@ config GPIO_LOONGSON
config GPIO_LOONGSON_64BIT
tristate "Loongson 64 bit GPIO support"
depends on LOONGARCH || COMPILE_TEST
- depends on OF_GPIO
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
@@ -447,7 +454,7 @@ config GPIO_LOONGSON_64BIT
config GPIO_LPC18XX
tristate "NXP LPC18XX/43XX GPIO support"
default y if ARCH_LPC18XX
- depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
+ depends on ARCH_LPC18XX || COMPILE_TEST
select IRQ_DOMAIN_HIERARCHY
select GPIOLIB_IRQCHIP
help
@@ -456,7 +463,8 @@ config GPIO_LPC18XX
config GPIO_LPC32XX
tristate "NXP LPC32XX GPIO support"
- depends on OF_GPIO && (ARCH_LPC32XX || COMPILE_TEST)
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ depends on OF
help
Select this option to enable GPIO driver for
NXP LPC32XX devices.
@@ -499,7 +507,7 @@ config GPIO_MPC8XXX
config GPIO_MT7621
bool "Mediatek MT7621 GPIO Support"
depends on SOC_MT7620 || SOC_MT7621 || COMPILE_TEST
- depends on OF_GPIO
+ depends on OF
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
@@ -592,7 +600,6 @@ config GPIO_RCAR
config GPIO_RDA
bool "RDA Micro GPIO controller support"
depends on ARCH_RDA || COMPILE_TEST
- depends on OF_GPIO
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
@@ -641,8 +648,8 @@ config GPIO_RTD
config GPIO_SAMA5D2_PIOBU
tristate "SAMA5D2 PIOBU GPIO support"
+ depends on OF
depends on MFD_SYSCON
- depends on OF_GPIO
depends on ARCH_AT91 || COMPILE_TEST
select GPIO_SYSCON
help
@@ -654,7 +661,7 @@ config GPIO_SAMA5D2_PIOBU
config GPIO_SIFIVE
tristate "SiFive GPIO support"
- depends on OF_GPIO
+ depends on OF
select IRQ_DOMAIN_HIERARCHY
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
@@ -673,7 +680,6 @@ config GPIO_SIOX
config GPIO_SNPS_CREG
bool "Synopsys GPIO via CREG (Control REGisters) driver"
depends on ARC || COMPILE_TEST
- depends on OF_GPIO
help
This driver supports GPIOs via CREG on various Synopsys SoCs.
This is a single-register MMIO GPIO driver for complex cases
@@ -683,7 +689,7 @@ config GPIO_SNPS_CREG
config GPIO_SPACEMIT_K1
tristate "SPACEMIT K1 GPIO support"
depends on ARCH_SPACEMIT || COMPILE_TEST
- depends on OF_GPIO
+ depends on OF
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
@@ -699,7 +705,6 @@ config GPIO_SPEAR_SPICS
config GPIO_SPRD
tristate "Spreadtrum GPIO support"
depends on ARCH_SPRD || COMPILE_TEST
- depends on OF_GPIO
select GPIOLIB_IRQCHIP
help
Say yes here to support Spreadtrum GPIO device.
@@ -707,7 +712,6 @@ config GPIO_SPRD
config GPIO_STP_XWAY
bool "XWAY STP GPIOs"
depends on SOC_XWAY || COMPILE_TEST
- depends on OF_GPIO
help
This enables support for the Serial To Parallel (STP) unit found on
XWAY SoC. The STP allows the SoC to drive a shift registers cascade,
@@ -742,7 +746,6 @@ config GPIO_TEGRA
tristate "NVIDIA Tegra GPIO support"
default ARCH_TEGRA
depends on ARCH_TEGRA || COMPILE_TEST
- depends on OF_GPIO
select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY
help
@@ -752,7 +755,7 @@ config GPIO_TEGRA186
tristate "NVIDIA Tegra186 GPIO support"
default ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC
depends on ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC || COMPILE_TEST
- depends on OF_GPIO
+ depends on OF
select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY
help
@@ -760,7 +763,6 @@ config GPIO_TEGRA186
config GPIO_TS4800
tristate "TS-4800 DIO blocks and compatibles"
- depends on OF_GPIO
depends on SOC_IMX51 || COMPILE_TEST
select GPIO_GENERIC
help
@@ -780,7 +782,6 @@ config GPIO_THUNDERX
config GPIO_UNIPHIER
tristate "UniPhier GPIO support"
depends on ARCH_UNIPHIER || COMPILE_TEST
- depends on OF_GPIO
select IRQ_DOMAIN_HIERARCHY
help
Say yes here to support UniPhier GPIOs.
@@ -797,7 +798,6 @@ config GPIO_VF610
config GPIO_VISCONTI
tristate "Toshiba Visconti GPIO support"
depends on ARCH_VISCONTI || COMPILE_TEST
- depends on OF_GPIO
select GPIOLIB_IRQCHIP
select GPIO_GENERIC
select IRQ_DOMAIN_HIERARCHY
@@ -806,14 +806,14 @@ config GPIO_VISCONTI
config GPIO_WCD934X
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver"
- depends on MFD_WCD934X && OF_GPIO
+ depends on MFD_WCD934X
help
This driver is to support GPIO block found on the Qualcomm Technologies
Inc WCD9340/WCD9341 Audio Codec.
config GPIO_XGENE
bool "APM X-Gene GPIO controller support"
- depends on ARM64 && OF_GPIO
+ depends on ARM64
help
This driver is to support the GPIO block within the APM X-Gene SoC
platform's generic flash controller. The GPIO pins are muxed with
@@ -1057,6 +1057,32 @@ config GPIO_SCH
The Intel Quark X1000 SoC has 2 GPIOs powered by the core
power well and 6 from the suspend power well.
+config GPIO_NOVALAKE
+ tristate "Intel Nova Lake GPIO-signaled ACPI events support"
+ depends on (X86 || COMPILE_TEST) && ACPI
+ select GPIOLIB_IRQCHIP
+ help
+ Select this to enable GPIO-signaled ACPI events support on platforms
+ with the following SoCs:
+
+ - Intel Nova Lake
+
+ This driver adds support for new mode of handling platform events,
+ through the use of GPIO-signaled ACPI events. Main purpose is to
+ handle platform IRQs that originate in PCH components, for example
+ interrupt triggered by Power Management Event (PME).
+
+ This driver, at this time, is not required to handle platform events.
+ Listed platform(s) will stay in legacy mode, handling ACPI events as
+ in previous generations. However, future platforms will eventually
+ switch to new handling mode, requiring this driver to run events
+ properly.
+
+ Driver supports up to 128 GPIO pins per GPE block.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-novalake-events.
+
config GPIO_SCH311X
tristate "SMSC SCH311x SuperI/O GPIO"
help
@@ -1111,7 +1137,7 @@ menu "I2C GPIO expanders"
config GPIO_ADNP
tristate "Avionic Design N-bit GPIO expander"
- depends on OF_GPIO
+ depends on OF
select GPIOLIB_IRQCHIP
help
This option enables support for N GPIOs found on Avionic Design
@@ -1144,7 +1170,7 @@ config GPIO_DS4520
config GPIO_GW_PLD
tristate "Gateworks PLD GPIO Expander"
- depends on OF_GPIO
+ depends on OF
help
Say yes here to provide access to the Gateworks I2C PLD GPIO
Expander. This is used at least on the Cambria GW2358-4.
@@ -1440,6 +1466,7 @@ config GPIO_JANZ_TTL
config GPIO_KEMPLD
tristate "Kontron ETX / COMexpress GPIO"
depends on MFD_KEMPLD
+ select GPIOLIB_IRQCHIP
help
This enables support for the PLD GPIO interface on some Kontron ETX
and COMexpress (ETXexpress) modules.
@@ -1567,7 +1594,6 @@ config GPIO_PALMAS
config GPIO_PMIC_EIC_SPRD
tristate "Spreadtrum PMIC EIC support"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
- depends on OF_GPIO
select GPIOLIB_IRQCHIP
help
Say yes here to support Spreadtrum PMIC EIC device.
@@ -1606,7 +1632,6 @@ config GPIO_SL28CPLD
config GPIO_STMPE
tristate "STMPE GPIOs"
depends on MFD_STMPE
- depends on OF_GPIO
select GPIOLIB_IRQCHIP
help
This enables support for the GPIOs found on the STMPE I/O
@@ -1615,7 +1640,6 @@ config GPIO_STMPE
config GPIO_TC3589X
bool "TC3589X GPIOs"
depends on MFD_TC3589X
- depends on OF_GPIO
select GPIOLIB_IRQCHIP
help
This enables support for the GPIOs found on the TC3589X
@@ -1986,7 +2010,6 @@ 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.
@@ -2005,7 +2028,7 @@ config GPIO_LATCH
config GPIO_LINE_MUX
tristate "GPIO line mux driver"
- depends on OF_GPIO
+ depends on OF
select MULTIPLEXER
help
Say Y here to support the GPIO line mux, which can provide virtual
@@ -2038,7 +2061,6 @@ config GPIO_SIM
tristate "GPIO Simulator Module"
select IRQ_SIM
select CONFIGFS_FS
- select DEV_SYNC_PROBE
help
This enables the GPIO simulator - a configfs-based GPIO testing
driver.
@@ -2076,7 +2098,6 @@ config GPIO_VIRTUSER
select DEBUG_FS
select CONFIGFS_FS
select IRQ_WORK
- select DEV_SYNC_PROBE
help
Say Y here to enable the configurable, configfs-based virtual GPIO
consumer testing driver.
@@ -2087,6 +2108,3 @@ config GPIO_VIRTUSER
endmenu
endif
-
-config DEV_SYNC_PROBE
- tristate
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index c05f7d795c43..b267598b517d 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -21,9 +21,6 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
# directly supported by gpio-generic
gpio-generic-$(CONFIG_GPIO_GENERIC) += gpio-mmio.o
-# Utilities for drivers that need synchronous fake device creation
-obj-$(CONFIG_DEV_SYNC_PROBE) += dev-sync-probe.o
-
obj-$(CONFIG_GPIO_104_DIO_48E) += gpio-104-dio-48e.o
obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o
obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o
@@ -51,6 +48,7 @@ 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_BY_PINCTRL) += gpio-by-pinctrl.o
obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
obj-$(CONFIG_GPIO_CGBC) += gpio-cgbc.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
@@ -134,6 +132,7 @@ obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_GPIO_NCT6694) += gpio-nct6694.o
obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o
+obj-$(CONFIG_GPIO_NOVALAKE) += gpio-novalake-events.o
obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o
obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO
index 5acaeab029ec..7ce80fde1f17 100644
--- a/drivers/gpio/TODO
+++ b/drivers/gpio/TODO
@@ -58,34 +58,6 @@ Work items:
-------------------------------------------------------------------------------
-Get rid of <linux/of_gpio.h>
-
-This header and helpers appeared at one point when there was no proper
-driver infrastructure for doing simpler MMIO GPIO devices and there was
-no core support for parsing device tree GPIOs from the core library with
-the [devm_]gpiod_get() calls we have today that will implicitly go into
-the device tree back-end. It is legacy and should not be used in new code.
-
-Work items:
-
-- Change all consumer drivers that #include <linux/of_gpio.h> to
- #include <linux/gpio/consumer.h> and stop doing custom parsing of the
- GPIO lines from the device tree. This can be tricky and often involves
- changing board files, etc.
-
-- Pull semantics for legacy device tree (OF) GPIO lookups into
- gpiolib-of.c: in some cases subsystems are doing custom flags and
- lookups for polarity inversion, open drain and what not. As we now
- handle this with generic OF bindings, pull all legacy handling into
- gpiolib so the library API becomes narrow and deep and handle all
- legacy bindings internally. (See e.g. commits 6953c57ab172,
- 6a537d48461d etc)
-
-- Delete <linux/of_gpio.h> when all the above is complete and everything
- uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead.
-
--------------------------------------------------------------------------------
-
Collect drivers
Collect GPIO drivers from arch/* and other places that should be placed
diff --git a/drivers/gpio/dev-sync-probe.c b/drivers/gpio/dev-sync-probe.c
deleted file mode 100644
index 9ea733b863b2..000000000000
--- a/drivers/gpio/dev-sync-probe.c
+++ /dev/null
@@ -1,97 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Common code for drivers creating fake platform devices.
- *
- * Provides synchronous device creation: waits for probe completion and
- * returns the probe success or error status to the device creator.
- *
- * Copyright (C) 2021 Bartosz Golaszewski <brgl@bgdev.pl>
- * Copyright (C) 2025 Koichiro Den <koichiro.den@canonical.com>
- */
-
-#include <linux/device.h>
-#include <linux/slab.h>
-
-#include "dev-sync-probe.h"
-
-static int dev_sync_probe_notifier_call(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct dev_sync_probe_data *pdata;
- struct device *dev = data;
-
- pdata = container_of(nb, struct dev_sync_probe_data, bus_notifier);
- if (!device_match_name(dev, pdata->name))
- return NOTIFY_DONE;
-
- switch (action) {
- case BUS_NOTIFY_BOUND_DRIVER:
- pdata->driver_bound = true;
- break;
- case BUS_NOTIFY_DRIVER_NOT_BOUND:
- pdata->driver_bound = false;
- break;
- default:
- return NOTIFY_DONE;
- }
-
- complete(&pdata->probe_completion);
- return NOTIFY_OK;
-}
-
-void dev_sync_probe_init(struct dev_sync_probe_data *data)
-{
- memset(data, 0, sizeof(*data));
- init_completion(&data->probe_completion);
- data->bus_notifier.notifier_call = dev_sync_probe_notifier_call;
-}
-EXPORT_SYMBOL_GPL(dev_sync_probe_init);
-
-int dev_sync_probe_register(struct dev_sync_probe_data *data,
- struct platform_device_info *pdevinfo)
-{
- struct platform_device *pdev;
- char *name;
-
- name = kasprintf(GFP_KERNEL, "%s.%d", pdevinfo->name, pdevinfo->id);
- if (!name)
- return -ENOMEM;
-
- data->driver_bound = false;
- data->name = name;
- reinit_completion(&data->probe_completion);
- bus_register_notifier(&platform_bus_type, &data->bus_notifier);
-
- pdev = platform_device_register_full(pdevinfo);
- if (IS_ERR(pdev)) {
- bus_unregister_notifier(&platform_bus_type, &data->bus_notifier);
- kfree(data->name);
- return PTR_ERR(pdev);
- }
-
- wait_for_completion(&data->probe_completion);
- bus_unregister_notifier(&platform_bus_type, &data->bus_notifier);
-
- if (!data->driver_bound) {
- platform_device_unregister(pdev);
- kfree(data->name);
- return -ENXIO;
- }
-
- data->pdev = pdev;
- return 0;
-}
-EXPORT_SYMBOL_GPL(dev_sync_probe_register);
-
-void dev_sync_probe_unregister(struct dev_sync_probe_data *data)
-{
- platform_device_unregister(data->pdev);
- kfree(data->name);
- data->pdev = NULL;
-}
-EXPORT_SYMBOL_GPL(dev_sync_probe_unregister);
-
-MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");
-MODULE_AUTHOR("Koichiro Den <koichiro.den@canonical.com>");
-MODULE_DESCRIPTION("Utilities for synchronous fake device creation");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/dev-sync-probe.h b/drivers/gpio/dev-sync-probe.h
deleted file mode 100644
index 4b3d52b70519..000000000000
--- a/drivers/gpio/dev-sync-probe.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-#ifndef DEV_SYNC_PROBE_H
-#define DEV_SYNC_PROBE_H
-
-#include <linux/completion.h>
-#include <linux/notifier.h>
-#include <linux/platform_device.h>
-
-struct dev_sync_probe_data {
- struct platform_device *pdev;
- const char *name;
-
- /* Synchronize with probe */
- struct notifier_block bus_notifier;
- struct completion probe_completion;
- bool driver_bound;
-};
-
-void dev_sync_probe_init(struct dev_sync_probe_data *data);
-int dev_sync_probe_register(struct dev_sync_probe_data *data,
- struct platform_device_info *pdevinfo);
-void dev_sync_probe_unregister(struct dev_sync_probe_data *data);
-
-#endif /* DEV_SYNC_PROBE_H */
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
index 9adf3228c12a..5915209e1e21 100644
--- a/drivers/gpio/gpio-aggregator.c
+++ b/drivers/gpio/gpio-aggregator.c
@@ -32,8 +32,6 @@
#include <linux/gpio/forwarder.h>
#include <linux/gpio/machine.h>
-#include "dev-sync-probe.h"
-
#define AGGREGATOR_MAX_GPIOS 512
#define AGGREGATOR_LEGACY_PREFIX "_sysfs"
@@ -42,7 +40,7 @@
*/
struct gpio_aggregator {
- struct dev_sync_probe_data probe_data;
+ struct platform_device *pdev;
struct config_group group;
struct gpiod_lookup_table *lookups;
struct mutex lock;
@@ -135,7 +133,7 @@ static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr)
{
lockdep_assert_held(&aggr->lock);
- return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev);
+ return aggr->pdev && platform_get_drvdata(aggr->pdev);
}
/* Only aggregators created via legacy sysfs can be "activating". */
@@ -143,7 +141,7 @@ static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr)
{
lockdep_assert_held(&aggr->lock);
- return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev);
+ return aggr->pdev && !platform_get_drvdata(aggr->pdev);
}
static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr)
@@ -909,6 +907,7 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr)
{
struct platform_device_info pdevinfo;
struct gpio_aggregator_line *line;
+ struct platform_device *pdev;
struct fwnode_handle *swnode;
unsigned int n = 0;
int ret = 0;
@@ -962,12 +961,23 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr)
gpiod_add_lookup_table(aggr->lookups);
- ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo);
- if (ret)
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
goto err_remove_lookup_table;
+ }
+ wait_for_device_probe();
+ if (!device_is_bound(&pdev->dev)) {
+ ret = -ENXIO;
+ goto err_unregister_pdev;
+ }
+
+ aggr->pdev = pdev;
return 0;
+err_unregister_pdev:
+ platform_device_unregister(pdev);
err_remove_lookup_table:
kfree(aggr->lookups->dev_id);
gpiod_remove_lookup_table(aggr->lookups);
@@ -981,7 +991,8 @@ err_remove_lookups:
static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr)
{
- dev_sync_probe_unregister(&aggr->probe_data);
+ platform_device_unregister(aggr->pdev);
+ aggr->pdev = NULL;
gpiod_remove_lookup_table(aggr->lookups);
kfree(aggr->lookups->dev_id);
kfree(aggr->lookups);
@@ -1145,7 +1156,7 @@ gpio_aggregator_device_dev_name_show(struct config_item *item, char *page)
guard(mutex)(&aggr->lock);
- pdev = aggr->probe_data.pdev;
+ pdev = aggr->pdev;
if (pdev)
return sysfs_emit(page, "%s\n", dev_name(&pdev->dev));
@@ -1322,7 +1333,6 @@ gpio_aggregator_make_group(struct config_group *group, const char *name)
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;
}
@@ -1471,12 +1481,6 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver,
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);
@@ -1495,7 +1499,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver,
goto remove_table;
}
- aggr->probe_data.pdev = pdev;
+ aggr->pdev = pdev;
module_put(THIS_MODULE);
return count;
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 9115e56a1626..e6af7f3fba5e 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -655,7 +655,7 @@ static void aspeed_init_irq_valid_mask(struct gpio_chip *gc,
while (!is_bank_props_sentinel(props)) {
unsigned int offset;
- const unsigned long int input = props->input;
+ const unsigned long input = props->input;
/* Pretty crummy approach, but similar to GPIO core */
for_each_clear_bit(offset, &input, 32) {
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index 208b71c59d58..b1d32d590cf8 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -58,15 +58,6 @@
#define LOCK_CODE 0xffffffff
#define UNLOCK_CODE 0x00000000
-struct bcm_kona_gpio {
- void __iomem *reg_base;
- int num_bank;
- raw_spinlock_t lock;
- struct gpio_chip gpio_chip;
- struct irq_domain *irq_domain;
- struct bcm_kona_gpio_bank *banks;
-};
-
struct bcm_kona_gpio_bank {
int id;
int irq;
@@ -90,6 +81,15 @@ struct bcm_kona_gpio_bank {
struct bcm_kona_gpio *kona_gpio;
};
+struct bcm_kona_gpio {
+ void __iomem *reg_base;
+ int num_bank;
+ raw_spinlock_t lock;
+ struct gpio_chip gpio_chip;
+ struct irq_domain *irq_domain;
+ struct bcm_kona_gpio_bank banks[] __counted_by(num_bank);
+};
+
static inline void bcm_kona_gpio_write_lock_regs(void __iomem *reg_base,
int bank_id, u32 lockcode)
{
@@ -584,12 +584,6 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
int ret;
int i;
- kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL);
- if (!kona_gpio)
- return -ENOMEM;
-
- kona_gpio->gpio_chip = template_chip;
- chip = &kona_gpio->gpio_chip;
ret = platform_irq_count(pdev);
if (!ret) {
dev_err(dev, "Couldn't determine # GPIO banks\n");
@@ -597,6 +591,11 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
} else if (ret < 0) {
return dev_err_probe(dev, ret, "Couldn't determine GPIO banks\n");
}
+
+ kona_gpio = devm_kzalloc(dev, struct_size(kona_gpio, banks, ret), GFP_KERNEL);
+ if (!kona_gpio)
+ return -ENOMEM;
+
kona_gpio->num_bank = ret;
if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) {
@@ -604,13 +603,9 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
GPIO_MAX_BANK_NUM);
return -ENXIO;
}
- kona_gpio->banks = devm_kcalloc(dev,
- kona_gpio->num_bank,
- sizeof(*kona_gpio->banks),
- GFP_KERNEL);
- if (!kona_gpio->banks)
- return -ENOMEM;
+ kona_gpio->gpio_chip = template_chip;
+ chip = &kona_gpio->gpio_chip;
chip->parent = dev;
chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK;
diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c
index 7c95bb36511e..cc5b1746f2fe 100644
--- a/drivers/gpio/gpio-bd9571mwv.c
+++ b/drivers/gpio/gpio-bd9571mwv.c
@@ -69,7 +69,7 @@ static int bd9571mwv_gpio_get(struct gpio_chip *chip, unsigned int offset)
if (ret < 0)
return ret;
- return val & BIT(offset);
+ return !!(val & BIT(offset));
}
static int bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset,
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index 2352d099709c..44ca798cf832 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-// Copyright (C) 2015-2017 Broadcom
+// Copyright (C) 2015-2017, 2026 Broadcom
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
@@ -54,6 +54,7 @@ struct brcmstb_gpio_priv {
int parent_irq;
int num_gpios;
int parent_wake_irq;
+ bool suspended;
};
#define MAX_GPIO_PER_BANK 32
@@ -95,15 +96,13 @@ static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq,
return hwirq - bank->chip.gc.offset;
}
-static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
- unsigned int hwirq, bool enable)
+static void __brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
+ irq_hw_number_t hwirq, bool enable)
{
struct brcmstb_gpio_priv *priv = bank->parent_priv;
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank));
u32 imask;
- guard(gpio_generic_lock_irqsave)(&bank->chip);
-
imask = gpio_generic_read_reg(&bank->chip,
priv->reg_base + GIO_MASK(bank->id));
if (enable)
@@ -114,6 +113,13 @@ static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
priv->reg_base + GIO_MASK(bank->id), imask);
}
+static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
+ irq_hw_number_t hwirq, bool enable)
+{
+ guard(gpio_generic_lock_irqsave)(&bank->chip);
+ __brcmstb_gpio_set_imask(bank, hwirq, enable);
+}
+
static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
@@ -132,7 +138,21 @@ static void brcmstb_gpio_irq_mask(struct irq_data *d)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
- brcmstb_gpio_set_imask(bank, d->hwirq, false);
+ brcmstb_gpio_set_imask(bank, irqd_to_hwirq(d), false);
+}
+
+static void brcmstb_gpio_irq_mask_ack(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
+ struct brcmstb_gpio_priv *priv = bank->parent_priv;
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank));
+
+ guard(gpio_generic_lock_irqsave)(&bank->chip);
+ __brcmstb_gpio_set_imask(bank, hwirq, false);
+ gpio_generic_write_reg(&bank->chip,
+ priv->reg_base + GIO_STAT(bank->id), mask);
}
static void brcmstb_gpio_irq_unmask(struct irq_data *d)
@@ -140,7 +160,7 @@ static void brcmstb_gpio_irq_unmask(struct irq_data *d)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
- brcmstb_gpio_set_imask(bank, d->hwirq, true);
+ brcmstb_gpio_set_imask(bank, irqd_to_hwirq(d), true);
}
static void brcmstb_gpio_irq_ack(struct irq_data *d)
@@ -148,7 +168,7 @@ static void brcmstb_gpio_irq_ack(struct irq_data *d)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
struct brcmstb_gpio_priv *priv = bank->parent_priv;
- u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank));
+ u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank));
gpio_generic_write_reg(&bank->chip,
priv->reg_base + GIO_STAT(bank->id), mask);
@@ -159,7 +179,7 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
struct brcmstb_gpio_priv *priv = bank->parent_priv;
- u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank));
+ u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank));
u32 edge_insensitive, iedge_insensitive;
u32 edge_config, iedge_config;
u32 level, ilevel;
@@ -221,6 +241,9 @@ static int brcmstb_gpio_priv_set_wake(struct brcmstb_gpio_priv *priv,
{
int ret = 0;
+ if (priv->parent_wake_irq == priv->parent_irq)
+ return ret;
+
if (enable)
ret = enable_irq_wake(priv->parent_wake_irq);
else
@@ -236,7 +259,7 @@ static int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
struct brcmstb_gpio_priv *priv = bank->parent_priv;
- u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank));
+ u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank));
/*
* Do not do anything specific for now, suspend/resume callbacks will
@@ -271,6 +294,11 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
while ((status = brcmstb_gpio_get_active_irqs(bank))) {
unsigned int offset;
+ if (priv->suspended && bank->wake_active & status) {
+ priv->suspended = false;
+ pm_wakeup_event(&priv->pdev->dev, 0);
+ }
+
for_each_set_bit(offset, &status, 32) {
if (offset >= bank->width)
dev_warn(&priv->pdev->dev,
@@ -444,18 +472,18 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev,
}
if (of_property_read_bool(np, "wakeup-source")) {
+ /*
+ * Set wakeup capability so we can process boot-time
+ * "wakeups" (e.g., from S5 cold boot).
+ */
+ device_set_wakeup_capable(dev, true);
+ device_wakeup_enable(dev);
priv->parent_wake_irq = platform_get_irq(pdev, 1);
if (priv->parent_wake_irq < 0) {
- priv->parent_wake_irq = 0;
+ priv->parent_wake_irq = priv->parent_irq;
dev_warn(dev,
"Couldn't get wake IRQ - GPIOs will not be able to wake from sleep");
} else {
- /*
- * Set wakeup capability so we can process boot-time
- * "wakeups" (e.g., from S5 cold boot)
- */
- device_set_wakeup_capable(dev, true);
- device_wakeup_enable(dev);
err = devm_request_irq(dev, priv->parent_wake_irq,
brcmstb_gpio_wake_irq_handler,
IRQF_SHARED,
@@ -466,18 +494,17 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev,
goto out_free_domain;
}
}
+ priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake;
}
priv->irq_chip.name = dev_name(dev);
priv->irq_chip.irq_disable = brcmstb_gpio_irq_mask;
priv->irq_chip.irq_mask = brcmstb_gpio_irq_mask;
+ priv->irq_chip.irq_mask_ack = brcmstb_gpio_irq_mask_ack;
priv->irq_chip.irq_unmask = brcmstb_gpio_irq_unmask;
priv->irq_chip.irq_ack = brcmstb_gpio_irq_ack;
priv->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type;
- if (priv->parent_wake_irq)
- priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake;
-
irq_set_chained_handler_and_data(priv->parent_irq,
brcmstb_gpio_irq_handler, priv);
irq_set_status_flags(priv->parent_irq, IRQ_DISABLE_UNLAZY);
@@ -500,16 +527,11 @@ static void brcmstb_gpio_bank_save(struct brcmstb_gpio_priv *priv,
priv->reg_base + GIO_BANK_OFF(bank->id, i));
}
-static void brcmstb_gpio_quiesce(struct device *dev, bool save)
+static void brcmstb_gpio_quiesce(struct brcmstb_gpio_priv *priv, bool save)
{
- struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
struct brcmstb_gpio_bank *bank;
u32 imask;
- /* disable non-wake interrupt */
- if (priv->parent_irq >= 0)
- disable_irq(priv->parent_irq);
-
list_for_each_entry(bank, &priv->bank_list, node) {
if (save)
brcmstb_gpio_bank_save(priv, bank);
@@ -527,8 +549,13 @@ static void brcmstb_gpio_quiesce(struct device *dev, bool save)
static void brcmstb_gpio_shutdown(struct platform_device *pdev)
{
+ struct brcmstb_gpio_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ if (priv->parent_irq > 0)
+ disable_irq(priv->parent_irq);
+
/* Enable GPIO for S5 cold boot */
- brcmstb_gpio_quiesce(&pdev->dev, false);
+ brcmstb_gpio_quiesce(priv, false);
}
static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
@@ -544,7 +571,30 @@ static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
static int brcmstb_gpio_suspend(struct device *dev)
{
- brcmstb_gpio_quiesce(dev, true);
+ struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->parent_irq > 0)
+ priv->suspended = true;
+
+ return 0;
+}
+
+static int brcmstb_gpio_suspend_noirq(struct device *dev)
+{
+ struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
+
+ /* Catch any wakeup sources occurring between suspend and noirq */
+ if (!priv->suspended)
+ return -EBUSY;
+
+ if (priv->parent_irq > 0)
+ disable_irq(priv->parent_irq);
+
+ brcmstb_gpio_quiesce(priv, true);
+
+ if (priv->parent_wake_irq)
+ enable_irq(priv->parent_irq);
+
return 0;
}
@@ -552,25 +602,24 @@ static int brcmstb_gpio_resume(struct device *dev)
{
struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
struct brcmstb_gpio_bank *bank;
- bool need_wakeup_event = false;
- list_for_each_entry(bank, &priv->bank_list, node) {
- need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
- brcmstb_gpio_bank_restore(priv, bank);
- }
+ if (priv->parent_wake_irq)
+ disable_irq(priv->parent_irq);
- if (priv->parent_wake_irq && need_wakeup_event)
- pm_wakeup_event(dev, 0);
+ priv->suspended = false;
- /* enable non-wake interrupt */
- if (priv->parent_irq >= 0)
+ list_for_each_entry(bank, &priv->bank_list, node)
+ brcmstb_gpio_bank_restore(priv, bank);
+
+ if (priv->parent_irq > 0)
enable_irq(priv->parent_irq);
return 0;
}
static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
- .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend),
+ .suspend = pm_sleep_ptr(brcmstb_gpio_suspend),
+ .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend_noirq),
.resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume),
};
diff --git a/drivers/gpio/gpio-by-pinctrl.c b/drivers/gpio/gpio-by-pinctrl.c
new file mode 100644
index 000000000000..ddfdc479d38a
--- /dev/null
+++ b/drivers/gpio/gpio-by-pinctrl.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2026 Linaro Inc.
+// Author: AKASHI takahiro <takahiro.akashi@linaro.org>
+
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include "gpiolib.h"
+
+static int pin_control_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ unsigned long config;
+ int ret;
+
+ config = PIN_CONFIG_OUTPUT_ENABLE;
+ ret = pinctrl_gpio_get_config(gc, offset, &config);
+ if (ret)
+ return ret;
+ if (config)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int pin_control_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int val)
+{
+ return pinctrl_gpio_direction_output(chip, offset);
+}
+
+static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ unsigned long config;
+ int ret;
+
+ config = PIN_CONFIG_LEVEL;
+ ret = pinctrl_gpio_get_config(chip, offset, &config);
+ if (ret)
+ return ret;
+
+ return !!config;
+}
+
+static int pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int val)
+{
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_LEVEL, val);
+ return pinctrl_gpio_set_config(chip, offset, config);
+}
+
+static int pin_control_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_chip *chip;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->label = dev_name(dev);
+ chip->parent = dev;
+ chip->base = -1;
+
+ chip->request = gpiochip_generic_request;
+ chip->free = gpiochip_generic_free;
+ chip->get_direction = pin_control_gpio_get_direction;
+ chip->direction_input = pinctrl_gpio_direction_input;
+ chip->direction_output = pin_control_gpio_direction_output;
+ chip->get = pin_control_gpio_get;
+ chip->set = pin_control_gpio_set;
+ chip->set_config = gpiochip_generic_config;
+
+ return devm_gpiochip_add_data(dev, chip, NULL);
+}
+
+static const struct of_device_id pin_control_gpio_match[] = {
+ { .compatible = "scmi-pinctrl-gpio" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pin_control_gpio_match);
+
+static struct platform_driver pin_control_gpio_driver = {
+ .probe = pin_control_gpio_probe,
+ .driver = {
+ .name = "pin-control-gpio",
+ .of_match_table = pin_control_gpio_match,
+ },
+};
+module_platform_driver(pin_control_gpio_driver);
+
+MODULE_AUTHOR("AKASHI Takahiro <takahiro.akashi@linaro.org>");
+MODULE_DESCRIPTION("Pinctrl based GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c
index 0efa1b61001a..84b5ed3c6e22 100644
--- a/drivers/gpio/gpio-cgbc.c
+++ b/drivers/gpio/gpio-cgbc.c
@@ -47,8 +47,8 @@ static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset)
if (ret)
return ret;
- else
- return (int)(val & (u8)BIT(offset));
+
+ return !!(val & BIT(offset));
}
static int __cgbc_gpio_set(struct gpio_chip *chip, unsigned int offset,
diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c
index 8affe4e9f90e..5f5373d86397 100644
--- a/drivers/gpio/gpio-cs5535.c
+++ b/drivers/gpio/gpio-cs5535.c
@@ -39,10 +39,6 @@ static ulong mask = GPIO_DEFAULT_MASK;
module_param_named(mask, mask, ulong, 0444);
MODULE_PARM_DESC(mask, "GPIO channel mask.");
-/*
- * FIXME: convert this singleton driver to use the state container
- * design pattern, see Documentation/driver-api/driver-model/design-patterns.rst
- */
static struct cs5535_gpio_chip {
struct gpio_chip chip;
resource_size_t base;
@@ -285,30 +281,29 @@ static const char * const cs5535_gpio_names[] = {
"GPIO28", NULL, NULL, NULL,
};
-static struct cs5535_gpio_chip cs5535_gpio_chip = {
- .chip = {
- .owner = THIS_MODULE,
- .label = DRV_NAME,
-
- .base = 0,
- .ngpio = 32,
- .names = cs5535_gpio_names,
- .request = chip_gpio_request,
-
- .get = chip_gpio_get,
- .set = chip_gpio_set,
-
- .direction_input = chip_direction_input,
- .direction_output = chip_direction_output,
- },
-};
-
static int cs5535_gpio_probe(struct platform_device *pdev)
{
+ struct cs5535_gpio_chip *priv;
+ struct gpio_chip *gc;
struct resource *res;
int err = -EIO;
ulong mask_orig = mask;
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ gc = &priv->chip;
+ gc->owner = THIS_MODULE;
+ gc->label = DRV_NAME;
+ gc->ngpio = 32;
+ gc->names = cs5535_gpio_names;
+ gc->request = chip_gpio_request;
+ gc->get = chip_gpio_get;
+ gc->set = chip_gpio_set;
+ gc->direction_input = chip_direction_input;
+ gc->direction_output = chip_direction_output;
+
/* There are two ways to get the GPIO base address; one is by
* fetching it from MSR_LBAR_GPIO, the other is by reading the
* PCI BAR info. The latter method is easier (especially across
@@ -329,9 +324,9 @@ static int cs5535_gpio_probe(struct platform_device *pdev)
}
/* set up the driver-specific struct */
- cs5535_gpio_chip.base = res->start;
- cs5535_gpio_chip.pdev = pdev;
- spin_lock_init(&cs5535_gpio_chip.lock);
+ priv->base = res->start;
+ priv->pdev = pdev;
+ spin_lock_init(&priv->lock);
dev_info(&pdev->dev, "reserved resource region %pR\n", res);
@@ -347,8 +342,7 @@ static int cs5535_gpio_probe(struct platform_device *pdev)
mask_orig, mask);
/* finally, register with the generic GPIO API */
- return devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
- &cs5535_gpio_chip);
+ return devm_gpiochip_add_data(&pdev->dev, gc, priv);
}
static struct platform_driver cs5535_gpio_driver = {
diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c
index a09bd6eb93cf..1949a6ea8ec6 100644
--- a/drivers/gpio/gpio-da9055.c
+++ b/drivers/gpio/gpio-da9055.c
@@ -55,7 +55,7 @@ static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset)
return ret;
}
- return ret & (1 << offset);
+ return !!(ret & (1 << offset));
}
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index 4986c465c9a8..15cebc8b5d66 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -75,8 +75,8 @@ struct dwapb_port_property {
};
struct dwapb_platform_data {
- struct dwapb_port_property *properties;
unsigned int nports;
+ struct dwapb_port_property properties[] __counted_by(nports);
};
/* Store GPIO context across system-wide suspend/resume transitions */
@@ -114,11 +114,11 @@ static inline struct dwapb_gpio *to_dwapb_gpio(struct gpio_chip *gc)
struct dwapb_gpio {
struct device *dev;
void __iomem *regs;
- struct dwapb_gpio_port *ports;
unsigned int nr_ports;
unsigned int flags;
struct reset_control *rst;
struct clk_bulk_data clks[DWAPB_NR_CLOCKS];
+ struct dwapb_gpio_port ports[] __counted_by(nr_ports);
};
static inline u32 gpio_reg_v2_convert(unsigned int offset)
@@ -585,14 +585,10 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev)
if (nports == 0)
return ERR_PTR(-ENODEV);
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, struct_size(pdata, properties, nports), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
- pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL);
- if (!pdata->properties)
- return ERR_PTR(-ENOMEM);
-
pdata->nports = nports;
i = 0;
@@ -714,22 +710,17 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
if (IS_ERR(pdata))
return PTR_ERR(pdata);
- gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, ports, pdata->nports), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
- gpio->dev = &pdev->dev;
gpio->nr_ports = pdata->nports;
+ gpio->dev = &pdev->dev;
err = dwapb_get_reset(gpio);
if (err)
return err;
- gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
- sizeof(*gpio->ports), GFP_KERNEL);
- if (!gpio->ports)
- return -ENOMEM;
-
gpio->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(gpio->regs))
return PTR_ERR(gpio->regs);
diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c
index 72935d6dbebf..d15423c718d0 100644
--- a/drivers/gpio/gpio-htc-egpio.c
+++ b/drivers/gpio/gpio-htc-egpio.c
@@ -46,8 +46,8 @@ struct egpio_info {
uint chained_irq;
/* egpio info */
- struct egpio_chip *chip;
int nchips;
+ struct egpio_chip chip[] __counted_by(nchips);
};
static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg)
@@ -270,10 +270,12 @@ static int __init egpio_probe(struct platform_device *pdev)
int i;
/* Initialize ei data structure. */
- ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL);
+ ei = devm_kzalloc(&pdev->dev, struct_size(ei, chip, pdata->num_chips), GFP_KERNEL);
if (!ei)
return -ENOMEM;
+ ei->nchips = pdata->num_chips;
+
spin_lock_init(&ei->lock);
/* Find chained irq */
@@ -302,13 +304,6 @@ static int __init egpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ei);
- ei->nchips = pdata->num_chips;
- ei->chip = devm_kcalloc(&pdev->dev,
- ei->nchips, sizeof(struct egpio_chip),
- GFP_KERNEL);
- if (!ei->chip)
- return -ENOMEM;
-
for (i = 0; i < ei->nchips; i++) {
ei->chip[i].reg_start = pdata->chip[i].reg_start;
ei->chip[i].cached_values = pdata->chip[i].initial_values;
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
index 923aad3ab4d4..5a63df3ea5fa 100644
--- a/drivers/gpio/gpio-kempld.c
+++ b/drivers/gpio/gpio-kempld.c
@@ -11,20 +11,35 @@
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/errno.h>
+#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/gpio/driver.h>
#include <linux/mfd/kempld.h>
#define KEMPLD_GPIO_MAX_NUM 16
#define KEMPLD_GPIO_MASK(x) (BIT((x) % 8))
-#define KEMPLD_GPIO_DIR_NUM(x) (0x40 + (x) / 8)
-#define KEMPLD_GPIO_LVL_NUM(x) (0x42 + (x) / 8)
+#define KEMPLD_GPIO_DIR 0x40
+#define KEMPLD_GPIO_LVL 0x42
+#define KEMPLD_GPIO_STS 0x44
#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46
+#define KEMPLD_GPIO_EVT_LOW_HIGH 0x48
#define KEMPLD_GPIO_IEN 0x4A
+#define KEMPLD_GPIO_OUT_LVL 0x4E
+
+/* The IRQ to use if none was configured in the BIOS */
+static unsigned int gpio_irq;
+module_param_hw(gpio_irq, uint, irq, 0444);
+MODULE_PARM_DESC(gpio_irq, "Set legacy GPIO IRQ (1-15)");
struct kempld_gpio_data {
struct gpio_chip chip;
struct kempld_device_data *pld;
+ u8 out_lvl_reg;
+
+ struct mutex irq_lock;
+ u16 ien;
+ u16 evt_low_high;
+ u16 evt_lvl_edge;
};
/*
@@ -32,24 +47,25 @@ struct kempld_gpio_data {
* kempld_get_mutex must be called prior to calling this function.
*/
static void kempld_gpio_bitop(struct kempld_device_data *pld,
- u8 reg, u8 bit, u8 val)
+ u8 reg, unsigned int bit, bool val)
{
u8 status;
- status = kempld_read8(pld, reg);
+ status = kempld_read8(pld, reg + (bit / 8));
if (val)
status |= KEMPLD_GPIO_MASK(bit);
else
status &= ~KEMPLD_GPIO_MASK(bit);
- kempld_write8(pld, reg, status);
+ kempld_write8(pld, reg + (bit / 8), status);
}
-static int kempld_gpio_get_bit(struct kempld_device_data *pld, u8 reg, u8 bit)
+static int kempld_gpio_get_bit(struct kempld_device_data *pld,
+ u8 reg, unsigned int bit)
{
u8 status;
kempld_get_mutex(pld);
- status = kempld_read8(pld, reg);
+ status = kempld_read8(pld, reg + (bit / 8));
kempld_release_mutex(pld);
return !!(status & KEMPLD_GPIO_MASK(bit));
@@ -60,7 +76,34 @@ static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset)
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
struct kempld_device_data *pld = gpio->pld;
- return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset), offset);
+ return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL, offset);
+}
+
+static int kempld_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
+ struct kempld_device_data *pld = gpio->pld;
+ u8 reg = KEMPLD_GPIO_LVL;
+ unsigned int shift;
+
+ bits[0] &= ~mask[0];
+
+ kempld_get_mutex(pld);
+
+ /* Try to reduce to a single 8 bits access if possible */
+ for (shift = 0; shift < gpio->chip.ngpio; shift += 8, reg++) {
+ unsigned long msk = (mask[0] >> shift) & 0xff;
+
+ if (!msk)
+ continue;
+
+ bits[0] |= (kempld_read8(pld, reg) & msk) << shift;
+ }
+
+ kempld_release_mutex(pld);
+
+ return 0;
}
static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset,
@@ -70,7 +113,38 @@ static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset,
struct kempld_device_data *pld = gpio->pld;
kempld_get_mutex(pld);
- kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
+ kempld_gpio_bitop(pld, gpio->out_lvl_reg, offset, value);
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+static int kempld_gpio_set_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
+ struct kempld_device_data *pld = gpio->pld;
+ u8 reg = gpio->out_lvl_reg;
+ unsigned int shift;
+
+ kempld_get_mutex(pld);
+
+ /* Try to reduce to a single 8 bits access if possible */
+ for (shift = 0; shift < gpio->chip.ngpio; shift += 8, reg++) {
+ u8 val, msk = mask[0] >> shift;
+
+ if (!msk)
+ continue;
+
+ if (msk != 0xFF)
+ val = kempld_read8(pld, reg) & ~msk;
+ else
+ val = 0;
+
+ val |= (bits[0] >> shift) & msk;
+ kempld_write8(pld, reg, val);
+ }
+
kempld_release_mutex(pld);
return 0;
@@ -82,7 +156,7 @@ static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
struct kempld_device_data *pld = gpio->pld;
kempld_get_mutex(pld);
- kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 0);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR, offset, 0);
kempld_release_mutex(pld);
return 0;
@@ -95,8 +169,8 @@ static int kempld_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
struct kempld_device_data *pld = gpio->pld;
kempld_get_mutex(pld);
- kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
- kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 1);
+ kempld_gpio_bitop(pld, gpio->out_lvl_reg, offset, value);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR, offset, 1);
kempld_release_mutex(pld);
return 0;
@@ -107,7 +181,7 @@ static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
struct kempld_device_data *pld = gpio->pld;
- if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset))
+ if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR, offset))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
@@ -133,6 +207,180 @@ static int kempld_gpio_pincount(struct kempld_device_data *pld)
return evt ? __ffs(evt) : 16;
}
+static void kempld_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
+
+ gpio->ien &= ~BIT(irqd_to_hwirq(data));
+ gpiochip_disable_irq(chip, irqd_to_hwirq(data));
+}
+
+static void kempld_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
+
+ gpiochip_enable_irq(chip, irqd_to_hwirq(data));
+ gpio->ien |= BIT(irqd_to_hwirq(data));
+}
+
+static int kempld_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ gpio->evt_low_high |= BIT(data->hwirq);
+ gpio->evt_lvl_edge |= BIT(data->hwirq);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ gpio->evt_low_high &= ~BIT(data->hwirq);
+ gpio->evt_lvl_edge |= BIT(data->hwirq);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ gpio->evt_low_high |= BIT(data->hwirq);
+ gpio->evt_lvl_edge &= ~BIT(data->hwirq);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ gpio->evt_low_high &= ~BIT(data->hwirq);
+ gpio->evt_lvl_edge &= ~BIT(data->hwirq);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void kempld_irq_bus_lock(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
+
+ mutex_lock(&gpio->irq_lock);
+}
+
+static void kempld_irq_bus_sync_unlock(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ kempld_get_mutex(pld);
+ kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, gpio->evt_lvl_edge);
+ kempld_write16(pld, KEMPLD_GPIO_EVT_LOW_HIGH, gpio->evt_low_high);
+ kempld_write16(pld, KEMPLD_GPIO_IEN, gpio->ien);
+ kempld_release_mutex(pld);
+
+ mutex_unlock(&gpio->irq_lock);
+}
+
+static const struct irq_chip kempld_irqchip = {
+ .name = "kempld-gpio",
+ .irq_mask = kempld_irq_mask,
+ .irq_unmask = kempld_irq_unmask,
+ .irq_set_type = kempld_irq_set_type,
+ .irq_bus_lock = kempld_irq_bus_lock,
+ .irq_bus_sync_unlock = kempld_irq_bus_sync_unlock,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static irqreturn_t kempld_gpio_irq_handler(int irq, void *data)
+{
+ struct kempld_gpio_data *gpio = data;
+ struct gpio_chip *chip = &gpio->chip;
+ unsigned int pin, child_irq;
+ unsigned long status;
+
+ kempld_get_mutex(gpio->pld);
+
+ status = kempld_read16(gpio->pld, KEMPLD_GPIO_STS);
+ if (status)
+ kempld_write16(gpio->pld, KEMPLD_GPIO_STS, status);
+
+ kempld_release_mutex(gpio->pld);
+
+ status &= gpio->ien;
+ if (!status)
+ return IRQ_NONE;
+
+ for_each_set_bit(pin, &status, chip->ngpio) {
+ child_irq = irq_find_mapping(chip->irq.domain, pin);
+ handle_nested_irq(child_irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int kempld_gpio_irq_init(struct device *dev,
+ struct kempld_gpio_data *gpio)
+{
+ struct kempld_device_data *pld = gpio->pld;
+ struct gpio_chip *chip = &gpio->chip;
+ struct gpio_irq_chip *girq;
+ unsigned int irq;
+ int ret;
+
+ /* Get the IRQ configured by the BIOS in the PLD */
+ kempld_get_mutex(pld);
+ irq = kempld_read8(pld, KEMPLD_IRQ_GPIO);
+ kempld_release_mutex(pld);
+
+ if (irq == 0xff) {
+ dev_info(dev, "GPIO controller has no IRQ support\n");
+ return 0;
+ }
+
+ /* Allow overriding the IRQ with the module parameter */
+ if (gpio_irq > 0) {
+ dev_warn(dev, "Forcing IRQ to %d\n", gpio_irq);
+ irq &= ~KEMPLD_IRQ_GPIO_MASK;
+ irq |= gpio_irq & KEMPLD_IRQ_GPIO_MASK;
+ }
+
+ if (!(irq & KEMPLD_IRQ_GPIO_MASK)) {
+ dev_warn(dev, "No IRQ configured\n");
+ return 0;
+ }
+
+ /* Get the current config, disable all child interrupts, clear them
+ * and set the parent IRQ
+ */
+ kempld_get_mutex(pld);
+ gpio->evt_low_high = kempld_read16(pld, KEMPLD_GPIO_EVT_LOW_HIGH);
+ gpio->evt_lvl_edge = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
+ kempld_write16(pld, KEMPLD_GPIO_IEN, 0);
+ kempld_write16(pld, KEMPLD_GPIO_STS, 0xFFFF);
+ kempld_write16(pld, KEMPLD_IRQ_GPIO, irq);
+ kempld_release_mutex(pld);
+
+ girq = &chip->irq;
+ gpio_irq_chip_set_chip(girq, &kempld_irqchip);
+
+ 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;
+
+ mutex_init(&gpio->irq_lock);
+
+ ret = devm_request_threaded_irq(dev, irq & KEMPLD_IRQ_GPIO_MASK,
+ NULL, kempld_gpio_irq_handler,
+ IRQF_ONESHOT, chip->label,
+ gpio);
+ if (ret) {
+ dev_err(dev, "failed to request irq %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
static int kempld_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -152,6 +400,15 @@ static int kempld_gpio_probe(struct platform_device *pdev)
if (!gpio)
return -ENOMEM;
+ /* Starting with version 2.8 there is a dedicated register for the
+ * output state, earlier versions share the register used to read
+ * the line level.
+ */
+ if (pld->info.spec_major > 2 || pld->info.spec_minor >= 8)
+ gpio->out_lvl_reg = KEMPLD_GPIO_OUT_LVL;
+ else
+ gpio->out_lvl_reg = KEMPLD_GPIO_LVL;
+
gpio->pld = pld;
platform_set_drvdata(pdev, gpio);
@@ -169,13 +426,19 @@ static int kempld_gpio_probe(struct platform_device *pdev)
chip->direction_output = kempld_gpio_direction_output;
chip->get_direction = kempld_gpio_get_direction;
chip->get = kempld_gpio_get;
+ chip->get_multiple = kempld_gpio_get_multiple;
chip->set = kempld_gpio_set;
+ chip->set_multiple = kempld_gpio_set_multiple;
chip->ngpio = kempld_gpio_pincount(pld);
if (chip->ngpio == 0) {
dev_err(dev, "No GPIO pins detected\n");
return -ENODEV;
}
+ ret = kempld_gpio_irq_init(dev, gpio);
+ if (ret)
+ return ret;
+
ret = devm_gpiochip_add_data(dev, chip, gpio);
if (ret) {
dev_err(dev, "Could not register GPIO chip\n");
diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c
index 5376708a81bf..f4413fa5a811 100644
--- a/drivers/gpio/gpio-lp873x.c
+++ b/drivers/gpio/gpio-lp873x.c
@@ -55,7 +55,7 @@ static int lp873x_gpio_get(struct gpio_chip *chip, unsigned int offset)
if (ret < 0)
return ret;
- return val & BIT(offset * BITS_PER_GPO);
+ return !!(val & BIT(offset * BITS_PER_GPO));
}
static int lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset,
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index a61d670ceeda..281ba1740a6a 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -10,14 +10,18 @@
* Derived from drivers/gpio/pca953x.c
*/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/string.h>
+#include <linux/cleanup.h>
+#include <linux/err.h>
+#include <linux/device.h>
#include <linux/gpio/driver.h>
-#include <linux/interrupt.h>
#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/platform_data/max732x.h>
+#include <linux/slab.h>
+#include <linux/string.h>
/*
* Each port of MAX732x (including MAX7319) falls into one of the
@@ -207,22 +211,20 @@ static void max732x_gpio_set_mask(struct gpio_chip *gc, unsigned off, int mask,
uint8_t reg_out;
int ret;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0];
reg_out = (reg_out & ~mask) | (val & mask);
ret = max732x_writeb(chip, is_group_a(chip, off), reg_out);
if (ret < 0)
- goto out;
+ return;
/* update the shadow register then */
if (off > 7)
chip->reg_out[1] = reg_out;
else
chip->reg_out[0] = reg_out;
-out:
- mutex_unlock(&chip->lock);
}
static int max732x_gpio_set_value(struct gpio_chip *gc, unsigned int off,
@@ -329,7 +331,7 @@ static void max732x_irq_update_mask(struct max732x_chip *chip)
if (chip->irq_features == INT_NO_MASK)
return;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
switch (chip->irq_features) {
case INT_INDEP_MASK:
@@ -342,8 +344,6 @@ static void max732x_irq_update_mask(struct max732x_chip *chip)
max732x_writeb(chip, 1, (uint8_t)msg);
break;
}
-
- mutex_unlock(&chip->lock);
}
static void max732x_irq_mask(struct irq_data *d)
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index edbcaad57d00..0941d034a49c 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -42,18 +42,16 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/bitops.h>
#include <linux/cleanup.h>
-#include <linux/compiler.h>
#include <linux/err.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
+#include <linux/limits.h>
#include <linux/log2.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/property.h>
-#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c
index 12191aeb6566..a859deab2bca 100644
--- a/drivers/gpio/gpio-mpsse.c
+++ b/drivers/gpio/gpio-mpsse.c
@@ -548,13 +548,6 @@ static void gpio_mpsse_ida_remove(void *data)
ida_free(&gpio_mpsse_ida, priv->id);
}
-static void gpio_mpsse_usb_put_dev(void *data)
-{
- struct mpsse_priv *priv = data;
-
- usb_put_dev(priv->udev);
-}
-
static int mpsse_init_valid_mask(struct gpio_chip *chip,
unsigned long *valid_mask,
unsigned int ngpios)
@@ -598,11 +591,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
INIT_LIST_HEAD(&priv->workers);
- priv->udev = usb_get_dev(interface_to_usbdev(interface));
- err = devm_add_action_or_reset(dev, gpio_mpsse_usb_put_dev, priv);
- if (err)
- return err;
-
+ priv->udev = interface_to_usbdev(interface);
priv->intf = interface;
priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber;
diff --git a/drivers/gpio/gpio-novalake-events.c b/drivers/gpio/gpio-novalake-events.c
new file mode 100644
index 000000000000..b3bf0038f84a
--- /dev/null
+++ b/drivers/gpio/gpio-novalake-events.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel Nova Lake GPIO-signaled ACPI events driver
+ *
+ * Copyright (c) 2026, Intel Corporation.
+ *
+ * Author: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>
+ *
+ * Intel client platforms released in 2026 and later (starting with Intel Nova
+ * Lake) support two modes of handling ACPI General Purpose Events (GPE):
+ * exposed GPIO interrupt mode and legacy mode.
+ *
+ * By default, the platform uses legacy mode, handling GPEs as usual. If this
+ * driver is installed, it signals to the platform (on every boot) that exposed
+ * GPIO interrupt mode is supported. The platform then switches to exposed
+ * mode, which takes effect on next boot. From the user perspective, this
+ * change is transparent.
+ *
+ * However, if driver is uninstalled while in exposed interrupt mode, GPEs will
+ * _not_ be handled until platform falls back to legacy mode. This means that
+ * USB keyboard, mouse might not function properly for the fallback duration.
+ * Fallback requires two reboots to take effect: on first reboot, platform no
+ * longer receives signal from this driver and switches to legacy mode, which
+ * takes effect on second boot.
+ *
+ * Example ACPI event: Power Management Event coming from motherboard PCH,
+ * waking system from sleep following USB mouse hotplug.
+ *
+ * This driver supports up to 128 GPIO pins in each GPE block, per ACPI
+ * specification v6.6 section 5.6.4.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gfp_types.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+#include <linux/gpio/driver.h>
+
+/*
+ * GPE block has two registers, each register takes half the block size.
+ * Convert size to bits to get total GPIO pin count.
+ */
+#define GPE_BLK_REG_SIZE(block_size) ((block_size) / 2)
+#define GPE_REG_PIN_COUNT(block_size) BYTES_TO_BITS(GPE_BLK_REG_SIZE(block_size))
+#define GPE_STS_REG_OFFSET 0
+#define GPE_EN_REG_OFFSET(block_size) GPE_BLK_REG_SIZE(block_size)
+
+/**
+ * struct nvl_gpio - Intel Nova Lake GPIO driver state
+ * @gc: GPIO controller interface
+ * @reg_base: Base address of the GPE registers
+ * @lock: Guard register access
+ * @blk_size: GPE block length
+ */
+struct nvl_gpio {
+ struct gpio_chip gc;
+ void __iomem *reg_base;
+ raw_spinlock_t lock;
+ size_t blk_size;
+};
+
+static void __iomem *nvl_gpio_get_byte_addr(struct nvl_gpio *priv,
+ unsigned int reg_offset,
+ unsigned long gpio)
+{
+ return priv->reg_base + reg_offset + gpio;
+}
+
+static int nvl_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct nvl_gpio *priv = gpiochip_get_data(gc);
+ unsigned int byte_idx = gpio / BITS_PER_BYTE;
+ unsigned int bit_idx = gpio % BITS_PER_BYTE;
+ void __iomem *addr;
+ u8 reg;
+
+ addr = nvl_gpio_get_byte_addr(priv, GPE_STS_REG_OFFSET, byte_idx);
+
+ guard(raw_spinlock_irqsave)(&priv->lock);
+
+ reg = ioread8(addr);
+
+ return !!(reg & BIT(bit_idx));
+}
+
+static const struct gpio_chip nvl_gpio_chip = {
+ .owner = THIS_MODULE,
+ .get = nvl_gpio_get,
+};
+
+static int nvl_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ irq_set_handler_locked(d, handle_edge_irq);
+ else if (type & IRQ_TYPE_LEVEL_MASK)
+ irq_set_handler_locked(d, handle_level_irq);
+
+ return 0;
+}
+
+static void nvl_gpio_irq_mask_unmask(struct gpio_chip *gc, unsigned long hwirq,
+ bool mask)
+{
+ struct nvl_gpio *priv = gpiochip_get_data(gc);
+ unsigned int byte_idx = hwirq / BITS_PER_BYTE;
+ unsigned int bit_idx = hwirq % BITS_PER_BYTE;
+ void __iomem *addr;
+ u8 reg;
+
+ addr = nvl_gpio_get_byte_addr(priv, GPE_EN_REG_OFFSET(priv->blk_size), byte_idx);
+
+ guard(raw_spinlock_irqsave)(&priv->lock);
+
+ reg = ioread8(addr);
+ if (mask)
+ reg &= ~BIT(bit_idx);
+ else
+ reg |= BIT(bit_idx);
+ iowrite8(reg, addr);
+}
+
+static void nvl_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ gpiochip_enable_irq(gc, hwirq);
+ nvl_gpio_irq_mask_unmask(gc, hwirq, false);
+}
+
+static void nvl_gpio_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ nvl_gpio_irq_mask_unmask(gc, hwirq, true);
+ gpiochip_disable_irq(gc, hwirq);
+}
+
+static void nvl_gpio_irq_ack(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct nvl_gpio *priv = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ unsigned int byte_idx = hwirq / BITS_PER_BYTE;
+ unsigned int bit_idx = hwirq % BITS_PER_BYTE;
+ void __iomem *addr;
+ u8 reg;
+
+ addr = nvl_gpio_get_byte_addr(priv, GPE_STS_REG_OFFSET, byte_idx);
+
+ guard(raw_spinlock_irqsave)(&priv->lock);
+
+ reg = ioread8(addr);
+ reg |= BIT(bit_idx);
+ iowrite8(reg, addr);
+}
+
+static const struct irq_chip nvl_gpio_irq_chip = {
+ .name = "gpio-novalake",
+ .irq_ack = nvl_gpio_irq_ack,
+ .irq_mask = nvl_gpio_irq_mask,
+ .irq_unmask = nvl_gpio_irq_unmask,
+ .irq_set_type = nvl_gpio_irq_set_type,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static irqreturn_t nvl_gpio_irq(int irq, void *data)
+{
+ struct nvl_gpio *priv = data;
+ const size_t block_size = priv->blk_size;
+ unsigned int handled = 0;
+
+ for (unsigned int i = 0; i < block_size; i++) {
+ const void __iomem *reg = priv->reg_base + i;
+ unsigned long pending;
+ unsigned long enabled;
+ unsigned int bit_idx;
+
+ scoped_guard(raw_spinlock, &priv->lock) {
+ pending = ioread8(reg + GPE_STS_REG_OFFSET);
+ enabled = ioread8(reg + GPE_EN_REG_OFFSET(block_size));
+ }
+ pending &= enabled;
+
+ for_each_set_bit(bit_idx, &pending, BITS_PER_BYTE) {
+ unsigned int hwirq = i * BITS_PER_BYTE + bit_idx;
+
+ generic_handle_domain_irq(priv->gc.irq.domain, hwirq);
+ }
+
+ handled += pending ? 1 : 0;
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+/* UUID for GPE device _DSM: 079406e6-bdea-49cf-8563-03e2811901cb */
+static const guid_t nvl_gpe_dsm_guid =
+ GUID_INIT(0x079406e6, 0xbdea, 0x49cf,
+ 0x85, 0x63, 0x03, 0xe2, 0x81, 0x19, 0x01, 0xcb);
+
+#define DSM_GPE_MODE_REV 1
+#define DSM_GPE_MODE_FN_INDEX 1
+#define DSM_ENABLE_GPE_MODE 1
+
+static int nvl_acpi_enable_gpe_mode(struct device *dev)
+{
+ union acpi_object argv4[2];
+ union acpi_object *obj;
+
+ argv4[0].type = ACPI_TYPE_PACKAGE;
+ argv4[0].package.count = 1;
+ argv4[0].package.elements = &argv4[1];
+ argv4[1].integer.type = ACPI_TYPE_INTEGER;
+ argv4[1].integer.value = DSM_ENABLE_GPE_MODE;
+
+ obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(dev), &nvl_gpe_dsm_guid,
+ DSM_GPE_MODE_REV, DSM_GPE_MODE_FN_INDEX,
+ argv4, ACPI_TYPE_BUFFER);
+ if (!obj)
+ return -EIO;
+ ACPI_FREE(obj);
+
+ return 0;
+}
+
+static int nvl_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ resource_size_t ioresource_size;
+ struct gpio_irq_chip *girq;
+ struct nvl_gpio *priv;
+ struct resource *res;
+ void __iomem *regs;
+ int ret, irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -ENXIO;
+
+ /*
+ * GPE block length should be non-negative multiple of two and allow up
+ * to 128 pins. ACPI v6.6 section 5.2.9 and 5.6.4.
+ */
+ ioresource_size = resource_size(res);
+ if (!ioresource_size || ioresource_size % 2 || ioresource_size > 0x20)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid GPE block length, resource: %pR\n",
+ res);
+
+ regs = devm_ioport_map(dev, res->start, ioresource_size);
+ if (!regs)
+ return -ENOMEM;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ raw_spin_lock_init(&priv->lock);
+
+ priv->reg_base = regs;
+ priv->blk_size = ioresource_size;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, nvl_gpio_irq, IRQF_SHARED, dev_name(dev), priv);
+ if (ret)
+ return ret;
+
+ priv->gc = nvl_gpio_chip;
+ priv->gc.label = dev_name(dev);
+ priv->gc.parent = dev;
+ priv->gc.ngpio = GPE_REG_PIN_COUNT(priv->blk_size);
+ priv->gc.base = -1;
+
+ girq = &priv->gc.irq;
+ gpio_irq_chip_set_chip(girq, &nvl_gpio_irq_chip);
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+
+ ret = devm_gpiochip_add_data(dev, &priv->gc, priv);
+ if (ret)
+ return ret;
+
+ return nvl_acpi_enable_gpe_mode(dev);
+}
+
+static const struct acpi_device_id nvl_gpio_acpi_match[] = {
+ { "INTC1114" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, nvl_gpio_acpi_match);
+
+static struct platform_driver nvl_gpio_driver = {
+ .driver = {
+ .name = "gpio-novalake-events",
+ .acpi_match_table = nvl_gpio_acpi_match,
+ },
+ .probe = nvl_gpio_probe,
+};
+module_platform_driver(nvl_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Nova Lake ACPI GPIO events driver");
diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c
index 4cf91528f547..5e3152c2e51a 100644
--- a/drivers/gpio/gpio-realtek-otto.c
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -351,6 +351,10 @@ static const struct of_device_id realtek_gpio_of_match[] = {
{
.compatible = "realtek,rtl9310-gpio",
},
+ {
+ .compatible = "realtek,rtl9607-gpio",
+ .data = (void *)GPIO_PORTS_REVERSED,
+ },
{}
};
MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 0fff4a699f12..50f3733a455d 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -582,7 +582,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
bank->gpio_chip = rockchip_gpiolib_chip;
gc = &bank->gpio_chip;
- gc->base = bank->pin_base;
+ gc->base = -1;
gc->ngpio = bank->nr_pins;
gc->label = bank->name;
gc->parent = bank->dev;
diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c
index 13b87c8e6d0c..e19701c2ed67 100644
--- a/drivers/gpio/gpio-sim.c
+++ b/drivers/gpio/gpio-sim.c
@@ -36,10 +36,9 @@
#include <linux/sysfs.h>
#include <linux/types.h>
-#include "dev-sync-probe.h"
-
#define GPIO_SIM_NGPIO_MAX 1024
#define GPIO_SIM_PROP_MAX 5 /* Max 4 properties + sentinel. */
+#define GPIO_SIM_HOG_PROP_MAX 5
#define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */
static DEFINE_IDA(gpio_sim_ida);
@@ -545,7 +544,7 @@ static struct platform_driver gpio_sim_driver = {
};
struct gpio_sim_device {
- struct dev_sync_probe_data probe_data;
+ struct platform_device *pdev;
struct config_group group;
int id;
@@ -561,8 +560,6 @@ struct gpio_sim_device {
*/
struct mutex lock;
- struct gpiod_hog *hogs;
-
struct list_head bank_list;
};
@@ -655,6 +652,7 @@ struct gpio_sim_hog {
char *name;
int dir;
+ bool active_low;
};
static struct gpio_sim_hog *to_gpio_sim_hog(struct config_item *item)
@@ -673,7 +671,7 @@ static bool gpio_sim_device_is_live(struct gpio_sim_device *dev)
{
lockdep_assert_held(&dev->lock);
- return !!dev->probe_data.pdev;
+ return !!dev->pdev;
}
static char *gpio_sim_strdup_trimmed(const char *str, size_t count)
@@ -695,7 +693,7 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
guard(mutex)(&dev->lock);
- pdev = dev->probe_data.pdev;
+ pdev = dev->pdev;
if (pdev)
return sprintf(page, "%s\n", dev_name(&pdev->dev));
@@ -774,102 +772,6 @@ static void gpio_sim_set_reserved_ranges(struct gpio_sim_bank *bank,
}
}
-static void gpio_sim_remove_hogs(struct gpio_sim_device *dev)
-{
- struct gpiod_hog *hog;
-
- if (!dev->hogs)
- return;
-
- gpiod_remove_hogs(dev->hogs);
-
- for (hog = dev->hogs; hog->chip_label; hog++) {
- kfree(hog->chip_label);
- kfree(hog->line_name);
- }
-
- kfree(dev->hogs);
- dev->hogs = NULL;
-}
-
-static int gpio_sim_add_hogs(struct gpio_sim_device *dev)
-{
- unsigned int num_hogs = 0, idx = 0;
- struct gpio_sim_bank *bank;
- struct gpio_sim_line *line;
- struct gpiod_hog *hog;
-
- list_for_each_entry(bank, &dev->bank_list, siblings) {
- list_for_each_entry(line, &bank->line_list, siblings) {
- if (line->offset >= bank->num_lines)
- continue;
-
- if (line->hog)
- num_hogs++;
- }
- }
-
- if (!num_hogs)
- return 0;
-
- /* Allocate one more for the sentinel. */
- dev->hogs = kzalloc_objs(*dev->hogs, num_hogs + 1);
- if (!dev->hogs)
- return -ENOMEM;
-
- list_for_each_entry(bank, &dev->bank_list, siblings) {
- list_for_each_entry(line, &bank->line_list, siblings) {
- if (line->offset >= bank->num_lines)
- continue;
-
- if (!line->hog)
- continue;
-
- hog = &dev->hogs[idx++];
-
- /*
- * We need to make this string manually because at this
- * point the device doesn't exist yet and so dev_name()
- * is not available.
- */
- if (gpio_sim_bank_has_label(bank))
- hog->chip_label = kstrdup(bank->label,
- GFP_KERNEL);
- else
- hog->chip_label = kasprintf(GFP_KERNEL,
- "gpio-sim.%u:%pfwP",
- dev->id,
- bank->swnode);
- if (!hog->chip_label) {
- gpio_sim_remove_hogs(dev);
- return -ENOMEM;
- }
-
- /*
- * We need to duplicate this because the hog config
- * item can be removed at any time (and we can't block
- * it) and gpiolib doesn't make a deep copy of the hog
- * data.
- */
- if (line->hog->name) {
- hog->line_name = kstrdup(line->hog->name,
- GFP_KERNEL);
- if (!hog->line_name) {
- gpio_sim_remove_hogs(dev);
- return -ENOMEM;
- }
- }
-
- hog->chip_hwnum = line->offset;
- hog->dflags = line->hog->dir;
- }
- }
-
- gpiod_add_hogs(dev->hogs);
-
- return 0;
-}
-
static struct fwnode_handle *
gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
struct fwnode_handle *parent)
@@ -917,12 +819,61 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
return fwnode_create_software_node(properties, parent);
}
+static int gpio_sim_bank_add_hogs(struct gpio_sim_bank *bank)
+{
+ struct property_entry properties[GPIO_SIM_HOG_PROP_MAX];
+ struct fwnode_handle *swnode;
+ struct gpio_sim_line *line;
+ struct gpio_sim_hog *hog;
+ unsigned int idx;
+ u32 gpios[2];
+
+ list_for_each_entry(line, &bank->line_list, siblings) {
+ if (!line->hog)
+ continue;
+
+ hog = line->hog;
+
+ gpios[0] = line->offset;
+ gpios[1] = hog->active_low ? 1 : 0;
+
+ memset(properties, 0, sizeof(properties));
+
+ idx = 0;
+ properties[idx++] = PROPERTY_ENTRY_BOOL("gpio-hog");
+ properties[idx++] = PROPERTY_ENTRY_U32_ARRAY("gpios", gpios);
+ properties[idx++] = PROPERTY_ENTRY_STRING("line-name", hog->name);
+
+ switch (hog->dir) {
+ case GPIOD_IN:
+ properties[idx++] = PROPERTY_ENTRY_BOOL("input");
+ break;
+ case GPIOD_OUT_HIGH:
+ properties[idx++] = PROPERTY_ENTRY_BOOL("output-high");
+ break;
+ case GPIOD_OUT_LOW:
+ properties[idx++] = PROPERTY_ENTRY_BOOL("output-low");
+ break;
+ default:
+ /* Would have been validated at configfs store. */
+ WARN(1, "Unexpected hog direction value: %d", hog->dir);
+ return -EINVAL;
+ }
+
+ swnode = fwnode_create_software_node(properties, bank->swnode);
+ if (IS_ERR(swnode))
+ return PTR_ERR(swnode);
+ }
+
+ return 0;
+}
+
static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode)
{
struct fwnode_handle *child;
fwnode_for_each_child_node(swnode, child)
- fwnode_remove_software_node(child);
+ gpio_sim_remove_swnode_recursive(child);
fwnode_remove_software_node(swnode);
}
@@ -947,6 +898,7 @@ static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev)
static int gpio_sim_device_activate(struct gpio_sim_device *dev)
{
struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
struct fwnode_handle *swnode;
struct gpio_sim_bank *bank;
int ret;
@@ -974,29 +926,39 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev)
bank->swnode = gpio_sim_make_bank_swnode(bank, swnode);
if (IS_ERR(bank->swnode)) {
ret = PTR_ERR(bank->swnode);
- gpio_sim_remove_swnode_recursive(swnode);
- return ret;
+ goto err_remove_swnode;
}
- }
- ret = gpio_sim_add_hogs(dev);
- if (ret) {
- gpio_sim_remove_swnode_recursive(swnode);
- return ret;
+ ret = gpio_sim_bank_add_hogs(bank);
+ if (ret)
+ goto err_remove_swnode;
}
pdevinfo.name = "gpio-sim";
pdevinfo.fwnode = swnode;
pdevinfo.id = dev->id;
- ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo);
- if (ret) {
- gpio_sim_remove_hogs(dev);
- gpio_sim_remove_swnode_recursive(swnode);
- return ret;
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ goto err_remove_swnode;
+ }
+
+ wait_for_device_probe();
+ if (!device_is_bound(&pdev->dev)) {
+ ret = -ENXIO;
+ goto err_unregister_pdev;
}
+ dev->pdev = pdev;
return 0;
+
+err_unregister_pdev:
+ platform_device_unregister(pdev);
+err_remove_swnode:
+ gpio_sim_remove_swnode_recursive(swnode);
+
+ return ret;
}
static void gpio_sim_device_deactivate(struct gpio_sim_device *dev)
@@ -1005,9 +967,9 @@ static void gpio_sim_device_deactivate(struct gpio_sim_device *dev)
lockdep_assert_held(&dev->lock);
- swnode = dev_fwnode(&dev->probe_data.pdev->dev);
- dev_sync_probe_unregister(&dev->probe_data);
- gpio_sim_remove_hogs(dev);
+ swnode = dev_fwnode(&dev->pdev->dev);
+ platform_device_unregister(dev->pdev);
+ dev->pdev = NULL;
gpio_sim_remove_swnode_recursive(swnode);
}
@@ -1109,7 +1071,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item,
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live(dev))
- return device_for_each_child(&dev->probe_data.pdev->dev, &ctx,
+ return device_for_each_child(&dev->pdev->dev, &ctx,
gpio_sim_emit_chip_name);
return sprintf(page, "none\n");
@@ -1365,9 +1327,46 @@ gpio_sim_hog_config_direction_store(struct config_item *item,
CONFIGFS_ATTR(gpio_sim_hog_config_, direction);
+static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item,
+ char *page)
+{
+ struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
+ struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
+
+ guard(mutex)(&dev->lock);
+
+ return sprintf(page, "%c\n", hog->active_low ? '1' : '0');
+}
+
+static ssize_t
+gpio_sim_hog_config_active_low_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
+ struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
+ bool active_low;
+ int ret;
+
+ guard(mutex)(&dev->lock);
+
+ if (gpio_sim_device_is_live(dev))
+ return -EBUSY;
+
+ ret = kstrtobool(page, &active_low);
+ if (ret)
+ return ret;
+
+ hog->active_low = active_low;
+
+ return count;
+}
+
+CONFIGFS_ATTR(gpio_sim_hog_config_, active_low);
+
static struct configfs_attribute *gpio_sim_hog_config_attrs[] = {
&gpio_sim_hog_config_attr_name,
&gpio_sim_hog_config_attr_direction,
+ &gpio_sim_hog_config_attr_active_low,
NULL
};
@@ -1583,8 +1582,6 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name)
mutex_init(&dev->lock);
INIT_LIST_HEAD(&dev->bank_list);
- dev_sync_probe_init(&dev->probe_data);
-
return &no_free_ptr(dev)->group;
}
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index 493c027afdd6..78d6c78d4aab 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -102,7 +102,7 @@ static int xway_stp_get(struct gpio_chip *gc, unsigned int gpio)
{
struct xway_stp *chip = gpiochip_get_data(gc);
- return (xway_stp_r32(chip->virt, XWAY_STP_CPU0) & BIT(gpio));
+ return !!(xway_stp_r32(chip->virt, XWAY_STP_CPU0) & BIT(gpio));
}
/**
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 9c874f07be75..aa7c3e44234f 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -125,7 +125,6 @@ struct tegra_gpio_soc {
struct tegra_gpio {
struct gpio_chip gpio;
unsigned int num_irq;
- unsigned int *irq;
const struct tegra_gpio_soc *soc;
unsigned int num_irqs_per_bank;
@@ -133,6 +132,8 @@ struct tegra_gpio {
void __iomem *secure;
void __iomem *base;
+
+ unsigned int irq[] __counted_by(num_irq);
};
static const struct tegra_gpio_port *
@@ -857,12 +858,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
struct device_node *np;
struct resource *res;
char **names;
- int err;
+ int node, err;
+
+ err = platform_irq_count(pdev);
+ if (err < 0)
+ return err;
- gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, irq, err), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
+ gpio->num_irq = err;
+
gpio->soc = device_get_match_data(&pdev->dev);
gpio->gpio.label = gpio->soc->name;
gpio->gpio.parent = &pdev->dev;
@@ -889,21 +896,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpio->base))
return PTR_ERR(gpio->base);
- err = platform_irq_count(pdev);
- if (err < 0)
- return err;
-
- gpio->num_irq = err;
-
err = tegra186_gpio_irqs_per_bank(gpio);
if (err < 0)
return err;
- gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq),
- GFP_KERNEL);
- if (!gpio->irq)
- return -ENOMEM;
-
for (i = 0; i < gpio->num_irq; i++) {
err = platform_get_irq(pdev, i);
if (err < 0)
@@ -937,16 +933,22 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
if (!names)
return -ENOMEM;
+ node = dev_to_node(&pdev->dev);
+
for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) {
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
char *name;
for (j = 0; j < port->pins; j++) {
- if (gpio->soc->prefix)
- name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "%s-P%s.%02x",
- gpio->soc->prefix, port->name, j);
+ if (node >= 0)
+ name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL,
+ "%d-%sP%s.%02x", node,
+ gpio->soc->prefix ?: "",
+ port->name, j);
else
- name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "P%s.%02x",
+ name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL,
+ "%sP%s.%02x",
+ gpio->soc->prefix ?: "",
port->name, j);
if (!name)
return -ENOMEM;
@@ -1373,6 +1375,9 @@ static const struct tegra_gpio_soc tegra256_main_soc = {
.has_vm_support = true,
};
+/* Macro to define GPIO name prefix with separator */
+#define TEGRA_GPIO_PREFIX(_x) _x "-"
+
#define TEGRA410_COMPUTE_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA410_COMPUTE, _name, _bank, _port, _pins)
@@ -1388,7 +1393,7 @@ static const struct tegra_gpio_soc tegra410_compute_soc = {
.num_ports = ARRAY_SIZE(tegra410_compute_ports),
.ports = tegra410_compute_ports,
.name = "tegra410-gpio-compute",
- .prefix = "COMPUTE",
+ .prefix = TEGRA_GPIO_PREFIX("COMPUTE"),
.num_irqs_per_bank = 8,
.instance = 0,
};
@@ -1418,7 +1423,7 @@ static const struct tegra_gpio_soc tegra410_system_soc = {
.num_ports = ARRAY_SIZE(tegra410_system_ports),
.ports = tegra410_system_ports,
.name = "tegra410-gpio-system",
- .prefix = "SYSTEM",
+ .prefix = TEGRA_GPIO_PREFIX("SYSTEM"),
.num_irqs_per_bank = 8,
.instance = 0,
};
diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c
index 84b17b83476f..df770ecf28bc 100644
--- a/drivers/gpio/gpio-tps65086.c
+++ b/drivers/gpio/gpio-tps65086.c
@@ -50,7 +50,7 @@ static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset)
if (ret < 0)
return ret;
- return val & BIT(4 + offset);
+ return !!(val & BIT(4 + offset));
}
static int tps65086_gpio_set(struct gpio_chip *chip, unsigned int offset,
diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c
index 992ee231db9f..0207c2b813f4 100644
--- a/drivers/gpio/gpio-ts4800.c
+++ b/drivers/gpio/gpio-ts4800.c
@@ -11,7 +11,6 @@
#include <linux/platform_device.h>
#include <linux/property.h>
-#define DEFAULT_PIN_NUMBER 16
#define INPUT_REG_OFFSET 0x00
#define OUTPUT_REG_OFFSET 0x02
#define DIRECTION_REG_OFFSET 0x04
@@ -23,7 +22,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev)
struct gpio_generic_chip *chip;
void __iomem *base_addr;
int retval;
- u32 ngpios;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -33,12 +31,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev)
if (IS_ERR(base_addr))
return PTR_ERR(base_addr);
- retval = device_property_read_u32(dev, "ngpios", &ngpios);
- if (retval == -EINVAL)
- ngpios = DEFAULT_PIN_NUMBER;
- else if (retval)
- return retval;
-
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 2,
@@ -52,8 +44,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev)
return dev_err_probe(dev, retval,
"failed to initialize the generic GPIO chip\n");
- chip->gc.ngpio = ngpios;
-
return devm_gpiochip_add_data(dev, &chip->gc, NULL);
}
diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
index 15e495c109d2..89087fd48a81 100644
--- a/drivers/gpio/gpio-viperboard.c
+++ b/drivers/gpio/gpio-viperboard.c
@@ -288,7 +288,7 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip,
/* if io is set to output, just return the saved value */
if (gpio->gpiob_out & (1 << offset))
- return gpio->gpiob_val & (1 << offset);
+ return !!(gpio->gpiob_val & (1 << offset));
mutex_lock(&vb->lock);
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
index 955b5efc283e..fe0eac920ced 100644
--- a/drivers/gpio/gpio-virtuser.c
+++ b/drivers/gpio/gpio-virtuser.c
@@ -36,8 +36,6 @@
#include <linux/string_helpers.h>
#include <linux/types.h>
-#include "dev-sync-probe.h"
-
#define GPIO_VIRTUSER_NAME_BUF_LEN 32
static DEFINE_IDA(gpio_virtuser_ida);
@@ -978,7 +976,7 @@ static struct platform_driver gpio_virtuser_driver = {
};
struct gpio_virtuser_device {
- struct dev_sync_probe_data probe_data;
+ struct platform_device *pdev;
struct config_group group;
int id;
@@ -1002,7 +1000,7 @@ gpio_virtuser_device_is_live(struct gpio_virtuser_device *dev)
{
lockdep_assert_held(&dev->lock);
- return !!dev->probe_data.pdev;
+ return !!dev->pdev;
}
struct gpio_virtuser_lookup {
@@ -1342,7 +1340,7 @@ gpio_virtuser_device_config_dev_name_show(struct config_item *item,
guard(mutex)(&dev->lock);
- pdev = dev->probe_data.pdev;
+ pdev = dev->pdev;
if (pdev)
return sprintf(page, "%s\n", dev_name(&pdev->dev));
@@ -1450,6 +1448,7 @@ static int
gpio_virtuser_device_activate(struct gpio_virtuser_device *dev)
{
struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
struct fwnode_handle *swnode;
int ret;
@@ -1471,12 +1470,23 @@ gpio_virtuser_device_activate(struct gpio_virtuser_device *dev)
if (ret)
goto err_remove_swnode;
- ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo);
- if (ret)
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
goto err_remove_lookup_table;
+ }
+
+ wait_for_device_probe();
+ if (!device_is_bound(&pdev->dev)) {
+ ret = -ENXIO;
+ goto err_unregister_pdev;
+ }
+ dev->pdev = pdev;
return 0;
+err_unregister_pdev:
+ platform_device_unregister(pdev);
err_remove_lookup_table:
gpio_virtuser_remove_lookup_table(dev);
err_remove_swnode:
@@ -1492,8 +1502,9 @@ gpio_virtuser_device_deactivate(struct gpio_virtuser_device *dev)
lockdep_assert_held(&dev->lock);
- swnode = dev_fwnode(&dev->probe_data.pdev->dev);
- dev_sync_probe_unregister(&dev->probe_data);
+ swnode = dev_fwnode(&dev->pdev->dev);
+ platform_device_unregister(dev->pdev);
+ dev->pdev = NULL;
gpio_virtuser_remove_lookup_table(dev);
fwnode_remove_software_node(swnode);
}
@@ -1723,7 +1734,6 @@ gpio_virtuser_config_make_device_group(struct config_group *group,
&gpio_virtuser_device_config_group_type);
mutex_init(&dev->lock);
INIT_LIST_HEAD(&dev->lookup_list);
- dev_sync_probe_init(&dev->probe_data);
return &no_free_ptr(dev)->group;
}
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index ced6375d1bad..09f860200a05 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -1220,75 +1220,6 @@ static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
}
}
-static struct gpio_desc *
-acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip,
- struct fwnode_handle *fwnode,
- const char **name,
- unsigned long *lflags,
- enum gpiod_flags *dflags)
-{
- struct gpio_chip *chip = achip->chip;
- struct gpio_desc *desc;
- u32 gpios[2];
- int ret;
-
- *lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- *dflags = GPIOD_ASIS;
- *name = NULL;
-
- ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios,
- ARRAY_SIZE(gpios));
- if (ret < 0)
- return ERR_PTR(ret);
-
- desc = gpiochip_get_desc(chip, gpios[0]);
- if (IS_ERR(desc))
- return desc;
-
- if (gpios[1])
- *lflags |= GPIO_ACTIVE_LOW;
-
- if (fwnode_property_present(fwnode, "input"))
- *dflags |= GPIOD_IN;
- else if (fwnode_property_present(fwnode, "output-low"))
- *dflags |= GPIOD_OUT_LOW;
- else if (fwnode_property_present(fwnode, "output-high"))
- *dflags |= GPIOD_OUT_HIGH;
- else
- return ERR_PTR(-EINVAL);
-
- fwnode_property_read_string(fwnode, "line-name", name);
-
- return desc;
-}
-
-static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip)
-{
- struct gpio_chip *chip = achip->chip;
-
- device_for_each_child_node_scoped(chip->parent, fwnode) {
- unsigned long lflags;
- enum gpiod_flags dflags;
- struct gpio_desc *desc;
- const char *name;
- int ret;
-
- if (!fwnode_property_present(fwnode, "gpio-hog"))
- continue;
-
- desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name,
- &lflags, &dflags);
- if (IS_ERR(desc))
- continue;
-
- ret = gpiod_hog(desc, name, lflags, dflags);
- if (ret) {
- dev_err(chip->parent, "Failed to hog GPIO\n");
- return;
- }
- }
-}
-
void acpi_gpiochip_add(struct gpio_chip *chip)
{
struct acpi_gpio_chip *acpi_gpio;
@@ -1321,7 +1252,6 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
}
acpi_gpiochip_request_regions(acpi_gpio);
- acpi_gpiochip_scan_gpios(acpi_gpio);
acpi_dev_clear_dependencies(adev);
}
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 73ae77f0f213..f36b7c06996d 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1586,15 +1586,16 @@ static const struct file_operations line_fileops = {
#endif
};
+DEFINE_FREE(linereq_free, struct linereq *, if (!IS_ERR_OR_NULL(_T)) linereq_free(_T))
+
static int linereq_create(struct gpio_device *gdev, void __user *ip)
{
struct gpio_v2_line_request ulr;
struct gpio_v2_line_config *lc;
- struct linereq *lr;
- struct file *file;
+ struct linereq *lr __free(linereq_free) = NULL;
u64 flags, edflags;
unsigned int i;
- int fd, ret;
+ int ret;
if (copy_from_user(&ulr, ip, sizeof(ulr)))
return -EFAULT;
@@ -1627,10 +1628,8 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
/* label is only initialized if consumer is set */
lr->label = kstrndup(ulr.consumer, sizeof(ulr.consumer) - 1,
GFP_KERNEL);
- if (!lr->label) {
- ret = -ENOMEM;
- goto out_free_linereq;
- }
+ if (!lr->label)
+ return -ENOMEM;
}
mutex_init(&lr->config_mutex);
@@ -1649,14 +1648,12 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
u32 offset = ulr.offsets[i];
struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
- if (IS_ERR(desc)) {
- ret = PTR_ERR(desc);
- goto out_free_linereq;
- }
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
ret = gpiod_request_user(desc, lr->label);
if (ret)
- goto out_free_linereq;
+ return ret;
lr->lines[i].desc = desc;
flags = gpio_v2_line_config_flags(lc, i);
@@ -1664,7 +1661,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
ret = gpiod_set_transitory(desc, false);
if (ret < 0)
- goto out_free_linereq;
+ return ret;
edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
/*
@@ -1676,16 +1673,16 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
ret = gpiod_direction_output_nonotify(desc, val);
if (ret)
- goto out_free_linereq;
+ return ret;
} else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
ret = gpiod_direction_input_nonotify(desc);
if (ret)
- goto out_free_linereq;
+ return ret;
ret = edge_detector_setup(&lr->lines[i], lc, i,
edflags);
if (ret)
- goto out_free_linereq;
+ return ret;
}
lr->lines[i].edflags = edflags;
@@ -1700,44 +1697,25 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&lr->device_unregistered_nb);
if (ret)
- goto out_free_linereq;
-
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto out_free_linereq;
- }
+ return ret;
- file = anon_inode_getfile("gpio-line", &line_fileops, lr,
- O_RDONLY | O_CLOEXEC);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto out_put_unused_fd;
- }
+ FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
+ anon_inode_getfile("gpio-line", &line_fileops, lr,
+ O_RDONLY | O_CLOEXEC));
+ if (fdf.err)
+ return fdf.err;
+ retain_and_null_ptr(lr);
- ulr.fd = fd;
- if (copy_to_user(ip, &ulr, sizeof(ulr))) {
- /*
- * fput() will trigger the release() callback, so do not go onto
- * the regular error cleanup path here.
- */
- fput(file);
- put_unused_fd(fd);
+ ulr.fd = fd_prepare_fd(fdf);
+ if (copy_to_user(ip, &ulr, sizeof(ulr)))
return -EFAULT;
- }
- fd_install(fd, file);
+ fd_publish(fdf);
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
- lr->num_lines);
+ ulr.num_lines);
return 0;
-
-out_put_unused_fd:
- put_unused_fd(fd);
-out_free_linereq:
- linereq_free(lr);
- return ret;
}
#ifdef CONFIG_GPIO_CDEV_V1
@@ -2010,16 +1988,16 @@ static irqreturn_t lineevent_irq_handler(int irq, void *p)
return IRQ_WAKE_THREAD;
}
+DEFINE_FREE(lineevent_free, struct lineevent_state *, if (!IS_ERR_OR_NULL(_T)) lineevent_free(_T))
+
static int lineevent_create(struct gpio_device *gdev, void __user *ip)
{
struct gpioevent_request eventreq;
- struct lineevent_state *le;
+ struct lineevent_state *le __free(lineevent_free) = NULL;
struct gpio_desc *desc;
- struct file *file;
u32 offset;
u32 lflags;
u32 eflags;
- int fd;
int ret;
int irq, irqflags = 0;
char *label;
@@ -2064,15 +2042,13 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
le->label = kstrndup(eventreq.consumer_label,
sizeof(eventreq.consumer_label) - 1,
GFP_KERNEL);
- if (!le->label) {
- ret = -ENOMEM;
- goto out_free_le;
- }
+ if (!le->label)
+ return -ENOMEM;
}
ret = gpiod_request_user(desc, le->label);
if (ret)
- goto out_free_le;
+ return ret;
le->desc = desc;
le->eflags = eflags;
@@ -2080,15 +2056,13 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
ret = gpiod_direction_input(desc);
if (ret)
- goto out_free_le;
+ return ret;
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
irq = gpiod_to_irq(desc);
- if (irq <= 0) {
- ret = -ENODEV;
- goto out_free_le;
- }
+ if (irq <= 0)
+ return -ENODEV;
if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
irqflags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) ?
@@ -2105,13 +2079,11 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&le->device_unregistered_nb);
if (ret)
- goto out_free_le;
+ return ret;
label = make_irq_label(le->label);
- if (IS_ERR(label)) {
- ret = PTR_ERR(label);
- goto out_free_le;
- }
+ if (IS_ERR(label))
+ return PTR_ERR(label);
/* Request a thread to read the events */
ret = request_threaded_irq(irq,
@@ -2122,46 +2094,25 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
le);
if (ret) {
free_irq_label(label);
- goto out_free_le;
+ return ret;
}
le->irq = irq;
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto out_free_le;
- }
-
- file = anon_inode_getfile("gpio-event",
- &lineevent_fileops,
- le,
- O_RDONLY | O_CLOEXEC);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto out_put_unused_fd;
- }
+ FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
+ anon_inode_getfile("gpio-event", &lineevent_fileops, le,
+ O_RDONLY | O_CLOEXEC));
+ if (fdf.err)
+ return fdf.err;
+ retain_and_null_ptr(le);
- eventreq.fd = fd;
- if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
- /*
- * fput() will trigger the release() callback, so do not go onto
- * the regular error cleanup path here.
- */
- fput(file);
- put_unused_fd(fd);
+ eventreq.fd = fd_prepare_fd(fdf);
+ if (copy_to_user(ip, &eventreq, sizeof(eventreq)))
return -EFAULT;
- }
- fd_install(fd, file);
+ fd_publish(fdf);
return 0;
-
-out_put_unused_fd:
- put_unused_fd(fd);
-out_free_le:
- lineevent_free(le);
- return ret;
}
static void gpio_v2_line_info_to_v1(struct gpio_v2_line_info *info_v2,
@@ -2689,12 +2640,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
struct gpio_chardev_data *cdev;
int ret = -ENOMEM;
- guard(srcu)(&gdev->srcu);
-
- /* Fail on open if the backing gpiochip is gone */
- if (!rcu_access_pointer(gdev->chip))
- return -ENODEV;
-
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENOMEM;
@@ -2782,9 +2727,9 @@ static const struct file_operations gpio_fileops = {
#endif
};
-int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
+int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt)
{
- struct gpio_chip *gc;
+ struct gpio_device *gdev = gc->gpiodev;
int ret;
cdev_init(&gdev->chrdev, &gpio_fileops);
@@ -2802,14 +2747,6 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
return ret;
}
- guard(srcu)(&gdev->srcu);
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc) {
- cdev_device_del(&gdev->chrdev, &gdev->dev);
- destroy_workqueue(gdev->line_state_wq);
- return -ENODEV;
- }
-
gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
return 0;
diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h
index b42644cbffb8..4a9cb3335d99 100644
--- a/drivers/gpio/gpiolib-cdev.h
+++ b/drivers/gpio/gpiolib-cdev.h
@@ -7,7 +7,7 @@
struct gpio_device;
-int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt);
+int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt);
void gpiolib_cdev_unregister(struct gpio_device *gdev);
#endif /* GPIOLIB_CDEV_H */
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index ef1ac68b94b7..2c923d17541f 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -10,11 +10,11 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
+#include <linux/fwnode.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_gpio.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -446,32 +446,6 @@ out:
return desc;
}
-/**
- * of_get_named_gpio() - Get a GPIO number to use with GPIO API
- * @np: device node to get GPIO from
- * @propname: Name of property containing gpio specifier(s)
- * @index: index of the GPIO
- *
- * **DEPRECATED** This function is deprecated and must not be used in new code.
- *
- * Returns:
- * GPIO number to use with Linux generic GPIO API, or one of the errno
- * value on the error condition.
- */
-int of_get_named_gpio(const struct device_node *np, const char *propname,
- int index)
-{
- struct gpio_desc *desc;
-
- desc = of_get_named_gpiod_flags(np, propname, index, NULL);
-
- if (IS_ERR(desc))
- return PTR_ERR(desc);
- else
- return desc_to_gpio(desc);
-}
-EXPORT_SYMBOL_GPL(of_get_named_gpio);
-
/* Converts gpio_lookup_flags into bitmask of GPIO_* values */
static unsigned long of_convert_gpio_flags(enum of_gpio_flags flags)
{
@@ -542,6 +516,10 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np,
{ "reset", "reset-n-io", "marvell,nfc-uart" },
{ "reset", "reset-n-io", "mrvl,nfc-uart" },
#endif
+#if IS_ENABLED(CONFIG_NFC_S3FWRN5_I2C)
+ { "en", "s3fwrn5,en-gpios", "samsung,s3fwrn5-i2c" },
+ { "wake", "s3fwrn5,fw-gpios", "samsung,s3fwrn5-i2c" },
+#endif
#if IS_ENABLED(CONFIG_PCI_LANTIQ)
/* MIPS Lantiq PCI */
{ "reset", "gpio-reset", "lantiq,pci-xway" },
@@ -735,139 +713,26 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
return desc;
}
-/**
- * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
- * @np: device node to get GPIO from
- * @chip: GPIO chip whose hog is parsed
- * @idx: Index of the GPIO to parse
- * @name: GPIO line name
- * @lflags: bitmask of gpio_lookup_flags GPIO_* values - returned from
- * of_find_gpio() or of_parse_own_gpio()
- * @dflags: gpiod_flags - optional GPIO initialization flags
- *
- * Returns:
- * GPIO descriptor to use with Linux GPIO API, or one of the errno
- * value on the error condition.
- */
-static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
- struct gpio_chip *chip,
- unsigned int idx, const char **name,
- unsigned long *lflags,
- enum gpiod_flags *dflags)
+int of_gpiochip_get_lflags(struct gpio_chip *chip,
+ struct fwnode_reference_args *gpiospec,
+ unsigned long *lflags)
{
- struct device_node *chip_np;
enum of_gpio_flags xlate_flags;
- struct of_phandle_args gpiospec;
+ struct of_phandle_args args;
struct gpio_desc *desc;
- unsigned int i;
- u32 tmp;
- int ret;
- chip_np = dev_of_node(&chip->gpiodev->dev);
- if (!chip_np)
- return ERR_PTR(-EINVAL);
+ args.np = to_of_node(gpiospec->fwnode);
+ args.args_count = gpiospec->nargs;
- xlate_flags = 0;
- *lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- *dflags = GPIOD_ASIS;
+ for (int i = 0; i < args.args_count; i++)
+ args.args[i] = gpiospec->args[i];
- ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
- if (ret)
- return ERR_PTR(ret);
-
- gpiospec.np = chip_np;
- gpiospec.args_count = tmp;
-
- for (i = 0; i < tmp; i++) {
- ret = of_property_read_u32_index(np, "gpios", idx * tmp + i,
- &gpiospec.args[i]);
- if (ret)
- return ERR_PTR(ret);
- }
-
- desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags);
+ desc = of_xlate_and_get_gpiod_flags(chip, &args, &xlate_flags);
if (IS_ERR(desc))
- return desc;
+ return PTR_ERR(desc);
*lflags = of_convert_gpio_flags(xlate_flags);
- if (of_property_read_bool(np, "input"))
- *dflags |= GPIOD_IN;
- else if (of_property_read_bool(np, "output-low"))
- *dflags |= GPIOD_OUT_LOW;
- else if (of_property_read_bool(np, "output-high"))
- *dflags |= GPIOD_OUT_HIGH;
- else {
- pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n",
- desc_to_gpio(desc), np);
- return ERR_PTR(-EINVAL);
- }
-
- if (name && of_property_read_string(np, "line-name", name))
- *name = np->name;
-
- return desc;
-}
-
-/**
- * of_gpiochip_add_hog - Add all hogs in a hog device node
- * @chip: gpio chip to act on
- * @hog: device node describing the hogs
- *
- * Returns:
- * 0 on success, or negative errno on failure.
- */
-static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
-{
- enum gpiod_flags dflags;
- struct gpio_desc *desc;
- unsigned long lflags;
- const char *name;
- unsigned int i;
- int ret;
-
- for (i = 0;; i++) {
- desc = of_parse_own_gpio(hog, chip, i, &name, &lflags, &dflags);
- if (IS_ERR(desc))
- break;
-
- ret = gpiod_hog(desc, name, lflags, dflags);
- if (ret < 0)
- return ret;
-
-#ifdef CONFIG_OF_DYNAMIC
- WRITE_ONCE(desc->hog, hog);
-#endif
- }
-
- return 0;
-}
-
-/**
- * of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
- * @chip: gpio chip to act on
- *
- * This is only used by of_gpiochip_add to request/set GPIO initial
- * configuration.
- *
- * Returns:
- * 0 on success, or negative errno on failure.
- */
-static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
-{
- int ret;
-
- for_each_available_child_of_node_scoped(dev_of_node(&chip->gpiodev->dev), np) {
- if (!of_property_read_bool(np, "gpio-hog"))
- continue;
-
- ret = of_gpiochip_add_hog(chip, np);
- if (ret < 0)
- return ret;
-
- of_node_set_flag(np, OF_POPULATED);
- }
-
return 0;
}
@@ -922,7 +787,7 @@ static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
if (!gdev)
return NOTIFY_DONE; /* not for us */
- ret = of_gpiochip_add_hog(gpio_device_get_chip(gdev), rd->dn);
+ ret = gpiochip_add_hog(gpio_device_get_chip(gdev), of_fwnode_handle(rd->dn));
if (ret < 0) {
pr_err("%s: failed to add hogs for %pOF\n", __func__,
rd->dn);
@@ -1201,16 +1066,24 @@ int of_gpiochip_add(struct gpio_chip *chip)
of_node_get(np);
- ret = of_gpiochip_scan_gpios(chip);
- if (ret)
- of_node_put(np);
+ for_each_available_child_of_node_scoped(np, child) {
+ if (of_property_read_bool(child, "gpio-hog"))
+ of_node_set_flag(child, OF_POPULATED);
+ }
return ret;
}
void of_gpiochip_remove(struct gpio_chip *chip)
{
- of_node_put(dev_of_node(&chip->gpiodev->dev));
+ struct device_node *np = dev_of_node(&chip->gpiodev->dev);
+
+ for_each_child_of_node_scoped(np, child) {
+ if (of_property_present(child, "gpio-hog"))
+ of_node_clear_flag(child, OF_POPULATED);
+ }
+
+ of_node_put(np);
}
bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index)
diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
index 2257f7a498a1..218cfe5bc4ac 100644
--- a/drivers/gpio/gpiolib-of.h
+++ b/drivers/gpio/gpiolib-of.h
@@ -10,6 +10,7 @@
struct device_node;
struct fwnode_handle;
+struct fwnode_reference_args;
struct gpio_chip;
struct gpio_desc;
@@ -24,6 +25,9 @@ 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);
+int of_gpiochip_get_lflags(struct gpio_chip *chip,
+ struct fwnode_reference_args *gpiospec,
+ unsigned long *lflags);
#else
static inline struct gpio_desc *of_find_gpio(struct device_node *np,
const char *con_id,
@@ -44,6 +48,12 @@ static inline int of_gpio_count(const struct fwnode_handle *fwnode,
{
return 0;
}
+static inline int of_gpiochip_get_lflags(struct gpio_chip *chip,
+ struct fwnode_reference_args *gpiospec,
+ unsigned long *lflags)
+{
+ return -ENOENT;
+}
#endif /* CONFIG_OF_GPIO */
extern struct notifier_block gpio_of_notifier;
diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c
index 0d7f3f09a0b4..4374067f621e 100644
--- a/drivers/gpio/gpiolib-swnode.c
+++ b/drivers/gpio/gpiolib-swnode.c
@@ -93,6 +93,14 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
ret = swnode_gpio_get_reference(fwnode, propname, idx, &args);
if (ret == 0)
break;
+ if (ret == -ENOTCONN)
+ /*
+ * -ENOTCONN for a software node reference lookup means
+ * that a remote struct software_node exists but has
+ * not yet been registered as a firmware node. Defer
+ * until this happens.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
}
if (ret) {
pr_debug("%s: can't parse '%s' property of node '%pfwP[%d]'\n",
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 270e8060e761..fc06b0c2881b 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -983,10 +983,10 @@ void gpiod_unexport(struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiod_unexport);
-int gpiochip_sysfs_register(struct gpio_device *gdev)
+int gpiochip_sysfs_register(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct gpiodev_data *data;
- struct gpio_chip *chip;
struct device *parent;
int err;
@@ -999,18 +999,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
if (!class_is_registered(&gpio_class))
return 0;
- guard(srcu)(&gdev->srcu);
-
- chip = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!chip)
- return -ENODEV;
-
/*
* For sysfs backward compatibility we need to preserve this
* preferred parenting to the gpio_chip parent field, if set.
*/
- if (chip->parent)
- parent = chip->parent;
+ if (gc->parent)
+ parent = gc->parent;
else
parent = &gdev->dev;
@@ -1029,7 +1023,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
MKDEV(0, 0), data,
gpiochip_groups,
GPIOCHIP_NAME "%d",
- chip->base);
+ gc->base);
if (IS_ERR(data->cdev_base)) {
err = PTR_ERR(data->cdev_base);
kfree(data);
@@ -1053,11 +1047,11 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
return 0;
}
-void gpiochip_sysfs_unregister(struct gpio_device *gdev)
+void gpiochip_sysfs_unregister(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct gpiodev_data *data;
struct gpio_desc *desc;
- struct gpio_chip *chip;
guard(mutex)(&sysfs_lock);
@@ -1065,13 +1059,8 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
if (!data)
return;
- guard(srcu)(&gdev->srcu);
- chip = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!chip)
- return;
-
/* unregister gpiod class devices owned by sysfs */
- for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) {
+ for_each_gpio_desc_with_flag(gc, desc, GPIOD_FLAG_SYSFS) {
gpiod_unexport_unlocked(desc);
gpiod_free(desc);
}
@@ -1090,10 +1079,9 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
*/
static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
{
- struct gpio_device *gdev = gc->gpiodev;
int ret;
- ret = gpiochip_sysfs_register(gdev);
+ ret = gpiochip_sysfs_register(gc);
if (ret)
gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret);
diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h
index b794b396d6a5..d0998de043a2 100644
--- a/drivers/gpio/gpiolib-sysfs.h
+++ b/drivers/gpio/gpiolib-sysfs.h
@@ -7,17 +7,17 @@ struct gpio_device;
#ifdef CONFIG_GPIO_SYSFS
-int gpiochip_sysfs_register(struct gpio_device *gdev);
-void gpiochip_sysfs_unregister(struct gpio_device *gdev);
+int gpiochip_sysfs_register(struct gpio_chip *gc);
+void gpiochip_sysfs_unregister(struct gpio_chip *gc);
#else
-static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
+static inline int gpiochip_sysfs_register(struct gpio_chip *gc)
{
return 0;
}
-static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
+static inline void gpiochip_sysfs_unregister(struct gpio_chip *gc)
{
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 300de30fd920..1e6dce430dca 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -11,6 +11,7 @@
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/fwnode.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -102,9 +103,6 @@ static DEFINE_MUTEX(gpio_devices_lock);
/* Ensures coherence during read-only accesses to the list of GPIO devices. */
DEFINE_STATIC_SRCU(gpio_devices_srcu);
-static DEFINE_MUTEX(gpio_machine_hogs_mutex);
-static LIST_HEAD(gpio_machine_hogs);
-
const char *const gpio_suffixes[] = { "gpios", "gpio", NULL };
static void gpiochip_free_hogs(struct gpio_chip *gc);
@@ -340,7 +338,15 @@ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
}
EXPORT_SYMBOL_GPL(gpio_device_get_chip);
-/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
+/**
+ * gpiochip_find_base_unlocked() - Find a global GPIO number base
+ * @ngpio: Number of consecutive GPIOs to number
+ *
+ * Finds and allocates a consecutive range of unsigned integers representing
+ * the GPIOs on the system. Using this numberspace outside of gpiolibs
+ * internals is STRONGLY DISCOURAGED, drivers and consumers should NOT concern
+ * themselves with this numberspace.
+ */
static int gpiochip_find_base_unlocked(u16 ngpio)
{
unsigned int base = GPIO_DYNAMIC_BASE;
@@ -881,14 +887,14 @@ static const struct device_type gpio_dev_type = {
};
#ifdef CONFIG_GPIO_CDEV
-#define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt))
+#define gcdev_register(gc, devt) gpiolib_cdev_register((gc), (devt))
#define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev))
#else
/*
* gpiolib_cdev_register() indirectly calls device_add(), which is still
* required even when cdev is not selected.
*/
-#define gcdev_register(gdev, devt) device_add(&(gdev)->dev)
+#define gcdev_register(gc, devt) device_add(&(gc)->gpiodev->dev)
#define gcdev_unregister(gdev) device_del(&(gdev)->dev)
#endif
@@ -896,8 +902,9 @@ static const struct device_type gpio_dev_type = {
* An initial reference count has been held in gpiochip_add_data_with_key().
* The caller should drop the reference via gpio_device_put() on errors.
*/
-static int gpiochip_setup_dev(struct gpio_device *gdev)
+static int gpiochip_setup_dev(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
int ret;
@@ -908,11 +915,11 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
if (fwnode && !fwnode->dev)
fwnode_dev_initialized(fwnode, false);
- ret = gcdev_register(gdev, gpio_devt);
+ ret = gcdev_register(gc, gpio_devt);
if (ret)
return ret;
- ret = gpiochip_sysfs_register(gdev);
+ ret = gpiochip_sysfs_register(gc);
if (ret)
goto err_remove_device;
@@ -926,46 +933,131 @@ err_remove_device:
return ret;
}
-static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
+int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
{
+ struct fwnode_handle *gc_node = dev_fwnode(&gc->gpiodev->dev);
+ struct fwnode_reference_args gpiospec;
+ enum gpiod_flags dflags;
+ const char *name = NULL;
struct gpio_desc *desc;
- int rv;
+ unsigned int num_hogs;
+ unsigned long lflags;
+ int ret, argc;
+ /*
+ * For devicetree-based systems, this needs to be defined in bindings
+ * and there's no real default value. For other firmware descriptions
+ * it makes the most sense to use 2 cells for the GPIO offset and
+ * request flags.
+ */
+ u32 cells = 2;
- desc = gpiochip_get_desc(gc, hog->chip_hwnum);
- if (IS_ERR(desc)) {
- gpiochip_err(gc, "%s: unable to get GPIO desc: %ld\n",
- __func__, PTR_ERR(desc));
- return;
+ lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+ dflags = GPIOD_ASIS;
+ name = NULL;
+
+ argc = fwnode_property_count_u32(fwnode, "gpios");
+ if (argc < 0)
+ return argc;
+
+ ret = fwnode_property_read_u32(gc_node, "#gpio-cells", &cells);
+ if (ret && is_of_node(fwnode))
+ return ret;
+ if (argc % cells)
+ return -EINVAL;
+
+ num_hogs = argc / cells;
+
+ u32 *gpios __free(kfree) = kzalloc_objs(*gpios, argc);
+ if (!gpios)
+ return -ENOMEM;
+
+ ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, argc);
+ if (ret < 0)
+ return ret;
+
+ if (fwnode_property_present(fwnode, "input"))
+ dflags |= GPIOD_IN;
+ else if (fwnode_property_present(fwnode, "output-low"))
+ dflags |= GPIOD_OUT_LOW;
+ else if (fwnode_property_present(fwnode, "output-high"))
+ dflags |= GPIOD_OUT_HIGH;
+ else
+ return -EINVAL;
+
+ fwnode_property_read_string(fwnode, "line-name", &name);
+
+ for (unsigned int i = 0; i < num_hogs; i++) {
+ if (is_of_node(fwnode)) {
+ /*
+ * OF-nodes need some additional special handling for
+ * translating of devicetree flags.
+ */
+ memset(&gpiospec, 0, sizeof(gpiospec));
+ gpiospec.fwnode = fwnode;
+ gpiospec.nargs = cells;
+
+ for (unsigned int j = 0; j < cells; j++)
+ gpiospec.args[j] = gpios[i * cells + j];
+
+ ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * GPIO_ACTIVE_LOW is currently the only lookup flag
+ * supported for non-OF firmware nodes.
+ */
+ if (gpios[i * cells + 1])
+ lflags |= GPIO_ACTIVE_LOW;
+ }
+
+ desc = gpiochip_get_desc(gc, gpios[i * cells]);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ ret = gpiod_hog(desc, name, lflags, dflags);
+ if (ret)
+ return ret;
}
- rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags);
- if (rv)
- gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n",
- __func__, gc->label, hog->chip_hwnum, rv);
+ return 0;
}
-static void machine_gpiochip_add(struct gpio_chip *gc)
+static int gpiochip_hog_lines(struct gpio_chip *gc)
{
- struct gpiod_hog *hog;
+ int ret;
- guard(mutex)(&gpio_machine_hogs_mutex);
+ device_for_each_child_node_scoped(&gc->gpiodev->dev, fwnode) {
+ if (!fwnode_property_present(fwnode, "gpio-hog"))
+ continue;
- list_for_each_entry(hog, &gpio_machine_hogs, list) {
- if (!strcmp(gc->label, hog->chip_label))
- gpiochip_machine_hog(gc, hog);
+ ret = gpiochip_add_hog(gc, fwnode);
+ if (ret)
+ return ret;
}
+
+ return 0;
}
static void gpiochip_setup_devs(void)
{
struct gpio_device *gdev;
+ struct gpio_chip *gc;
int ret;
guard(srcu)(&gpio_devices_srcu);
list_for_each_entry_srcu(gdev, &gpio_devices, list,
srcu_read_lock_held(&gpio_devices_srcu)) {
- ret = gpiochip_setup_dev(gdev);
+ guard(srcu)(&gdev->srcu);
+
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc) {
+ dev_err(&gdev->dev, "Underlying GPIO chip is gone\n");
+ continue;
+ }
+
+ ret = gpiochip_setup_dev(gc);
if (ret) {
gpio_device_put(gdev);
dev_err(&gdev->dev,
@@ -1197,7 +1289,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
acpi_gpiochip_add(gc);
- machine_gpiochip_add(gc);
+ ret = gpiochip_hog_lines(gc);
+ if (ret)
+ goto err_remove_of_chip;
ret = gpiochip_irqchip_init_valid_mask(gc);
if (ret)
@@ -1224,7 +1318,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* Otherwise, defer until later.
*/
if (gpiolib_initialized) {
- ret = gpiochip_setup_dev(gdev);
+ ret = gpiochip_setup_dev(gc);
if (ret)
goto err_teardown_shared;
}
@@ -1284,7 +1378,7 @@ void gpiochip_remove(struct gpio_chip *gc)
struct gpio_device *gdev = gc->gpiodev;
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
- gpiochip_sysfs_unregister(gdev);
+ gpiochip_sysfs_unregister(gc);
gpiochip_free_hogs(gc);
gpiochip_free_remaining_irqs(gc);
@@ -1389,7 +1483,16 @@ EXPORT_SYMBOL_GPL(gpio_device_find_by_label);
static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, const void *fwnode)
{
- return device_match_fwnode(&gc->gpiodev->dev, fwnode);
+ struct device *dev = &gc->gpiodev->dev;
+ struct fwnode_handle *node = dev_fwnode(dev);
+
+ if (IS_ERR(fwnode))
+ return 0;
+
+ if (device_match_fwnode(dev, fwnode))
+ return 1;
+
+ return node && node->secondary == fwnode;
}
/**
@@ -4469,42 +4572,6 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
}
EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table);
-/**
- * gpiod_add_hogs() - register a set of GPIO hogs from machine code
- * @hogs: table of gpio hog entries with a zeroed sentinel at the end
- */
-void gpiod_add_hogs(struct gpiod_hog *hogs)
-{
- struct gpiod_hog *hog;
-
- guard(mutex)(&gpio_machine_hogs_mutex);
-
- for (hog = &hogs[0]; hog->chip_label; hog++) {
- list_add_tail(&hog->list, &gpio_machine_hogs);
-
- /*
- * The chip may have been registered earlier, so check if it
- * exists and, if so, try to hog the line now.
- */
- struct gpio_device *gdev __free(gpio_device_put) =
- gpio_device_find_by_label(hog->chip_label);
- if (gdev)
- gpiochip_machine_hog(gpio_device_get_chip(gdev), hog);
- }
-}
-EXPORT_SYMBOL_GPL(gpiod_add_hogs);
-
-void gpiod_remove_hogs(struct gpiod_hog *hogs)
-{
- struct gpiod_hog *hog;
-
- guard(mutex)(&gpio_machine_hogs_mutex);
-
- for (hog = &hogs[0]; hog->chip_label; hog++)
- list_del(&hog->list);
-}
-EXPORT_SYMBOL_GPL(gpiod_remove_hogs);
-
static bool gpiod_match_lookup_table(struct device *dev,
const struct gpiod_lookup_table *table)
{
@@ -4557,8 +4624,8 @@ static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *c
return desc;
}
- dev_warn(dev, "cannot find GPIO line %s, deferring\n",
- p->key);
+ dev_dbg(dev, "cannot find GPIO line %s, deferring\n",
+ p->key);
return ERR_PTR(-EPROBE_DEFER);
}
@@ -4572,8 +4639,8 @@ static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *c
* consumer be probed again or let the Deferred
* Probe infrastructure handle the error.
*/
- dev_warn(dev, "cannot find GPIO chip %s, deferring\n",
- p->key);
+ dev_dbg(dev, "cannot find GPIO chip %s, deferring\n",
+ p->key);
return ERR_PTR(-EPROBE_DEFER);
}
@@ -5317,23 +5384,14 @@ core_initcall(gpiolib_dev_init);
#ifdef CONFIG_DEBUG_FS
-static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *gc)
{
bool active_low, is_irq, is_out;
struct gpio_desc *desc;
unsigned int gpio = 0;
- struct gpio_chip *gc;
unsigned long flags;
int value;
- guard(srcu)(&gdev->srcu);
-
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc) {
- seq_puts(s, "Underlying GPIO chip is gone\n");
- return;
- }
-
for_each_gpio_desc(gc, desc) {
guard(srcu)(&desc->gdev->desc_srcu);
flags = READ_ONCE(desc->flags);
@@ -5446,7 +5504,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
if (gc->dbg_show)
gc->dbg_show(s, gc);
else
- gpiolib_dbg_show(s, gdev);
+ gpiolib_dbg_show(s, gc);
return 0;
}
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 3abb90385829..dc4cb61a9318 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -17,11 +17,14 @@
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include <linux/srcu.h>
#include <linux/workqueue.h>
#define GPIOCHIP_NAME "gpiochip"
+struct fwnode_handle;
+
/**
* struct gpio_device - internal state container for GPIO devices
* @dev: the GPIO device struct
@@ -107,7 +110,7 @@ extern const char *const gpio_suffixes[];
if (con_id) \
snprintf(propname, sizeof(propname), "%s-%s", con_id, __gs); \
else \
- snprintf(propname, sizeof(propname), "%s", __gs); \
+ strscpy(propname, __gs); \
1; \
}); \
__suffixes++)
@@ -273,6 +276,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags);
+int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode);
int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
const char *gpiod_get_label(struct gpio_desc *desc);