summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/gpio/cdns,gpio.yaml7
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml3
-rw-r--r--Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml71
-rw-r--r--Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml7
-rw-r--r--MAINTAINERS2
-rw-r--r--arch/mips/alchemy/board-xxs1500.c2
-rw-r--r--arch/mips/alchemy/devboards/db1000.c2
-rw-r--r--arch/mips/alchemy/devboards/db1200.c2
-rw-r--r--arch/mips/alchemy/devboards/db1550.c2
-rw-r--r--arch/mips/bcm47xx/workarounds.c2
-rw-r--r--arch/mips/bcm63xx/boards/board_bcm963xx.c1
-rw-r--r--arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h2
-rw-r--r--arch/sh/Kconfig1
-rw-r--r--arch/sh/boards/Kconfig8
-rw-r--r--arch/sh/boards/board-magicpanelr2.c2
-rw-r--r--arch/sh/boards/board-sh7757lcr.c2
-rw-r--r--arch/sh/boards/board-urquell.c2
-rw-r--r--arch/sh/boards/mach-ap325rxa/setup.c2
-rw-r--r--arch/sh/boards/mach-ecovec24/setup.c2
-rw-r--r--arch/sh/boards/mach-highlander/Kconfig1
-rw-r--r--arch/sh/boards/mach-highlander/pinmux-r7785rp.c2
-rw-r--r--arch/sh/boards/mach-kfr2r09/lcd_wqvga.c2
-rw-r--r--arch/sh/boards/mach-kfr2r09/setup.c2
-rw-r--r--arch/sh/boards/mach-migor/lcd_qvga.c2
-rw-r--r--arch/sh/boards/mach-migor/setup.c2
-rw-r--r--arch/sh/boards/mach-rsk/Kconfig3
-rw-r--r--arch/sh/boards/mach-rsk/devices-rsk7269.c1
-rw-r--r--arch/sh/boards/mach-se/7724/setup.c2
-rw-r--r--arch/sh/include/mach-common/mach/magicpanelr2.h2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-shx3.c2
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/platform/olpc/olpc-xo1-sci.c2
-rw-r--r--drivers/base/regmap/regmap-irq.c22
-rw-r--r--drivers/base/swnode.c79
-rw-r--r--drivers/base/test/Kconfig5
-rw-r--r--drivers/base/test/Makefile2
-rw-r--r--drivers/base/test/swnode-devlink-test.c337
-rw-r--r--drivers/gpib/gpio/gpib_bitbang.c1
-rw-r--r--drivers/gpio/Kconfig19
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-104-idi-48.c7
-rw-r--r--drivers/gpio/gpio-amd-fch.c22
-rw-r--r--drivers/gpio/gpio-bd72720.c24
-rw-r--r--drivers/gpio/gpio-bt8xx.c21
-rw-r--r--drivers/gpio/gpio-by-pinctrl.c4
-rw-r--r--drivers/gpio/gpio-davinci.c8
-rw-r--r--drivers/gpio/gpio-i8255.c4
-rw-r--r--drivers/gpio/gpio-idio-16.c6
-rw-r--r--drivers/gpio/gpio-max7360.c8
-rw-r--r--drivers/gpio/gpio-mt7621.c42
-rw-r--r--drivers/gpio/gpio-mvebu.c44
-rw-r--r--drivers/gpio/gpio-mxs.c4
-rw-r--r--drivers/gpio/gpio-nomadik.c64
-rw-r--r--drivers/gpio/gpio-omap.c9
-rw-r--r--drivers/gpio/gpio-pca9570.c2
-rw-r--r--drivers/gpio/gpio-pcf857x.c13
-rw-r--r--drivers/gpio/gpio-pcie-idio-24.c6
-rw-r--r--drivers/gpio/gpio-pl061.c5
-rw-r--r--drivers/gpio/gpio-pxa.c2
-rw-r--r--drivers/gpio/gpio-rcar.c1
-rw-r--r--drivers/gpio/gpio-rda.c4
-rw-r--r--drivers/gpio/gpio-regmap.c104
-rw-r--r--drivers/gpio/gpio-rockchip.c16
-rw-r--r--drivers/gpio/gpio-rtd1625.c677
-rw-r--r--drivers/gpio/gpio-sa1100.c2
-rw-r--r--drivers/gpio/gpio-sifive.c1
-rw-r--r--drivers/gpio/gpio-sloppy-logic-analyzer.c1
-rw-r--r--drivers/gpio/gpio-sodaville.c2
-rw-r--r--drivers/gpio/gpio-stmpe.c2
-rw-r--r--drivers/gpio/gpio-tb10x.c5
-rw-r--r--drivers/gpio/gpio-tegra.c4
-rw-r--r--drivers/gpio/gpio-timberdale.c2
-rw-r--r--drivers/gpio/gpio-vf610.c1
-rw-r--r--drivers/gpio/gpiolib-cdev.c9
-rw-r--r--drivers/gpio/gpiolib-kunit.c571
-rw-r--r--drivers/gpio/gpiolib-swnode.c23
-rw-r--r--drivers/iio/adc/ad7173.c8
-rw-r--r--drivers/iio/addac/stx104.c6
-rw-r--r--drivers/input/keyboard/gpio_keys.c7
-rw-r--r--drivers/input/keyboard/gpio_keys_polled.c4
-rw-r--r--drivers/input/misc/Kconfig3
-rw-r--r--drivers/input/misc/soc_button_array.c1
-rw-r--r--drivers/mfd/aat2870-core.c2
-rw-r--r--drivers/mfd/arizona-irq.c2
-rw-r--r--drivers/mfd/lp3943.c2
-rw-r--r--drivers/mfd/sm501.c2
-rw-r--r--drivers/mfd/tps6105x.c2
-rw-r--r--drivers/mfd/tps65911-comparator.c2
-rw-r--r--drivers/mfd/wm8994-irq.c2
-rw-r--r--drivers/pcmcia/bcm63xx_pcmcia.c2
-rw-r--r--drivers/pcmcia/db1xxx_ss.c2
-rw-r--r--drivers/pcmcia/sa1100_h3600.c2
-rw-r--r--drivers/pcmcia/soc_common.c2
-rw-r--r--drivers/pcmcia/xxs1500_ss.c2
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c1
-rw-r--r--drivers/phy/rockchip/phy-rockchip-usbdp.c2
-rw-r--r--drivers/phy/ti/phy-j721e-wiz.c1
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm63xx.c1
-rw-r--r--drivers/pinctrl/pinctrl-tps6594.c1
-rw-r--r--include/kunit/fwnode.h29
-rw-r--r--include/linux/gpio/machine.h2
-rw-r--r--include/linux/gpio/regmap.h73
-rw-r--r--include/linux/gpio_keys.h2
-rw-r--r--include/linux/mfd/lp3943.h2
-rw-r--r--include/linux/mfd/ti-lmu.h1
-rw-r--r--include/linux/mfd/tps65910.h2
-rw-r--r--include/linux/mfd/ucb1x00.h2
-rw-r--r--include/linux/regmap.h2
-rw-r--r--lib/kunit/Makefile1
-rw-r--r--lib/kunit/fwnode.c146
-rw-r--r--sound/pci/Kconfig1
-rw-r--r--sound/pci/cs5535audio/cs5535audio_olpc.c2
-rw-r--r--sound/soc/codecs/cs42l84.c1
-rw-r--r--sound/soc/codecs/cx2072x.c2
-rw-r--r--sound/soc/codecs/dmic.c1
115 files changed, 2363 insertions, 297 deletions
diff --git a/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml b/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml
index a84d60b39459..7eacf5af6554 100644
--- a/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/cdns,gpio.yaml
@@ -14,8 +14,11 @@ properties:
oneOf:
- const: cdns,gpio-r1p02
- items:
- - enum:
- - axiado,ax3000-gpio
+ - const: axiado,ax3000-gpio
+ - const: cdns,gpio-r1p02
+ - items:
+ - const: axiado,ax3005-gpio
+ - const: axiado,ax3000-gpio
- const: cdns,gpio-r1p02
reg:
diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
index 4f955f855e1a..4631388a7d91 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
@@ -23,6 +23,9 @@ properties:
- const: diodes,pi4ioe5v6534q
- const: nxp,pcal6534
- items:
+ - const: kinetic,kts1622
+ - const: ti,tcal6416
+ - items:
- enum:
- exar,xra1202
- maxim,max7310
diff --git a/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml b/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml
new file mode 100644
index 000000000000..f13c910b73c6
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/realtek,rtd1625-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC RTD1625 GPIO controller
+
+maintainers:
+ - Tzuyi Chang <tychang@realtek.com>
+
+description: |
+ GPIO controller for the Realtek RTD1625 SoC, featuring a per-pin register
+ architecture that differs significantly from earlier RTD series controllers.
+ Each GPIO has dedicated registers for configuration (direction, input/output
+ values, debounce), and interrupt control supporting edge and level detection
+ modes.
+
+properties:
+ compatible:
+ enum:
+ - realtek,rtd1625-iso-gpio
+ - realtek,rtd1625-isom-gpio
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: Interrupt number of the assert GPIO interrupt, which is
+ triggered when there is a rising edge.
+ - description: Interrupt number of the deassert GPIO interrupt, which is
+ triggered when there is a falling edge.
+ - description: Interrupt number of the level-sensitive GPIO interrupt,
+ triggered by a configured logic level.
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 2
+
+ gpio-ranges: true
+
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
+required:
+ - compatible
+ - reg
+ - gpio-ranges
+ - gpio-controller
+ - "#gpio-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ gpio@89100 {
+ compatible = "realtek,rtd1625-isom-gpio";
+ reg = <0x89100 0x30>;
+ interrupt-parent = <&iso_m_irq_mux>;
+ interrupts = <0>, <1>, <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-ranges = <&isom_pinctrl 0 0 4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml
index bdd83f42615c..774e9c7de606 100644
--- a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml
+++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml
@@ -44,6 +44,13 @@ properties:
power-domains:
maxItems: 1
+ rockchip,grf:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ The phandle of the syscon node managing the IO control registers
+ of this bank, on SoCs such as the RV1106 where each GPIO bank has
+ its own IOC block.
+
patternProperties:
"^.+-hog(-[0-9]+)?$":
type: object
diff --git a/MAINTAINERS b/MAINTAINERS
index 9f8d226b305a..d3f266e39800 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25094,10 +25094,12 @@ R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
R: Daniel Scally <djrscally@gmail.com>
R: Heikki Krogerus <heikki.krogerus@linux.intel.com>
R: Sakari Ailus <sakari.ailus@linux.intel.com>
+R: Bartosz Golaszewski <brgl@kernel.org>
L: linux-acpi@vger.kernel.org
S: Maintained
F: drivers/base/property.c
F: drivers/base/swnode.c
+F: drivers/base/test/swnode-devlink-test.c
F: include/linux/fwnode.h
F: include/linux/property.h
diff --git a/arch/mips/alchemy/board-xxs1500.c b/arch/mips/alchemy/board-xxs1500.c
index f175bce2987f..a03762dde4e7 100644
--- a/arch/mips/alchemy/board-xxs1500.c
+++ b/arch/mips/alchemy/board-xxs1500.c
@@ -11,7 +11,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <asm/bootinfo.h>
diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c
index 8fb24b220e3a..5eff34a8683b 100644
--- a/arch/mips/alchemy/devboards/db1000.c
+++ b/arch/mips/alchemy/devboards/db1000.c
@@ -8,7 +8,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/init.h>
diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c
index de2a9083ed9a..539b311fc8af 100644
--- a/arch/mips/alchemy/devboards/db1200.c
+++ b/arch/mips/alchemy/devboards/db1200.c
@@ -7,7 +7,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c
index b8295a5c2e9a..579fc8f1eaed 100644
--- a/arch/mips/alchemy/devboards/db1550.c
+++ b/arch/mips/alchemy/devboards/db1550.c
@@ -7,7 +7,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/io.h>
diff --git a/arch/mips/bcm47xx/workarounds.c b/arch/mips/bcm47xx/workarounds.c
index 745c6228eb2c..dc9e5483347d 100644
--- a/arch/mips/bcm47xx/workarounds.c
+++ b/arch/mips/bcm47xx/workarounds.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "bcm47xx_private.h"
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <bcm47xx_board.h>
#include <bcm47xx.h>
diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c
index c5617b889b1c..71628dac6c26 100644
--- a/arch/mips/bcm63xx/boards/board_bcm963xx.c
+++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
@@ -7,6 +7,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
+#include <linux/gpio/legacy.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/platform_device.h>
diff --git a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h
index 830f53f28e3f..428cf4508f9a 100644
--- a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h
+++ b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h
@@ -3,7 +3,7 @@
#define BOARD_BCM963XX_H_
#include <linux/types.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/leds.h>
#include <bcm63xx_dev_enet.h>
#include <bcm63xx_dev_usb_usbd.h>
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index d5795067befa..d60f1d5a94c0 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -462,6 +462,7 @@ config CPU_SUBTYPE_SHX3
select CPU_SHX3
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select GPIOLIB
+ select GPIOLIB_LEGACY
select PINCTRL
# SH4AL-DSP Processor Support
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index 1af93be61b1f..d89b74177233 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -80,6 +80,7 @@ config SH_7724_SOLUTION_ENGINE
select SOLUTION_ENGINE
depends on CPU_SUBTYPE_SH7724
select GPIOLIB
+ select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
imply SND_SOC_AK4642 if SND_SIMPLE_CARD
help
@@ -199,6 +200,7 @@ config SH_SH7757LCR
bool "SH7757LCR"
depends on CPU_SUBTYPE_SH7757
select GPIOLIB
+ select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
config SH_SH7785LCR
@@ -226,6 +228,7 @@ config SH_URQUELL
bool "Urquell"
depends on CPU_SUBTYPE_SH7786
select GPIOLIB
+ select GPIOLIB_LEGACY
select HAVE_PCI
select NO_IOPORT_MAP if !PCI
@@ -233,6 +236,7 @@ config SH_MIGOR
bool "Migo-R"
depends on CPU_SUBTYPE_SH7722
select GPIOLIB
+ select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Migo-R if configuring for the SH7722 Migo-R platform
@@ -242,6 +246,7 @@ config SH_AP325RXA
bool "AP-325RXA"
depends on CPU_SUBTYPE_SH7723
select GPIOLIB
+ select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Renesas "AP-325RXA" support.
@@ -251,6 +256,7 @@ config SH_KFR2R09
bool "KFR2R09"
depends on CPU_SUBTYPE_SH7724
select GPIOLIB
+ select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
"Kit For R2R for 2009" support.
@@ -259,6 +265,7 @@ config SH_ECOVEC
bool "EcoVec"
depends on CPU_SUBTYPE_SH7724
select GPIOLIB
+ select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
imply SND_SOC_DA7210 if SND_SIMPLE_CARD
help
@@ -329,6 +336,7 @@ config SH_MAGIC_PANEL_R2
bool "Magic Panel R2"
depends on CPU_SUBTYPE_SH7720
select GPIOLIB
+ select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Magic Panel R2 if configuring for Magic Panel R2.
diff --git a/arch/sh/boards/board-magicpanelr2.c b/arch/sh/boards/board-magicpanelr2.c
index 75de893152af..3f14118a1015 100644
--- a/arch/sh/boards/board-magicpanelr2.c
+++ b/arch/sh/boards/board-magicpanelr2.c
@@ -10,7 +10,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c
index bca54e489e11..6d18f59ef261 100644
--- a/arch/sh/boards/board-sh7757lcr.c
+++ b/arch/sh/boards/board-sh7757lcr.c
@@ -7,7 +7,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/irq.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
diff --git a/arch/sh/boards/board-urquell.c b/arch/sh/boards/board-urquell.c
index dad2b3b40735..1f73c04d341f 100644
--- a/arch/sh/boards/board-urquell.c
+++ b/arch/sh/boards/board-urquell.c
@@ -14,7 +14,7 @@
#include <linux/smc91x.h>
#include <linux/mtd/physmap.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/sh_intc.h>
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index bb5004a8ac02..17c3f568d92e 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -17,7 +17,7 @@
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/i2c.h>
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index a641e26f8fdf..ca4b4dd1ddef 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -20,7 +20,7 @@
#include <linux/mmc/host.h>
#include <linux/platform_data/sh_mmcif.h>
#include <linux/mtd/physmap.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/platform_data/gpio_backlight.h>
#include <linux/platform_data/tmio.h>
diff --git a/arch/sh/boards/mach-highlander/Kconfig b/arch/sh/boards/mach-highlander/Kconfig
index b0abd03cac4e..cd3a553ce30c 100644
--- a/arch/sh/boards/mach-highlander/Kconfig
+++ b/arch/sh/boards/mach-highlander/Kconfig
@@ -20,6 +20,7 @@ config SH_R7785RP
bool "R7785RP board support"
depends on CPU_SUBTYPE_SH7785
select GPIOLIB
+ select GPIOLIB_LEGACY
endchoice
diff --git a/arch/sh/boards/mach-highlander/pinmux-r7785rp.c b/arch/sh/boards/mach-highlander/pinmux-r7785rp.c
index 689bd8732d9e..3a1057ee9ace 100644
--- a/arch/sh/boards/mach-highlander/pinmux-r7785rp.c
+++ b/arch/sh/boards/mach-highlander/pinmux-r7785rp.c
@@ -3,7 +3,7 @@
* Copyright (C) 2008 Paul Mundt
*/
#include <linux/init.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <cpu/sh7785.h>
#include <mach/highlander.h>
diff --git a/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c b/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c
index f6bbac106d13..68716660de34 100644
--- a/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c
+++ b/arch/sh/boards/mach-kfr2r09/lcd_wqvga.c
@@ -14,7 +14,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <video/sh_mobile_lcdc.h>
#include <mach/kfr2r09.h>
#include <cpu/sh7724.h>
diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c
index 70236859919d..30dd7dae7906 100644
--- a/arch/sh/boards/mach-kfr2r09/setup.c
+++ b/arch/sh/boards/mach-kfr2r09/setup.c
@@ -14,7 +14,7 @@
#include <linux/clkdev.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/i2c.h>
#include <linux/init.h>
diff --git a/arch/sh/boards/mach-migor/lcd_qvga.c b/arch/sh/boards/mach-migor/lcd_qvga.c
index 4ebf130510bc..f72934e370fd 100644
--- a/arch/sh/boards/mach-migor/lcd_qvga.c
+++ b/arch/sh/boards/mach-migor/lcd_qvga.c
@@ -14,7 +14,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <video/sh_mobile_lcdc.h>
#include <cpu/sh7722.h>
#include <mach/migor.h>
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index 1853e6319a66..b0198e7cb9ab 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -22,7 +22,7 @@
#include <linux/smc91x.h>
#include <linux/delay.h>
#include <linux/clk.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/videodev2.h>
#include <linux/sh_intc.h>
diff --git a/arch/sh/boards/mach-rsk/Kconfig b/arch/sh/boards/mach-rsk/Kconfig
index f0299bc4416f..3810937aa5d4 100644
--- a/arch/sh/boards/mach-rsk/Kconfig
+++ b/arch/sh/boards/mach-rsk/Kconfig
@@ -12,16 +12,19 @@ config SH_RSK7201
config SH_RSK7203
bool "RSK7203"
select GPIOLIB
+ select GPIOLIB_LEGACY
depends on CPU_SUBTYPE_SH7203
config SH_RSK7264
bool "RSK2+SH7264"
select GPIOLIB
+ select GPIOLIB_LEGACY
depends on CPU_SUBTYPE_SH7264
config SH_RSK7269
bool "RSK2+SH7269"
select GPIOLIB
+ select GPIOLIB_LEGACY
depends on CPU_SUBTYPE_SH7269
endchoice
diff --git a/arch/sh/boards/mach-rsk/devices-rsk7269.c b/arch/sh/boards/mach-rsk/devices-rsk7269.c
index 4b1e386b51dd..54edd9958eca 100644
--- a/arch/sh/boards/mach-rsk/devices-rsk7269.c
+++ b/arch/sh/boards/mach-rsk/devices-rsk7269.c
@@ -11,7 +11,6 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/smsc911x.h>
-#include <linux/gpio.h>
#include <asm/machvec.h>
#include <asm/io.h>
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index e500feb91053..bed8f44fcecb 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -15,7 +15,7 @@
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/sh_keysc.h>
diff --git a/arch/sh/include/mach-common/mach/magicpanelr2.h b/arch/sh/include/mach-common/mach/magicpanelr2.h
index c2d218cea74b..29d86dd21b7b 100644
--- a/arch/sh/include/mach-common/mach/magicpanelr2.h
+++ b/arch/sh/include/mach-common/mach/magicpanelr2.h
@@ -10,8 +10,6 @@
#ifndef __ASM_SH_MAGICPANELR2_H
#define __ASM_SH_MAGICPANELR2_H
-#include <linux/gpio.h>
-
#define __IO_PREFIX mpr2
#include <asm/io_generic.h>
diff --git a/arch/sh/kernel/cpu/sh4a/setup-shx3.c b/arch/sh/kernel/cpu/sh4a/setup-shx3.c
index 7014d6d199b3..60be8dca54f6 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-shx3.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-shx3.c
@@ -9,7 +9,7 @@
#include <linux/serial.h>
#include <linux/serial_sci.h>
#include <linux/io.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/sh_timer.h>
#include <linux/sh_intc.h>
#include <cpu/shx3.h>
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 485e014330cc..48ccc3e6059d 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2978,6 +2978,7 @@ config OLPC
bool "One Laptop Per Child support"
depends on !X86_PAE
select GPIOLIB
+ select GPIOLIB_LEGACY
select OF
select OF_PROMTREE
select IRQ_DOMAIN
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
index 97eb4738d602..8b5b3626b559 100644
--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -9,7 +9,7 @@
#include <linux/cs5535.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 07234d415b51..99b55b1053ee 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -296,6 +296,26 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
+static int regmap_irq_reqres(struct irq_data *data)
+{
+ struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ if (d->chip->irq_reqres)
+ return d->chip->irq_reqres(d->chip->irq_drv_data, hwirq);
+
+ return 0;
+}
+
+static void regmap_irq_relres(struct irq_data *data)
+{
+ struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ if (d->chip->irq_relres)
+ d->chip->irq_relres(d->chip->irq_drv_data, hwirq);
+}
+
static const struct irq_chip regmap_irq_chip = {
.irq_bus_lock = regmap_irq_lock,
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
@@ -303,6 +323,8 @@ static const struct irq_chip regmap_irq_chip = {
.irq_enable = regmap_irq_enable,
.irq_set_type = regmap_irq_set_type,
.irq_set_wake = regmap_irq_set_wake,
+ .irq_request_resources = regmap_irq_reqres,
+ .irq_release_resources = regmap_irq_relres,
};
static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 869228a65cb3..1f2315858cc3 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -699,6 +699,62 @@ software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode,
return 0;
}
+static int software_node_add_links(struct fwnode_handle *fwnode)
+{
+ const struct software_node_ref_args *ref, *ref_array;
+ struct swnode *swnode = to_swnode(fwnode);
+ const struct property_entry *prop;
+ struct fwnode_handle *refnode;
+ unsigned int count;
+
+ if (!swnode || !swnode->node->properties)
+ return 0;
+
+ /*
+ * Unlike Device Tree, where phandles appear in many non-supplier
+ * contexts and a curated allowlist is required, a software node only
+ * carries a DEV_PROP_REF property when the author explicitly describes
+ * a reference to another node. Every such reference is therefore an
+ * intentional supplier dependency, so we create fwnode links for all
+ * of them.
+ */
+ for (prop = swnode->node->properties; prop->name; prop++) {
+ if (prop->type != DEV_PROP_REF || prop->is_inline)
+ continue;
+
+ /*
+ * TODO: Graph "remote-endpoint" references go both ways
+ * between endpoint child nodes and would create endpoint
+ * cycles. Let's leave it out for now until we have potential
+ * users.
+ */
+ if (!strcmp(prop->name, "remote-endpoint"))
+ continue;
+
+ ref_array = prop->pointer;
+ count = prop->length / sizeof(*ref_array);
+
+ for (unsigned int i = 0; i < count; i++) {
+ ref = &ref_array[i];
+
+ if (ref->swnode)
+ refnode = software_node_fwnode(ref->swnode);
+ else if (ref->fwnode)
+ refnode = ref->fwnode;
+ else
+ continue;
+
+ /* Supplier not registered yet, or self-reference. */
+ if (!refnode || refnode == &swnode->fwnode)
+ continue;
+
+ fwnode_link_add(&swnode->fwnode, refnode, 0);
+ }
+ }
+
+ return 0;
+}
+
static const struct fwnode_operations software_node_ops = {
.get = software_node_get,
.put = software_node_put,
@@ -716,6 +772,7 @@ static const struct fwnode_operations software_node_ops = {
.graph_get_remote_endpoint = software_node_graph_get_remote_endpoint,
.graph_get_port_parent = software_node_graph_get_port_parent,
.graph_parse_endpoint = software_node_graph_parse_endpoint,
+ .add_links = software_node_add_links,
};
/* -------------------------------------------------------------------------- */
@@ -787,6 +844,8 @@ static void software_node_release(struct kobject *kobj)
{
struct swnode *swnode = kobj_to_swnode(kobj);
+ fwnode_links_purge(&swnode->fwnode);
+
if (swnode->parent) {
ida_free(&swnode->parent->child_ids, swnode->id);
list_del(&swnode->entry);
@@ -1105,6 +1164,17 @@ void software_node_notify(struct device *dev)
if (!swnode)
return;
+ /*
+ * When the software node is the device's secondary firmware node,
+ * the core only records the owning device on the primary fwnode
+ * (see device_add()). fw_devlink resolves a supplier device through
+ * fwnode->dev, so without this a consumer referencing the software
+ * node could never find the supplier device and would defer forever.
+ * Make fwnode.dev point to its owner in that case.
+ */
+ if (!device_match_fwnode(dev, &swnode->fwnode) && !swnode->fwnode.dev)
+ swnode->fwnode.dev = dev;
+
swnode_get(swnode);
ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node");
if (ret)
@@ -1127,6 +1197,15 @@ void software_node_notify_remove(struct device *dev)
sysfs_remove_link(&swnode->kobj, dev_name(dev));
sysfs_remove_link(&dev->kobj, "software_node");
+
+ /*
+ * Drop the device pointer mirrored onto a secondary software node in
+ * software_node_notify(). For a primary software node the core owns
+ * fwnode->dev and clears it in device_del().
+ */
+ if (!device_match_fwnode(dev, &swnode->fwnode) && swnode->fwnode.dev == dev)
+ swnode->fwnode.dev = NULL;
+
swnode_put(swnode);
if (swnode->managed) {
diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig
index 2756870615cc..1ecf0791241a 100644
--- a/drivers/base/test/Kconfig
+++ b/drivers/base/test/Kconfig
@@ -18,3 +18,8 @@ config DRIVER_PE_KUNIT_TEST
tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
+
+config DRIVER_SWNODE_KUNIT_TEST
+ tristate "KUnit Tests for software node fw_devlink links" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile
index e321dfc7e922..9ced7bbd569f 100644
--- a/drivers/base/test/Makefile
+++ b/drivers/base/test/Makefile
@@ -6,3 +6,5 @@ obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN)
+
+obj-$(CONFIG_DRIVER_SWNODE_KUNIT_TEST) += swnode-devlink-test.o
diff --git a/drivers/base/test/swnode-devlink-test.c b/drivers/base/test/swnode-devlink-test.c
new file mode 100644
index 000000000000..f53549e92611
--- /dev/null
+++ b/drivers/base/test/swnode-devlink-test.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
+ */
+
+#include <linux/array_size.h>
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <kunit/fwnode.h>
+#include <kunit/platform_device.h>
+#include <kunit/test.h>
+
+static int swnode_count_suppliers(struct fwnode_handle *fwnode)
+{
+ struct fwnode_link *link;
+ unsigned int count = 0;
+
+ /*
+ * The suppliers and consumers lists should typically only be accessed
+ * with the fwnode_link_lock taken but it's private to the driver core.
+ *
+ * These are tests and at this point nobody should be modifying them so
+ * let's just access the list.
+ */
+ list_for_each_entry(link, &fwnode->suppliers, c_hook)
+ count++;
+
+ return count;
+}
+
+/* True if a supplier link con->sup exists, checked from both list ends. */
+static bool swnode_has_link(struct fwnode_handle *consumer,
+ struct fwnode_handle *supplier)
+{
+ bool from_con = false, from_sup = false;
+ struct fwnode_link *link;
+
+ list_for_each_entry(link, &consumer->suppliers, c_hook) {
+ if (link->supplier == supplier && link->consumer == consumer)
+ from_con = true;
+ }
+
+ list_for_each_entry(link, &supplier->consumers, s_hook) {
+ if (link->supplier == supplier && link->consumer == consumer)
+ from_sup = true;
+ }
+
+ return from_con && from_sup;
+}
+
+/* A single reference creates exactly one supplier link, on both list ends. */
+static void swnode_devlink_test_single_ref(struct kunit *test)
+{
+ static const struct software_node supp_swnode = {
+ .name = "swnode-devlink-test-supplier",
+ };
+
+ struct fwnode_handle *cons_fwnode, *supp_fwnode;
+ int ret;
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("supplier", &supp_swnode),
+ { }
+ };
+
+ supp_fwnode = kunit_software_node_register(test, &supp_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
+
+ cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
+
+ ret = fwnode_call_int_op(cons_fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 1);
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(cons_fwnode, supp_fwnode));
+}
+
+/* Multiple distinct references create multiple supplier links. */
+static void swnode_devlink_test_multiple_refs(struct kunit *test)
+{
+ static const struct software_node supp1_swnode = {
+ .name = "swnode-devlink-test-supplier-1",
+ };
+ static const struct software_node supp2_swnode = {
+ .name = "swnode-devlink-test-supplier-2",
+ };
+ static const struct software_node *supp_nodes[] = {
+ &supp1_swnode, &supp2_swnode, NULL
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("foo", &supp1_swnode),
+ PROPERTY_ENTRY_REF("bar", &supp2_swnode),
+ { }
+ };
+
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ ret = kunit_software_node_register_node_group(test, supp_nodes);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = fwnode_call_int_op(fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
+}
+
+/* A reference to an unregistered node creates no link (graceful skip). */
+static void swnode_devlink_test_unregistered_ref(struct kunit *test)
+{
+ static const struct software_node supp_swnode = {
+ .name = "swnode-devlink-test-supplier",
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("supplier", &supp_swnode),
+ { }
+ };
+
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = fwnode_call_int_op(fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 0);
+}
+
+/* Graph "remote-endpoint" references are excluded. */
+static void swnode_devlink_test_remote_endpoint_excluded(struct kunit *test)
+{
+ static const struct software_node ep_swnode = {
+ .name = "swnode-devlink-test-end-point"
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("remote-endpoint", &ep_swnode),
+ { }
+ };
+
+ struct fwnode_handle *cons_fwnode, *supp_fwnode;
+ int ret;
+
+ supp_fwnode = kunit_software_node_register(test, &ep_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
+
+ cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
+
+ ret = fwnode_call_int_op(cons_fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 0);
+}
+
+/* A reference array creates one link per registered element. */
+static void swnode_devlink_test_ref_array(struct kunit *test)
+{
+ static const struct software_node supp1_swnode = {
+ .name = "swnode-devlink-test-supplier-1",
+ };
+ static const struct software_node supp2_swnode = {
+ .name = "swnode-devlink-test-supplier-2",
+ };
+ static const struct software_node *supp_nodes[] = {
+ &supp1_swnode, &supp2_swnode, NULL
+ };
+ static const struct software_node_ref_args refs[] = {
+ SOFTWARE_NODE_REFERENCE(&supp1_swnode),
+ SOFTWARE_NODE_REFERENCE(&supp2_swnode, 4, 2),
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF_ARRAY("suppliers", refs),
+ { }
+ };
+
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ ret = kunit_software_node_register_node_group(test, supp_nodes);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = fwnode_call_int_op(fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
+}
+
+/*
+ * End-to-end test: fw_devlink must defer a consumer's probe until its
+ * supplier has probed.
+ *
+ * The reference created by software_node_add_links() is only useful if the
+ * driver core promotes it to a real device_link and uses it to order probing.
+ * This test drives actual probing through the platform bus and asserts the
+ * supplier binds before the consumer.
+ */
+
+#define SWNODE_DEVLINK_TEST_SUPPLIER "swnode-link-supplier"
+#define SWNODE_DEVLINK_TEST_CONSUMER "swnode-link-consumer"
+#define SWNODE_DEVLINK_TEST_TIMEOUT_MS (2 * MSEC_PER_SEC)
+
+struct swnode_test_probe_order {
+ /* Names in the order their drivers' .probe ran. */
+ const char *probed[2];
+ unsigned int count;
+ wait_queue_head_t wq;
+};
+
+static int swnode_test_record_probe(struct platform_device *pdev)
+{
+ struct swnode_test_probe_order *order = platform_get_drvdata(pdev);
+
+ if (order && order->count < ARRAY_SIZE(order->probed)) {
+ order->probed[order->count++] = dev_name(&pdev->dev);
+ wake_up_interruptible(&order->wq);
+ }
+
+ return 0;
+}
+
+static struct platform_driver swnode_test_supplier_driver = {
+ .probe = swnode_test_record_probe,
+ .driver = {
+ .name = SWNODE_DEVLINK_TEST_SUPPLIER,
+ },
+};
+
+static struct platform_driver swnode_test_consumer_driver = {
+ .probe = swnode_test_record_probe,
+ .driver = {
+ .name = SWNODE_DEVLINK_TEST_CONSUMER,
+ },
+};
+
+static void swnode_devlink_test_probe_order(struct kunit *test)
+{
+ static const struct software_node supplier_swnode = {
+ .name = "swnode-devlink-test-supplier",
+ };
+
+ const struct property_entry consumer_props[] = {
+ PROPERTY_ENTRY_REF("supplier-ref", &supplier_swnode),
+ { }
+ };
+
+ struct platform_device *supplier, *consumer;
+ struct swnode_test_probe_order *order;
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ order = kunit_kzalloc(test, sizeof(*order), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, order);
+ init_waitqueue_head(&order->wq);
+
+ fwnode = kunit_software_node_register(test, &supplier_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = kunit_platform_driver_register(test, &swnode_test_supplier_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = kunit_platform_driver_register(test, &swnode_test_consumer_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ supplier = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_SUPPLIER,
+ PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier);
+ consumer = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_CONSUMER,
+ PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, consumer);
+
+ platform_set_drvdata(supplier, order);
+ platform_set_drvdata(consumer, order);
+
+ ret = kunit_device_add_software_node(test, &supplier->dev, &supplier_swnode);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = device_create_managed_software_node(&consumer->dev,
+ consumer_props, NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_platform_device_add(test, consumer);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = kunit_platform_device_add(test, supplier);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = wait_event_interruptible_timeout(order->wq,
+ order->count == 2,
+ msecs_to_jiffies(SWNODE_DEVLINK_TEST_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ KUNIT_EXPECT_STREQ(test, order->probed[0], SWNODE_DEVLINK_TEST_SUPPLIER);
+ KUNIT_EXPECT_STREQ(test, order->probed[1], SWNODE_DEVLINK_TEST_CONSUMER);
+
+ /* Tear down the consumer (and its device link) before the supplier. */
+ kunit_platform_device_unregister(test, consumer);
+}
+
+static struct kunit_case swnode_test_cases[] = {
+ KUNIT_CASE(swnode_devlink_test_single_ref),
+ KUNIT_CASE(swnode_devlink_test_multiple_refs),
+ KUNIT_CASE(swnode_devlink_test_unregistered_ref),
+ KUNIT_CASE(swnode_devlink_test_remote_endpoint_excluded),
+ KUNIT_CASE(swnode_devlink_test_ref_array),
+ KUNIT_CASE(swnode_devlink_test_probe_order),
+ { }
+};
+
+static struct kunit_suite swnode_test_suite = {
+ .name = "software-node-links",
+ .test_cases = swnode_test_cases,
+};
+
+kunit_test_suite(swnode_test_suite);
+
+MODULE_DESCRIPTION("Test module for software node fw_devlink support");
+MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpib/gpio/gpib_bitbang.c b/drivers/gpib/gpio/gpib_bitbang.c
index 0e227980b493..34d14b94a0b8 100644
--- a/drivers/gpib/gpio/gpib_bitbang.c
+++ b/drivers/gpib/gpio/gpib_bitbang.c
@@ -64,7 +64,6 @@
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
-#include <linux/gpio.h>
#include <linux/irq.h>
static int sn7516x_used = 1, sn7516x;
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7a09e42672b4..a48586bb8edb 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -653,7 +653,7 @@ config GPIO_ROCKCHIP
config GPIO_RTD
tristate "Realtek DHC GPIO support"
depends on ARCH_REALTEK || COMPILE_TEST
- default y
+ default ARCH_REALTEK
select GPIOLIB_IRQCHIP
help
This option enables support for GPIOs found on Realtek DHC(Digital
@@ -663,6 +663,20 @@ config GPIO_RTD
Say yes here to support GPIO functionality and GPIO interrupt on
Realtek DHC SoCs.
+config GPIO_RTD1625
+ tristate "Realtek DHC RTD1625 GPIO support"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ default ARCH_REALTEK
+ select GPIOLIB_IRQCHIP
+ select GPIO_REGMAP
+ select REGMAP_MMIO
+ help
+ This option enables support for the GPIO controller on Realtek
+ DHC (Digital Home Center) RTD1625 SoC.
+
+ Say yes here to support both basic GPIO line functionality
+ and GPIO interrupt handling capabilities for this platform.
+
config GPIO_SAMA5D2_PIOBU
tristate "SAMA5D2 PIOBU GPIO support"
depends on OF
@@ -949,7 +963,8 @@ config GPIO_IDT3243X
endmenu
menu "Port-mapped I/O GPIO drivers"
- depends on X86 && HAS_IOPORT # I/O space access
+ depends on X86 || COMPILE_TEST
+ depends on HAS_IOPORT # I/O space access
config GPIO_VX855
tristate "VIA VX855/VX875 GPIO"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 23aedc107cc5..dc9e6d643b5b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -161,6 +161,7 @@ obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o
+obj-$(CONFIG_GPIO_RTD1625) += gpio-rtd1625.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index ba73ee9c0c29..fb017569cfa8 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -36,9 +36,10 @@ MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
#define IDI48_IRQ_STATUS 0x7
#define IDI48_IRQ_ENABLE IDI48_IRQ_STATUS
-static int idi_48_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
- unsigned int offset, unsigned int *reg,
- unsigned int *mask)
+static int idi_48_reg_mask_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask)
{
const unsigned int line = offset % 8;
const unsigned int stride = offset / 8;
diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c
index 9f329938202b..9b9d75acf35a 100644
--- a/drivers/gpio/gpio-amd-fch.c
+++ b/drivers/gpio/gpio-amd-fch.c
@@ -48,13 +48,11 @@ static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv,
static int amd_fch_gpio_direction_input(struct gpio_chip *gc,
unsigned int offset)
{
- unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
- spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
@@ -62,12 +60,11 @@ static int amd_fch_gpio_direction_input(struct gpio_chip *gc,
static int amd_fch_gpio_direction_output(struct gpio_chip *gc,
unsigned int gpio, int value)
{
- unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
u32 val;
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
val = readl_relaxed(ptr);
if (value)
@@ -77,33 +74,28 @@ static int amd_fch_gpio_direction_output(struct gpio_chip *gc,
writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
- spin_unlock_irqrestore(&priv->lock, flags);
-
return 0;
}
static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
{
int ret;
- unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION);
- spin_unlock_irqrestore(&priv->lock, flags);
return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}
static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
{
- unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
u32 mask;
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
mask = readl_relaxed(ptr);
if (value)
@@ -112,22 +104,18 @@ static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
mask &= ~AMD_FCH_GPIO_FLAG_WRITE;
writel_relaxed(mask, ptr);
- spin_unlock_irqrestore(&priv->lock, flags);
-
return 0;
}
static int amd_fch_gpio_get(struct gpio_chip *gc,
unsigned int offset)
{
- unsigned long flags;
u32 val;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
val = readl_relaxed(ptr);
- spin_unlock_irqrestore(&priv->lock, flags);
return FIELD_GET(AMD_FCH_GPIO_FLAG_READ, val);
}
diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c
index 306e23411209..c124d964e4cf 100644
--- a/drivers/gpio/gpio-bd72720.c
+++ b/drivers/gpio/gpio-bd72720.c
@@ -85,9 +85,11 @@ static int bd72720gpi_get(struct bd72720_gpio *bdgpio, unsigned int reg_offset)
static int bd72720gpo_get(struct bd72720_gpio *bdgpio,
unsigned int offset)
{
- const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
- BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
- BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+ static const int regs[] = {
+ BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL
+ };
int ret, val;
ret = regmap_read(bdgpio->regmap, regs[offset], &val);
@@ -111,9 +113,11 @@ static int bd72720gpo_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
- const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
- BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
- BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+ static const int regs[] = {
+ BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL
+ };
if (BIT(offset) & bdgpio->gpio_is_input) {
dev_dbg(bdgpio->dev, "pin %d not output.\n", offset);
@@ -132,9 +136,11 @@ static int bd72720_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
unsigned long config)
{
struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
- const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
- BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
- BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+ static const int regs[] = {
+ BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL
+ };
/*
* We can only set the output mode, which makes sense only when output
diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c
index 324eeb77dbd5..f3a2321bc388 100644
--- a/drivers/gpio/gpio-bt8xx.c
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -154,12 +154,18 @@ static int bt8xxgpio_probe(struct pci_dev *dev,
const struct pci_device_id *pci_id)
{
struct bt8xxgpio *bg;
+ void __iomem *mmio;
int err;
+ mmio = devm_ioremap_resource(&dev->dev, pci_resource_n(dev, 0));
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
bg = devm_kzalloc(&dev->dev, sizeof(struct bt8xxgpio), GFP_KERNEL);
if (!bg)
return -ENOMEM;
+ bg->mmio = mmio;
bg->pdev = dev;
spin_lock_init(&bg->lock);
@@ -168,24 +174,9 @@ static int bt8xxgpio_probe(struct pci_dev *dev,
dev_err(&dev->dev, "can't enable device.\n");
return err;
}
- if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0),
- pci_resource_len(dev, 0),
- "bt8xxgpio")) {
- dev_warn(&dev->dev, "can't request iomem (0x%llx).\n",
- (unsigned long long)pci_resource_start(dev, 0));
- err = -EBUSY;
- goto err_disable;
- }
pci_set_master(dev);
pci_set_drvdata(dev, bg);
- bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000);
- if (!bg->mmio) {
- dev_err(&dev->dev, "ioremap() failed\n");
- err = -EIO;
- goto err_disable;
- }
-
/* Disable interrupts */
bgwrite(0, BT848_INT_MASK);
diff --git a/drivers/gpio/gpio-by-pinctrl.c b/drivers/gpio/gpio-by-pinctrl.c
index fb8440acb31d..f54efd635f13 100644
--- a/drivers/gpio/gpio-by-pinctrl.c
+++ b/drivers/gpio/gpio-by-pinctrl.c
@@ -54,11 +54,11 @@ static int pin_control_gpio_direction_output(struct gpio_chip *chip,
{
int ret;
- ret = pinctrl_gpio_direction_output(chip, offset);
+ ret = pin_control_gpio_set(chip, offset, val);
if (ret)
return ret;
- return pin_control_gpio_set(chip, offset, val);
+ return pinctrl_gpio_direction_output(chip, offset);
}
static int pin_control_gpio_probe(struct platform_device *pdev)
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 270cd7c88812..cccbaea1dec2 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -285,9 +285,9 @@ static void gpio_irq_unmask(struct irq_data *d)
gpiochip_enable_irq(&chips->chip, hwirq);
- status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+ status &= IRQ_TYPE_EDGE_BOTH;
if (!status)
- status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
+ status = IRQ_TYPE_EDGE_BOTH;
if (status & IRQ_TYPE_EDGE_FALLING)
writel_relaxed(mask, &g->set_falling);
@@ -297,7 +297,7 @@ static void gpio_irq_unmask(struct irq_data *d)
static int gpio_irq_type(struct irq_data *d, unsigned trigger)
{
- if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ if (trigger & ~IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
return 0;
@@ -400,7 +400,7 @@ static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger)
mask = __gpio_mask(i);
- if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ if (trigger & ~IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_FALLING)
diff --git a/drivers/gpio/gpio-i8255.c b/drivers/gpio/gpio-i8255.c
index 953018bfa2b1..541a348447cd 100644
--- a/drivers/gpio/gpio-i8255.c
+++ b/drivers/gpio/gpio-i8255.c
@@ -67,8 +67,8 @@ static int i8255_ppi_init(struct regmap *const map, const unsigned int base)
return regmap_write(map, base + I8255_PORTC, 0x00);
}
-static int i8255_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
- unsigned int offset, unsigned int *reg,
+static int i8255_reg_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
const unsigned int ppi = offset / I8255_NGPIO;
diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c
index 4fbae6f6a497..9ac967c6b430 100644
--- a/drivers/gpio/gpio-idio-16.c
+++ b/drivers/gpio/gpio-idio-16.c
@@ -66,9 +66,9 @@ static int idio_16_handle_mask_sync(const int index, const unsigned int mask_buf
return 0;
}
-static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
- const unsigned int offset, unsigned int *const reg,
- unsigned int *const mask)
+static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, enum gpio_regmap_operation op,
+ const unsigned int base, const unsigned int offset,
+ unsigned int *const reg, unsigned int *const mask)
{
unsigned int stride;
diff --git a/drivers/gpio/gpio-max7360.c b/drivers/gpio/gpio-max7360.c
index d12cf1dc8d57..6755d58c4534 100644
--- a/drivers/gpio/gpio-max7360.c
+++ b/drivers/gpio/gpio-max7360.c
@@ -93,6 +93,7 @@ static int max7360_set_gpos_count(struct device *dev, struct regmap *regmap)
}
static int max7360_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
@@ -127,7 +128,7 @@ static int max7360_handle_mask_sync(const int index,
const unsigned int mask_buf,
void *const irq_drv_data)
{
- struct regmap *regmap = irq_drv_data;
+ struct regmap *regmap = gpio_regmap_get_drvdata(irq_drv_data);
int ret;
for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
@@ -170,7 +171,6 @@ static int max7360_gpio_probe(struct platform_device *pdev)
/* Create custom IRQ configuration. */
irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);
- gpio_config.regmap_irq_chip = irq_chip;
if (!irq_chip)
return -ENOMEM;
@@ -181,7 +181,9 @@ static int max7360_gpio_probe(struct platform_device *pdev)
irq_chip->num_irqs = MAX7360_MAX_GPIO;
irq_chip->irqs = max7360_regmap_irqs;
irq_chip->handle_mask_sync = max7360_handle_mask_sync;
- irq_chip->irq_drv_data = regmap;
+
+ gpio_config.regmap_irq_chip = irq_chip;
+ gpio_config.drvdata = regmap;
for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c
index 1b0b5247d3c9..a86f6b1060fc 100644
--- a/drivers/gpio/gpio-mt7621.c
+++ b/drivers/gpio/gpio-mt7621.c
@@ -68,7 +68,7 @@ mt7621_gpio_gc_to_priv(struct gpio_chip *gc)
}
static inline struct mtk_gc *
-to_mediatek_gpio(struct gpio_chip *chip)
+to_mt7621_gpio(struct gpio_chip *chip)
{
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(chip);
@@ -137,7 +137,7 @@ mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank)
}
static void
-mediatek_gpio_irq_unmask(struct irq_data *d)
+mt7621_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
@@ -159,7 +159,7 @@ mediatek_gpio_irq_unmask(struct irq_data *d)
}
static void
-mediatek_gpio_irq_mask(struct irq_data *d)
+mt7621_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
@@ -181,7 +181,7 @@ mediatek_gpio_irq_mask(struct irq_data *d)
}
static int
-mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
+mt7621_gpio_irq_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
@@ -194,7 +194,7 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
rg->hlevel | rg->llevel) & mask)
return 0;
- type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ type = IRQ_TYPE_EDGE_BOTH;
}
rg->rising &= ~mask;
@@ -245,11 +245,11 @@ mt7621_gpio_irq_relres(struct irq_data *d)
}
static int
-mediatek_gpio_xlate(struct gpio_chip *chip,
+mt7621_gpio_xlate(struct gpio_chip *chip,
const struct of_phandle_args *spec, u32 *flags)
{
int gpio = spec->args[0];
- struct mtk_gc *rg = to_mediatek_gpio(chip);
+ struct mtk_gc *rg = to_mt7621_gpio(chip);
if (rg->bank != gpio / MTK_BANK_WIDTH)
return -EINVAL;
@@ -264,10 +264,10 @@ static const struct irq_chip mt7621_irq_chip = {
.name = "mt7621-gpio",
.irq_request_resources = mt7621_gpio_irq_reqres,
.irq_release_resources = mt7621_gpio_irq_relres,
- .irq_mask_ack = mediatek_gpio_irq_mask,
- .irq_mask = mediatek_gpio_irq_mask,
- .irq_unmask = mediatek_gpio_irq_unmask,
- .irq_set_type = mediatek_gpio_irq_type,
+ .irq_mask_ack = mt7621_gpio_irq_mask,
+ .irq_mask = mt7621_gpio_irq_mask,
+ .irq_unmask = mt7621_gpio_irq_unmask,
+ .irq_set_type = mt7621_gpio_irq_type,
.flags = IRQCHIP_IMMUTABLE,
};
@@ -380,7 +380,7 @@ mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
}
static int
-mediatek_gpio_bank_probe(struct device *dev, int bank)
+mt7621_gpio_bank_probe(struct device *dev, int bank)
{
struct gpio_generic_chip_config config;
struct mtk *mtk = dev_get_drvdata(dev);
@@ -416,7 +416,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
}
rg->chip.gc.of_gpio_n_cells = 2;
- rg->chip.gc.of_xlate = mediatek_gpio_xlate;
+ rg->chip.gc.of_xlate = mt7621_gpio_xlate;
rg->chip.gc.ngpio = MTK_BANK_WIDTH;
rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
dev_name(dev), bank);
@@ -443,7 +443,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
}
static int
-mediatek_gpio_probe(struct platform_device *pdev)
+mt7621_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk *mtk;
@@ -477,7 +477,7 @@ mediatek_gpio_probe(struct platform_device *pdev)
return ret;
for (i = 0; i < MTK_BANK_CNT; i++) {
- ret = mediatek_gpio_bank_probe(dev, i);
+ ret = mt7621_gpio_bank_probe(dev, i);
if (ret)
return ret;
}
@@ -485,18 +485,18 @@ mediatek_gpio_probe(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id mediatek_gpio_match[] = {
+static const struct of_device_id mt7621_gpio_match[] = {
{ .compatible = "mediatek,mt7621-gpio" },
{},
};
-MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
+MODULE_DEVICE_TABLE(of, mt7621_gpio_match);
-static struct platform_driver mediatek_gpio_driver = {
- .probe = mediatek_gpio_probe,
+static struct platform_driver mt7621_gpio_driver = {
+ .probe = mt7621_gpio_probe,
.driver = {
.name = "mt7621_gpio",
- .of_match_table = mediatek_gpio_match,
+ .of_match_table = mt7621_gpio_match,
},
};
-builtin_platform_driver(mediatek_gpio_driver);
+builtin_platform_driver(mt7621_gpio_driver);
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index a010604e5ff7..93b8a08b04b9 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -37,6 +37,7 @@
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
@@ -114,7 +115,7 @@ struct mvebu_gpio_chip {
struct regmap *regs;
u32 offset;
struct regmap *percpu_regs;
- int irqbase;
+ int bank_irq[4];
struct irq_domain *domain;
int soc_variant;
@@ -604,6 +605,34 @@ static const struct regmap_config mvebu_gpio_regmap_config = {
};
/*
+ * Forward wake-up configuration to the parent bank IRQ.
+ * @d: interrupt data
+ * @enable: enable as wake-up if non-zero
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int mvebu_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ int bank;
+ int irq;
+
+ bank = d->hwirq / 8;
+ if (bank >= ARRAY_SIZE(mvchip->bank_irq))
+ return -EINVAL;
+
+ irq = mvchip->bank_irq[bank];
+ if (irq <= 0)
+ return -EINVAL;
+
+ if (enable)
+ return enable_irq_wake(irq);
+
+ return disable_irq_wake(irq);
+}
+
+/*
* Functions implementing the pwm_chip methods
*/
static struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip)
@@ -1253,7 +1282,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
err = irq_alloc_domain_generic_chips(
mvchip->domain, ngpios, 2, np->name, handle_level_irq,
- IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, 0);
+ IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, IRQ_GC_INIT_NESTED_LOCK);
if (err) {
dev_err(&pdev->dev, "couldn't allocate irq chips %s (DT).\n",
mvchip->chip.label);
@@ -1267,18 +1296,22 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
gc = irq_get_domain_generic_chip(mvchip->domain, 0);
gc->private = mvchip;
ct = &gc->chip_types[0];
- ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
+ ct->type = IRQ_TYPE_LEVEL_MASK;
ct->chip.irq_mask = mvebu_gpio_level_irq_mask;
ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask;
ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
+ ct->chip.irq_set_wake = mvebu_gpio_set_wake_irq;
+ ct->chip.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
ct->chip.name = mvchip->chip.label;
ct = &gc->chip_types[1];
- ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ ct->type = IRQ_TYPE_EDGE_BOTH;
ct->chip.irq_ack = mvebu_gpio_irq_ack;
ct->chip.irq_mask = mvebu_gpio_edge_irq_mask;
ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask;
ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
+ ct->chip.irq_set_wake = mvebu_gpio_set_wake_irq;
+ ct->chip.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
ct->handler = handle_edge_irq;
ct->chip.name = mvchip->chip.label;
@@ -1287,13 +1320,14 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
* interrupt handlers, with each handler dealing with 8 GPIO
* pins.
*/
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ARRAY_SIZE(mvchip->bank_irq); i++) {
int irq = platform_get_irq_optional(pdev, i);
if (irq < 0)
continue;
irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
mvchip);
+ mvchip->bank_irq[i] = irq;
}
return 0;
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index 5635694bf9f4..900315aba85a 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -198,7 +198,7 @@ static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
gc->private = port;
ct = &gc->chip_types[0];
- ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
+ ct->type = IRQ_TYPE_LEVEL_MASK;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
@@ -210,7 +210,7 @@ static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
ct->regs.disable = PINCTRL_PIN2IRQ(port) + MXS_CLR;
ct = &gc->chip_types[1];
- ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ ct->type = IRQ_TYPE_EDGE_BOTH;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c
index 5dc9f9d5912a..208fed631975 100644
--- a/drivers/gpio/gpio-nomadik.c
+++ b/drivers/gpio/gpio-nomadik.c
@@ -519,22 +519,23 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
gpio_dev = bus_find_device_by_fwnode(&platform_bus_type, fwnode);
if (!gpio_dev) {
- dev_err(dev, "populate \"%pfwP\": device not found\n", fwnode);
- return ERR_PTR(-ENODEV);
+ ret = -ENODEV;
+ dev_err_probe(dev, ret, "populate \"%pfwP\": device not found\n", fwnode);
+ return ERR_PTR(ret);
}
gpio_pdev = to_platform_device(gpio_dev);
if (device_property_read_u32(gpio_dev, "gpio-bank", &id)) {
- dev_err(dev, "populate: gpio-bank property not found\n");
- platform_device_put(gpio_pdev);
- return ERR_PTR(-EINVAL);
+ ret = -EINVAL;
+ dev_err_probe(dev, ret, "populate: gpio-bank property not found\n");
+ goto err_put_pdev;
}
#ifdef CONFIG_PINCTRL_NOMADIK
if (id >= ARRAY_SIZE(nmk_gpio_chips)) {
- dev_err(dev, "populate: invalid id: %u\n", id);
- platform_device_put(gpio_pdev);
- return ERR_PTR(-EINVAL);
+ ret = -EINVAL;
+ dev_err_probe(dev, ret, "populate: invalid id: %u\n", id);
+ goto err_put_pdev;
}
/* Already populated? */
nmk_chip = nmk_gpio_chips[id];
@@ -546,8 +547,8 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
nmk_chip = devm_kzalloc(dev, sizeof(*nmk_chip), GFP_KERNEL);
if (!nmk_chip) {
- platform_device_put(gpio_pdev);
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto err_put_pdev;
}
if (device_property_read_u32(gpio_dev, "ngpios", &ngpio)) {
@@ -568,16 +569,17 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
res = platform_get_resource(gpio_pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
- platform_device_put(gpio_pdev);
- return ERR_CAST(base);
+ ret = PTR_ERR(base);
+ goto err_put_pdev;
}
nmk_chip->addr = base;
/* NOTE: do not use devm_ here! */
clk = clk_get_optional(gpio_dev, NULL);
if (IS_ERR(clk)) {
- platform_device_put(gpio_pdev);
- return ERR_CAST(clk);
+ ret = PTR_ERR(clk);
+ dev_err_probe(dev, ret, "failed getting clock\n");
+ goto err_put_pdev;
}
clk_prepare(clk);
nmk_chip->clk = clk;
@@ -585,12 +587,9 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
/* NOTE: do not use devm_ here! */
reset = reset_control_get_optional_shared(gpio_dev, NULL);
if (IS_ERR(reset)) {
- clk_unprepare(clk);
- clk_put(clk);
- platform_device_put(gpio_pdev);
- dev_err(dev, "failed getting reset control: %pe\n",
- reset);
- return ERR_CAST(reset);
+ ret = PTR_ERR(reset);
+ dev_err_probe(dev, ret, "failed getting reset control\n");
+ goto err_unprepare_clk;
}
/*
@@ -600,18 +599,23 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
*/
ret = reset_control_deassert(reset);
if (ret) {
- reset_control_put(reset);
- clk_unprepare(clk);
- clk_put(clk);
- platform_device_put(gpio_pdev);
- dev_err(dev, "failed reset deassert: %d\n", ret);
- return ERR_PTR(ret);
+ dev_err_probe(dev, ret, "failed reset deassert\n");
+ goto err_put_reset;
}
#ifdef CONFIG_PINCTRL_NOMADIK
nmk_gpio_chips[id] = nmk_chip;
#endif
return nmk_chip;
+
+err_put_reset:
+ reset_control_put(reset);
+err_unprepare_clk:
+ clk_unprepare(clk);
+ clk_put(clk);
+err_put_pdev:
+ platform_device_put(gpio_pdev);
+ return ERR_PTR(ret);
}
static void nmk_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
@@ -647,10 +651,8 @@ static int nmk_gpio_probe(struct platform_device *pdev)
int ret;
nmk_chip = nmk_gpio_populate_chip(dev_fwnode(dev), pdev);
- if (IS_ERR(nmk_chip)) {
- dev_err(dev, "could not populate nmk chip struct\n");
+ if (IS_ERR(nmk_chip))
return PTR_ERR(nmk_chip);
- }
supports_sleepmode =
device_property_read_bool(dev, "st,supports-sleepmode");
@@ -693,7 +695,7 @@ static int nmk_gpio_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, nmk_gpio_irq_handler, IRQF_SHARED,
dev_name(dev), nmk_chip);
if (ret) {
- dev_err(dev, "failed requesting IRQ\n");
+ dev_err_probe(dev, ret, "failed requesting IRQ\n");
return ret;
}
@@ -709,8 +711,6 @@ static int nmk_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, nmk_chip);
- dev_info(dev, "chip registered\n");
-
return 0;
}
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index e39723b5901b..005420baecf4 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -432,8 +432,7 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
- if (!bank->regs->leveldetect0 &&
- (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
+ if (!bank->regs->leveldetect0 && (type & IRQ_TYPE_LEVEL_MASK))
return -EINVAL;
raw_spin_lock_irqsave(&bank->lock, flags);
@@ -450,9 +449,9 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
}
raw_spin_unlock_irqrestore(&bank->lock, flags);
- if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
- else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ else if (type & IRQ_TYPE_EDGE_BOTH)
/*
* Edge IRQs are already cleared/acked in irq_handler and
* not need to be masked, as result handle_edge_irq()
@@ -702,7 +701,7 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
* after enabing the interrupt to clear the wakeup status.
*/
if (bank->regs->leveldetect0 && bank->regs->wkup_en &&
- trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+ trigger & IRQ_TYPE_LEVEL_MASK)
omap_clear_gpio_irqstatus(bank, offset);
if (trigger)
diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c
index 7a47a9aa0414..db027c10494f 100644
--- a/drivers/gpio/gpio-pca9570.c
+++ b/drivers/gpio/gpio-pca9570.c
@@ -133,7 +133,7 @@ static int pca9570_probe(struct i2c_client *client)
gpio->chip.get = pca9570_get;
gpio->chip.set = pca9570_set;
gpio->chip.base = -1;
- gpio->chip_data = device_get_match_data(&client->dev);
+ gpio->chip_data = i2c_get_match_data(client);
gpio->chip.ngpio = gpio->chip_data->ngpio;
gpio->chip.can_sleep = true;
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 4196916c4f94..c1f5e10a3c20 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -18,6 +18,8 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <asm/byteorder.h>
+
static const struct i2c_device_id pcf857x_id[] = {
{ .name = "pcf8574", .driver_data = 8 },
{ .name = "pcf8574a", .driver_data = 8 },
@@ -96,22 +98,23 @@ static int i2c_read_le8(struct i2c_client *client)
static int i2c_write_le16(struct i2c_client *client, unsigned int word)
{
- u8 buf[2] = { word & 0xff, word >> 8, };
+ __le16 buf = cpu_to_le16(word);
int status;
- status = i2c_master_send(client, buf, 2);
+ status = i2c_master_send(client, (char *)&buf, sizeof(buf));
return (status < 0) ? status : 0;
}
static int i2c_read_le16(struct i2c_client *client)
{
- u8 buf[2];
+ __le16 buf;
int status;
- status = i2c_master_recv(client, buf, 2);
+ status = i2c_master_recv(client, (char *)&buf, sizeof(buf));
if (status < 0)
return status;
- return (buf[1] << 8) | buf[0];
+
+ return le16_to_cpu(buf);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index 80c0ba0afa67..b99f4863443d 100644
--- a/drivers/gpio/gpio-pcie-idio-24.c
+++ b/drivers/gpio/gpio-pcie-idio-24.c
@@ -225,9 +225,9 @@ exit_unlock:
return ret;
}
-static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
- const unsigned int offset, unsigned int *const reg,
- unsigned int *const mask)
+static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, enum gpio_regmap_operation op,
+ const unsigned int base, const unsigned int offset,
+ unsigned int *const reg, unsigned int *const mask)
{
const unsigned int out_stride = offset / IDIO_24_NGPIO_PER_REG;
const unsigned int in_stride = (offset - 24) / IDIO_24_NGPIO_PER_REG;
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 919cf86fd590..fcd44e34f65e 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -132,8 +132,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
if (offset < 0 || offset >= PL061_GPIO_NR)
return -EINVAL;
- if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
- (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
+ if ((trigger & IRQ_TYPE_LEVEL_MASK) && (trigger & IRQ_TYPE_EDGE_BOTH))
{
dev_err(gc->parent,
"trying to configure line %d for both level and edge "
@@ -149,7 +148,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
gpiois = readb(pl061->base + GPIOIS);
gpioibe = readb(pl061->base + GPIOIBE);
- if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+ if (trigger & IRQ_TYPE_LEVEL_MASK) {
bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
/* Disable edge detection */
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 5d61053e0596..2b5da973f51f 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -406,7 +406,7 @@ static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type)
if (__gpio_is_occupied(pchip, gpio))
return 0;
- type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ type = IRQ_TYPE_EDGE_BOTH;
}
gpdr = readl_relaxed(c->regbase + GPDR_OFFSET);
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 86777e097fd8..09bebde5c426 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -59,7 +59,6 @@ struct gpio_rcar_priv {
#define MSKCLR 0x1c /* Interrupt Mask Clear Register */
#define POSNEG 0x20 /* Positive/Negative Logic Select Register */
#define EDGLEVEL 0x24 /* Edge/level Select Register */
-#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */
#define OUTDTSEL 0x40 /* Output Data Select Register */
#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
#define INEN 0x50 /* General Input Enable Register */
diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c
index 7bbc6f0ce4c8..f6c05fdbe63f 100644
--- a/drivers/gpio/gpio-rda.c
+++ b/drivers/gpio/gpio-rda.c
@@ -169,9 +169,9 @@ static int rda_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
if (ret)
return ret;
- if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ if (flow_type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(data, handle_level_irq);
- else if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ else if (flow_type & IRQ_TYPE_EDGE_BOTH)
irq_set_handler_locked(data, handle_edge_irq);
return 0;
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index 51b4d69b8740..fb21396e1d02 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -6,6 +6,7 @@
*/
#include <linux/bits.h>
+#include <linux/compiler_attributes.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -39,9 +40,19 @@ struct gpio_regmap {
struct regmap_irq_chip_data *irq_chip_data;
#endif
- int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
- unsigned int offset, unsigned int *reg,
- unsigned int *mask);
+ int (*reg_mask_xlate)(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask);
+
+ int (*value_xlate)(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset,
+ unsigned int reg, unsigned int *mask,
+ unsigned int *val);
+
+ int (*set_config)(struct gpio_regmap *gpio, struct gpio_chip *chip,
+ unsigned int offset, unsigned long config);
void *driver_data;
};
@@ -55,6 +66,7 @@ static unsigned int gpio_regmap_addr(unsigned int addr)
}
static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation __maybe_unused op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
@@ -79,7 +91,7 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
else
base = gpio_regmap_addr(gpio->reg_set_base);
- ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -102,7 +114,7 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
unsigned int reg, mask, mask_val;
int ret;
- ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -111,6 +123,13 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
else
mask_val = 0;
+ if (gpio->value_xlate) {
+ ret = gpio->value_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset,
+ reg, &mask, &mask_val);
+ if (ret)
+ return ret;
+ }
+
/* ignore input values which shadow the old output value */
if (gpio->reg_dat_base == gpio->reg_set_base)
ret = regmap_write_bits(gpio->regmap, reg, mask, mask_val);
@@ -124,7 +143,7 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
unsigned int offset, int val)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
- unsigned int base, reg, mask;
+ unsigned int base, reg, mask, value = 0;
int ret;
if (val)
@@ -132,10 +151,17 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
else
base = gpio_regmap_addr(gpio->reg_clr_base);
- ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, &reg, &mask);
if (ret)
return ret;
+ if (gpio->value_xlate) {
+ ret = gpio->value_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset,
+ reg, &mask, &value);
+ if (ret)
+ return ret;
+ }
+
return regmap_write(gpio->regmap, reg, mask);
}
@@ -182,7 +208,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
- ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -236,7 +262,7 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
- ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -245,6 +271,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
else
val = output ? mask : 0;
+ if (gpio->value_xlate) {
+ ret = gpio->value_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset,
+ reg, &mask, &val);
+ if (ret)
+ return ret;
+ }
+
return regmap_update_bits(gpio->regmap, reg, mask, val);
}
@@ -276,12 +309,55 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
return gpio_regmap_set_direction(chip, offset, true);
}
+static int gpio_regmap_set_config(struct gpio_chip *chip,
+ unsigned int offset,
+ unsigned long cfg)
+{
+ struct gpio_regmap *gpio = gpiochip_get_data(chip);
+
+ return gpio->set_config(gpio, chip, offset, cfg);
+}
+
+int gpio_regmap_reqres_irq(struct gpio_regmap *gpio, unsigned int offset)
+{
+ return gpiochip_reqres_irq(&gpio->gpio_chip, offset);
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_reqres_irq);
+
+void gpio_regmap_relres_irq(struct gpio_regmap *gpio, unsigned int offset)
+{
+ gpiochip_relres_irq(&gpio->gpio_chip, offset);
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_relres_irq);
+
+static int __maybe_unused gpio_regmap_irq_reqres(void *irq_drv_data, irq_hw_number_t hwirq)
+{
+ return gpio_regmap_reqres_irq(irq_drv_data, hwirq);
+}
+
+static void __maybe_unused gpio_regmap_irq_relres(void *irq_drv_data, irq_hw_number_t hwirq)
+{
+ gpio_regmap_relres_irq(irq_drv_data, hwirq);
+}
+
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
{
return gpio->driver_data;
}
EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
+void gpio_regmap_enable_irq(struct gpio_regmap *gpio, irq_hw_number_t hwirq)
+{
+ gpiochip_enable_irq(&gpio->gpio_chip, hwirq);
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_enable_irq);
+
+void gpio_regmap_disable_irq(struct gpio_regmap *gpio, irq_hw_number_t hwirq)
+{
+ gpiochip_disable_irq(&gpio->gpio_chip, hwirq);
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_disable_irq);
+
/**
* gpio_regmap_register() - Register a generic regmap GPIO controller
* @config: configuration for gpio_regmap
@@ -390,6 +466,13 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
if (!gpio->reg_mask_xlate)
gpio->reg_mask_xlate = gpio_regmap_simple_xlate;
+ gpio->value_xlate = config->value_xlate;
+
+ if (config->set_config) {
+ gpio->set_config = config->set_config;
+ chip->set_config = gpio_regmap_set_config;
+ }
+
ret = gpiochip_add_data(chip, gpio);
if (ret < 0)
goto err_free_bitmap_output;
@@ -397,6 +480,9 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
#ifdef CONFIG_REGMAP_IRQ
if (config->regmap_irq_chip) {
gpio->regmap_irq_line = config->regmap_irq_line;
+ config->regmap_irq_chip->irq_reqres = gpio_regmap_irq_reqres;
+ config->regmap_irq_chip->irq_relres = gpio_regmap_irq_relres;
+ config->regmap_irq_chip->irq_drv_data = gpio;
ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,
config->regmap_irq_line, config->regmap_irq_flags,
0, config->regmap_irq_chip, &gpio->irq_chip_data);
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 9478a58f1caa..77b239a9a601 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -647,21 +647,17 @@ static void rockchip_clk_put(void *data)
static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
{
- struct resource res;
+ struct platform_device *pdev = to_platform_device(bank->dev);
int id = 0, ret;
- if (of_address_to_resource(bank->of_node, 0, &res)) {
- dev_err(bank->dev, "cannot find IO resource for bank\n");
- return -ENOENT;
- }
-
- bank->reg_base = devm_ioremap_resource(bank->dev, &res);
+ bank->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bank->reg_base))
return PTR_ERR(bank->reg_base);
- bank->irq = irq_of_parse_and_map(bank->of_node, 0);
- if (!bank->irq)
- return -EINVAL;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ bank->irq = ret;
bank->clk = devm_clk_get_enabled(bank->dev, NULL);
if (IS_ERR(bank->clk))
diff --git a/drivers/gpio/gpio-rtd1625.c b/drivers/gpio/gpio-rtd1625.c
new file mode 100644
index 000000000000..483e44cf5abc
--- /dev/null
+++ b/drivers/gpio/gpio-rtd1625.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Realtek DHC RTD1625 gpio driver
+ *
+ * Copyright (c) 2023-2026 Realtek Semiconductor Corp.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define RTD1625_GPIO_DIR BIT(0)
+#define RTD1625_GPIO_OUT BIT(2)
+#define RTD1625_GPIO_IN BIT(4)
+#define RTD1625_GPIO_EDGE_INT_DP BIT(6)
+#define RTD1625_GPIO_EDGE_INT_EN BIT(8)
+#define RTD1625_GPIO_LEVEL_INT_EN BIT(16)
+#define RTD1625_GPIO_LEVEL_INT_DP BIT(18)
+#define RTD1625_GPIO_DEBOUNCE GENMASK(30, 28)
+#define RTD1625_GPIO_DEBOUNCE_WREN BIT(31)
+
+#define RTD1625_GPIO_WREN(x) ((x) << 1)
+
+/* Write-enable masks for all GPIO configs and reserved hardware bits */
+#define RTD1625_ISO_GPIO_WREN_ALL 0x8000aa8a
+#define RTD1625_ISOM_GPIO_WREN_ALL 0x800aaa8a
+
+#define RTD1625_GPIO_DEBOUNCE_1US 0
+#define RTD1625_GPIO_DEBOUNCE_10US 1
+#define RTD1625_GPIO_DEBOUNCE_100US 2
+#define RTD1625_GPIO_DEBOUNCE_1MS 3
+#define RTD1625_GPIO_DEBOUNCE_10MS 4
+#define RTD1625_GPIO_DEBOUNCE_20MS 5
+#define RTD1625_GPIO_DEBOUNCE_30MS 6
+#define RTD1625_GPIO_DEBOUNCE_50MS 7
+
+#define GPIO_CONTROL(gpio) ((gpio) * 4)
+
+static struct lock_class_key rtd1625_gpio_irq_lock_class;
+static struct lock_class_key rtd1625_gpio_irq_request_class;
+
+enum rtd1625_irq_index {
+ RTD1625_IRQ_ASSERT,
+ RTD1625_IRQ_DEASSERT,
+ RTD1625_IRQ_LEVEL,
+ RTD1625_MAX_IRQS
+};
+
+/**
+ * struct rtd1625_gpio_info - Specific GPIO register information
+ * @num_gpios: The number of GPIOs
+ * @irq_type_support: Supported IRQ types
+ * @base_offset: Offset for GPIO controller register
+ * @gpa_offset: Offset for GPIO assert interrupt status register
+ * @gpda_offset: Offset for GPIO deassert interrupt status register
+ * @level_offset: Offset of level interrupt status register
+ * @write_en_all: Write-enable mask for all configurable bits
+ */
+struct rtd1625_gpio_info {
+ unsigned int num_gpios;
+ unsigned int irq_type_support;
+ unsigned int base_offset;
+ unsigned int gpa_offset;
+ unsigned int gpda_offset;
+ unsigned int level_offset;
+ unsigned int write_en_all;
+};
+
+struct rtd1625_gpio {
+ struct gpio_regmap *gpio_reg;
+ const struct rtd1625_gpio_info *info;
+ struct regmap *regmap;
+ unsigned int irqs[RTD1625_MAX_IRQS];
+ raw_spinlock_t lock;
+ struct irq_domain *domain;
+ unsigned int *save_regs;
+};
+
+static unsigned int rtd1625_gpio_gpa_offset(struct rtd1625_gpio *data, unsigned int offset)
+{
+ return data->info->gpa_offset + ((offset / 32) * 4);
+}
+
+static unsigned int rtd1625_gpio_gpda_offset(struct rtd1625_gpio *data, unsigned int offset)
+{
+ return data->info->gpda_offset + ((offset / 32) * 4);
+}
+
+static unsigned int rtd1625_gpio_level_offset(struct rtd1625_gpio *data, unsigned int offset)
+{
+ return data->info->level_offset + ((offset / 32) * 4);
+}
+
+static int rtd1625_reg_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset, unsigned int *reg,
+ unsigned int *mask)
+{
+ /* Each GPIO has its own dedicated 32-bit register */
+ struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio);
+ int val = 0, ret = 0;
+ *reg = base + offset * 4;
+
+ switch (op) {
+ case GPIO_REGMAP_SET_OP:
+ *mask = RTD1625_GPIO_OUT;
+ return 0;
+
+ case GPIO_REGMAP_GET_OP:
+ ret = regmap_read(data->regmap, *reg, &val);
+ if (ret)
+ return ret;
+
+ if (val & RTD1625_GPIO_DIR)
+ *mask = RTD1625_GPIO_OUT;
+ else
+ *mask = RTD1625_GPIO_IN;
+ return 0;
+
+ case GPIO_REGMAP_GET_DIR_OP:
+ case GPIO_REGMAP_SET_DIR_OP:
+ *mask = RTD1625_GPIO_DIR;
+ return 0;
+
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int rtd1625_value_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset,
+ unsigned int reg, unsigned int *mask,
+ unsigned int *val)
+{
+ switch (op) {
+ case GPIO_REGMAP_SET_OP:
+ *val |= RTD1625_GPIO_WREN(RTD1625_GPIO_OUT);
+ *mask |= RTD1625_GPIO_WREN(RTD1625_GPIO_OUT);
+ return 0;
+
+ case GPIO_REGMAP_SET_DIR_OP:
+ *val |= RTD1625_GPIO_WREN(RTD1625_GPIO_DIR);
+ *mask |= RTD1625_GPIO_WREN(RTD1625_GPIO_DIR);
+ return 0;
+
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int rtd1625_gpio_set_debounce(struct rtd1625_gpio *data, unsigned int offset,
+ unsigned int debounce)
+{
+ u8 deb_val;
+ u32 val;
+
+ switch (debounce) {
+ case 1:
+ deb_val = RTD1625_GPIO_DEBOUNCE_1US;
+ break;
+ case 10:
+ deb_val = RTD1625_GPIO_DEBOUNCE_10US;
+ break;
+ case 100:
+ deb_val = RTD1625_GPIO_DEBOUNCE_100US;
+ break;
+ case 1000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_1MS;
+ break;
+ case 10000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_10MS;
+ break;
+ case 20000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_20MS;
+ break;
+ case 30000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_30MS;
+ break;
+ case 50000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_50MS;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ val = FIELD_PREP(RTD1625_GPIO_DEBOUNCE, deb_val) | RTD1625_GPIO_DEBOUNCE_WREN;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ return regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(offset), val);
+}
+
+static int rtd1625_gpio_set_config(struct gpio_regmap *gpio, struct gpio_chip *chip,
+ unsigned int offset, unsigned long config)
+{
+ struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio);
+ u32 debounce;
+
+ if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) {
+ debounce = pinconf_to_config_argument(config);
+ return rtd1625_gpio_set_debounce(data, offset, debounce);
+ }
+
+ return gpiochip_generic_config(chip, offset, config);
+}
+
+static void rtd1625_gpio_irq_handle(struct irq_desc *desc)
+{
+ unsigned int (*get_reg_offset)(struct rtd1625_gpio *gpio, unsigned int offset);
+ struct rtd1625_gpio *data = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int irq = irq_desc_get_irq(desc);
+ struct irq_domain *domain = data->domain;
+ unsigned int reg_offset, i, j, val;
+ irq_hw_number_t hwirq;
+ unsigned long status;
+ u32 irq_type;
+ int ret;
+
+ if (irq == data->irqs[RTD1625_IRQ_ASSERT])
+ get_reg_offset = &rtd1625_gpio_gpa_offset;
+ else if (irq == data->irqs[RTD1625_IRQ_DEASSERT])
+ get_reg_offset = &rtd1625_gpio_gpda_offset;
+ else if (irq == data->irqs[RTD1625_IRQ_LEVEL])
+ get_reg_offset = &rtd1625_gpio_level_offset;
+ else
+ return;
+
+ chained_irq_enter(chip, desc);
+
+ for (i = 0; i < data->info->num_gpios; i += 32) {
+ reg_offset = get_reg_offset(data, i);
+ ret = regmap_read(data->regmap, reg_offset, &val);
+ if (ret) {
+ pr_err_ratelimited("Failed to read IRQ status for GPIO %u: %d\n", i, ret);
+ continue;
+ }
+
+ status = val;
+
+ /*
+ * Hardware quirk: The controller fires both "assert" and "de-assert"
+ * interrupts simultaneously on any edge toggle.
+ * We must pre-clear edge interrupts here. If we drop an unwanted
+ * de-assert interrupt below, it will never reach the IRQ core
+ * (generic_handle_domain_irq), meaning ->irq_ack() won't be called.
+ * Failing to clear it here leads to an interrupt storm.
+ */
+ if (irq != data->irqs[RTD1625_IRQ_LEVEL]) {
+ ret = regmap_write(data->regmap, reg_offset, status);
+ if (ret)
+ pr_err_ratelimited("Failed to clear edge IRQ for GPIO %u: %d\n",
+ i, ret);
+ }
+
+ for_each_set_bit(j, &status, 32) {
+ hwirq = i + j;
+ irq_type = irq_get_trigger_type(irq_find_mapping(domain, hwirq));
+
+ /*
+ * Filter out the hardware-forced de-assert interrupt unless
+ * the user explicitly requested IRQ_TYPE_EDGE_BOTH.
+ */
+ if (irq == data->irqs[RTD1625_IRQ_DEASSERT] &&
+ irq_type != IRQ_TYPE_EDGE_BOTH)
+ continue;
+
+ generic_handle_domain_irq(domain, hwirq);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void rtd1625_gpio_ack_irq(struct irq_data *d)
+{
+ struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 irq_type = irqd_get_trigger_type(d);
+ u32 bit_mask = BIT(hwirq % 32);
+ int reg_offset;
+
+ if (irq_type & IRQ_TYPE_LEVEL_MASK) {
+ reg_offset = rtd1625_gpio_level_offset(data, hwirq);
+ regmap_write(data->regmap, reg_offset, bit_mask);
+ }
+}
+
+static void rtd1625_gpio_enable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ int gpda_reg_offset = rtd1625_gpio_gpda_offset(data, hwirq);
+ int gpa_reg_offset = rtd1625_gpio_gpa_offset(data, hwirq);
+ u32 clr_mask = BIT(hwirq % 32);
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ regmap_write(data->regmap, gpa_reg_offset, clr_mask);
+ regmap_write(data->regmap, gpda_reg_offset, clr_mask);
+ val = RTD1625_GPIO_EDGE_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN);
+ regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
+}
+
+static void rtd1625_gpio_disable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN);
+ regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
+}
+
+static void rtd1625_gpio_enable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ int level_reg_offset = rtd1625_gpio_level_offset(data, hwirq);
+ u32 clr_mask = BIT(hwirq % 32);
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ regmap_write(data->regmap, level_reg_offset, clr_mask);
+ val = RTD1625_GPIO_LEVEL_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN);
+ regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
+}
+
+static void rtd1625_gpio_disable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN);
+ regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
+}
+
+static void rtd1625_gpio_enable_irq(struct irq_data *d)
+{
+ struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 irq_type = irqd_get_trigger_type(d);
+
+ gpio_regmap_enable_irq(data->gpio_reg, hwirq);
+
+ if (irq_type & IRQ_TYPE_EDGE_BOTH)
+ rtd1625_gpio_enable_edge_irq(data, hwirq);
+ else if (irq_type & IRQ_TYPE_LEVEL_MASK)
+ rtd1625_gpio_enable_level_irq(data, hwirq);
+}
+
+static void rtd1625_gpio_disable_irq(struct irq_data *d)
+{
+ struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 irq_type = irqd_get_trigger_type(d);
+
+ if (irq_type & IRQ_TYPE_EDGE_BOTH)
+ rtd1625_gpio_disable_edge_irq(data, hwirq);
+ else if (irq_type & IRQ_TYPE_LEVEL_MASK)
+ rtd1625_gpio_disable_level_irq(data, hwirq);
+
+ gpio_regmap_disable_irq(data->gpio_reg, hwirq);
+}
+
+static int rtd1625_gpio_irq_set_level_type(struct irq_data *d, bool level)
+{
+ u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_DP);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ struct rtd1625_gpio *data;
+ int ret;
+
+ data = irq_data_get_irq_chip_data(d);
+ if (!(data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK))
+ return -EINVAL;
+
+ if (level)
+ val |= RTD1625_GPIO_LEVEL_INT_DP;
+
+ scoped_guard(raw_spinlock_irqsave, &data->lock) {
+ ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq),
+ val);
+ if (ret)
+ return ret;
+ }
+
+ irq_set_handler_locked(d, handle_level_irq);
+
+ return 0;
+}
+
+static int rtd1625_gpio_irq_set_edge_type(struct irq_data *d, bool polarity)
+{
+ struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
+ u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_DP);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ int ret;
+
+ if (!(data->info->irq_type_support & IRQ_TYPE_EDGE_BOTH))
+ return -EINVAL;
+
+ if (polarity)
+ val |= RTD1625_GPIO_EDGE_INT_DP;
+
+ scoped_guard(raw_spinlock_irqsave, &data->lock) {
+ ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq),
+ val);
+ if (ret)
+ return ret;
+ }
+
+ irq_set_handler_locked(d, handle_edge_irq);
+
+ return 0;
+}
+
+static int rtd1625_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ return rtd1625_gpio_irq_set_edge_type(d, 1);
+
+ case IRQ_TYPE_EDGE_FALLING:
+ return rtd1625_gpio_irq_set_edge_type(d, 0);
+
+ case IRQ_TYPE_EDGE_BOTH:
+ return rtd1625_gpio_irq_set_edge_type(d, 1);
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ return rtd1625_gpio_irq_set_level_type(d, 0);
+
+ case IRQ_TYPE_LEVEL_LOW:
+ return rtd1625_gpio_irq_set_level_type(d, 1);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rtd1625_gpio_irq_request_resources(struct irq_data *d)
+{
+ struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
+
+ return gpio_regmap_reqres_irq(data->gpio_reg, d->hwirq);
+}
+
+static void rtd1625_gpio_irq_release_resources(struct irq_data *d)
+{
+ struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
+
+ gpio_regmap_relres_irq(data->gpio_reg, d->hwirq);
+}
+
+static struct irq_chip rtd1625_iso_gpio_irq_chip = {
+ .name = "rtd1625-gpio",
+ .irq_ack = rtd1625_gpio_ack_irq,
+ .irq_mask = rtd1625_gpio_disable_irq,
+ .irq_unmask = rtd1625_gpio_enable_irq,
+ .irq_set_type = rtd1625_gpio_irq_set_type,
+ .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
+ .irq_request_resources = rtd1625_gpio_irq_request_resources,
+ .irq_release_resources = rtd1625_gpio_irq_release_resources,
+};
+
+static int rtd1625_gpio_setup_irq(struct platform_device *pdev, struct rtd1625_gpio *data)
+{
+ unsigned int num_irqs;
+ int irq;
+
+ /* IRQ is optional; operate as basic GPIO if absent */
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq == -ENXIO)
+ return 0;
+ if (irq < 0)
+ return irq;
+
+ num_irqs = (data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK) ? 3 : 2;
+
+ for (unsigned int i = 0; i < num_irqs; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ data->irqs[i] = irq;
+ irq_set_chained_handler_and_data(data->irqs[i], rtd1625_gpio_irq_handle, data);
+ }
+
+ return 0;
+}
+
+static int rtd1625_gpio_irq_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct rtd1625_gpio *data = domain->host_data;
+
+ irq_set_chip_data(irq, data);
+
+ irq_set_chip_and_handler(irq, &rtd1625_iso_gpio_irq_chip, handle_bad_irq);
+
+ irq_set_noprobe(irq);
+
+ irq_set_lockdep_class(irq, &rtd1625_gpio_irq_lock_class,
+ &rtd1625_gpio_irq_request_class);
+
+ return 0;
+}
+
+static const struct irq_domain_ops rtd1625_gpio_irq_domain_ops = {
+ .map = rtd1625_gpio_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static const struct regmap_config rtd1625_gpio_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+#ifndef CONFIG_DEBUG_GPIO
+ .disable_locking = true,
+#endif
+};
+
+static int rtd1625_gpio_probe(struct platform_device *pdev)
+{
+ struct gpio_regmap_config config = {};
+ struct irq_domain_info d_info = {};
+ struct device *dev = &pdev->dev;
+ struct gpio_regmap *gpio_reg;
+ struct rtd1625_gpio *data;
+ void __iomem *irq_base;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->info = device_get_match_data(dev);
+ if (!data->info)
+ return -ENODATA;
+
+ irq_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(irq_base))
+ return PTR_ERR(irq_base);
+
+ data->regmap = devm_regmap_init_mmio(dev, irq_base, &rtd1625_gpio_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ raw_spin_lock_init(&data->lock);
+ platform_set_drvdata(pdev, data);
+
+ data->save_regs = devm_kcalloc(dev, data->info->num_gpios, sizeof(*data->save_regs),
+ GFP_KERNEL);
+ if (!data->save_regs)
+ return -ENOMEM;
+
+ config.parent = dev;
+ config.regmap = data->regmap;
+ config.ngpio = data->info->num_gpios;
+ config.reg_dat_base = data->info->base_offset;
+ config.reg_set_base = data->info->base_offset;
+ config.reg_dir_out_base = data->info->base_offset;
+
+ config.reg_mask_xlate = rtd1625_reg_mask_xlate;
+ config.set_config = rtd1625_gpio_set_config;
+ config.value_xlate = rtd1625_value_xlate;
+
+ d_info.fwnode = dev_fwnode(&pdev->dev);
+ d_info.size = data->info->num_gpios;
+ d_info.hwirq_max = data->info->num_gpios;
+ d_info.ops = &rtd1625_gpio_irq_domain_ops;
+ d_info.host_data = data;
+
+ data->domain = devm_irq_domain_instantiate(dev, &d_info);
+ if (IS_ERR(data->domain))
+ return PTR_ERR(data->domain);
+
+ ret = rtd1625_gpio_setup_irq(pdev, data);
+ if (ret)
+ return ret;
+
+ config.irq_domain = data->domain;
+ config.drvdata = data;
+
+ gpio_reg = devm_gpio_regmap_register(dev, &config);
+ if (IS_ERR(gpio_reg))
+ return PTR_ERR(gpio_reg);
+
+ data->gpio_reg = gpio_reg;
+
+ return 0;
+}
+
+static const struct rtd1625_gpio_info rtd1625_iso_gpio_info = {
+ .num_gpios = 166,
+ .irq_type_support = IRQ_TYPE_EDGE_BOTH,
+ .base_offset = 0x100,
+ .gpa_offset = 0x000,
+ .gpda_offset = 0x020,
+ .write_en_all = RTD1625_ISO_GPIO_WREN_ALL,
+};
+
+static const struct rtd1625_gpio_info rtd1625_isom_gpio_info = {
+ .num_gpios = 4,
+ .irq_type_support = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_LOW |
+ IRQ_TYPE_LEVEL_HIGH,
+ .base_offset = 0x20,
+ .gpa_offset = 0x00,
+ .gpda_offset = 0x04,
+ .level_offset = 0x18,
+ .write_en_all = RTD1625_ISOM_GPIO_WREN_ALL,
+};
+
+static int rtd1625_gpio_suspend(struct device *dev)
+{
+ struct rtd1625_gpio *data = dev_get_drvdata(dev);
+ const struct rtd1625_gpio_info *info = data->info;
+ int ret;
+
+ for (unsigned int i = 0; i < info->num_gpios; i++) {
+ ret = regmap_read(data->regmap, data->info->base_offset + GPIO_CONTROL(i),
+ &data->save_regs[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtd1625_gpio_resume(struct device *dev)
+{
+ struct rtd1625_gpio *data = dev_get_drvdata(dev);
+ const struct rtd1625_gpio_info *info = data->info;
+ int ret;
+
+ for (unsigned int i = 0; i < info->num_gpios; i++) {
+ ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(i),
+ data->save_regs[i] | info->write_en_all);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static DEFINE_NOIRQ_DEV_PM_OPS(rtd1625_gpio_pm_ops, rtd1625_gpio_suspend, rtd1625_gpio_resume);
+
+static const struct of_device_id rtd1625_gpio_of_matches[] = {
+ { .compatible = "realtek,rtd1625-iso-gpio", .data = &rtd1625_iso_gpio_info },
+ { .compatible = "realtek,rtd1625-isom-gpio", .data = &rtd1625_isom_gpio_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rtd1625_gpio_of_matches);
+
+static struct platform_driver rtd1625_gpio_platform_driver = {
+ .driver = {
+ .name = "gpio-rtd1625",
+ .of_match_table = rtd1625_gpio_of_matches,
+ .pm = pm_sleep_ptr(&rtd1625_gpio_pm_ops),
+ },
+ .probe = rtd1625_gpio_probe,
+};
+module_platform_driver(rtd1625_gpio_platform_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Semiconductor Corporation");
+MODULE_DESCRIPTION("Realtek DHC SoC RTD1625 gpio driver");
diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c
index ffa73dd3b982..e4f3bf949b87 100644
--- a/drivers/gpio/gpio-sa1100.c
+++ b/drivers/gpio/gpio-sa1100.c
@@ -139,7 +139,7 @@ static int sa1100_gpio_type(struct irq_data *d, unsigned int type)
if (type == IRQ_TYPE_PROBE) {
if ((sgc->irqrising | sgc->irqfalling) & mask)
return 0;
- type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ type = IRQ_TYPE_EDGE_BOTH;
}
if (type & IRQ_TYPE_EDGE_RISING)
diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
index 94ef2efbd14f..e9a992cd4b9c 100644
--- a/drivers/gpio/gpio-sifive.c
+++ b/drivers/gpio/gpio-sifive.c
@@ -260,6 +260,7 @@ static const struct of_device_id sifive_gpio_match[] = {
{ .compatible = "sifive,fu540-c000-gpio" },
{ },
};
+MODULE_DEVICE_TABLE(of, sifive_gpio_match);
static struct platform_driver sifive_gpio_driver = {
.probe = sifive_gpio_probe,
diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c
index 2f9a1f0384ad..044d81e7cafb 100644
--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c
+++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c
@@ -244,6 +244,7 @@ static int gpio_la_poll_probe(struct platform_device *pdev)
if (ret)
return ret;
+ /* Initially allocate a buffer. It currently is NULL */
fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE);
ret = devm_add_action_or_reset(dev, fops_buf_release, priv);
if (ret)
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c
index 37c133837729..e2a784df275c 100644
--- a/drivers/gpio/gpio-sodaville.c
+++ b/drivers/gpio/gpio-sodaville.c
@@ -158,7 +158,7 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
sd->gc->private = sd;
ct = sd->gc->chip_types;
- ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
+ ct->type = IRQ_TYPE_LEVEL_MASK;
ct->regs.eoi = GPSTR;
ct->regs.mask = GPIO_INT;
ct->chip.irq_mask = irq_gc_mask_clr_bit;
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index 6faf30347a36..ccdf13a741b7 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -149,7 +149,7 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
int regoffset = offset / 8;
int mask = BIT(offset % 8);
- if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH)
+ if (type & IRQ_TYPE_LEVEL_MASK)
return -EINVAL;
/* STMPE801 and STMPE 1600 don't have RE and FE registers */
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
index 705bfd80a8d0..7fb8e6223bd1 100644
--- a/drivers/gpio/gpio-tb10x.c
+++ b/drivers/gpio/gpio-tb10x.c
@@ -51,7 +51,7 @@ static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
return ioread32(gpio->base + offs);
}
-static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
{
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
@@ -167,9 +167,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev),
tb10x_gpio->chip.gc.ngpio,
&irq_generic_chip_ops, NULL);
- if (!tb10x_gpio->domain) {
+ if (!tb10x_gpio->domain)
return -ENOMEM;
- }
ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
tb10x_gpio->chip.gc.ngpio, 1, tb10x_gpio->chip.gc.label,
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index fa6c8ee92093..ccda3a56d599 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -342,9 +342,9 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return ret;
}
- if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
- else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ else if (type & IRQ_TYPE_EDGE_BOTH)
irq_set_handler_locked(d, handle_edge_irq);
if (d->parent_data)
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
index ec378a4220a7..727fa1b5ad3b 100644
--- a/drivers/gpio/gpio-timberdale.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -148,7 +148,7 @@ static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
if (ver > 2)
bflr = ioread32(tgpio->membase + TGPIO_BFLR);
- if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+ if (trigger & IRQ_TYPE_LEVEL_MASK) {
bflr &= ~(1 << offset);
flr &= ~(1 << offset);
if (trigger & IRQ_TYPE_LEVEL_HIGH)
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index aa8586d8a787..adfa2c2d5873 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -83,6 +83,7 @@ static const struct of_device_id vf610_gpio_dt_ids[] = {
{ .compatible = "fsl,imx8ulp-gpio", .data = &imx8ulp_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, vf610_gpio_dt_ids);
static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
{
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 82f27db0b230..9f3b628d5793 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2682,15 +2682,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
file->private_data = cdev;
cdev->fp = file;
- ret = nonseekable_open(inode, file);
- if (ret)
- goto out_unregister_device_notifier;
-
- return ret;
+ return nonseekable_open(inode, file);
-out_unregister_device_notifier:
- blocking_notifier_chain_unregister(&gdev->device_notifier,
- &cdev->device_unregistered_nb);
out_unregister_line_notifier:
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
raw_notifier_chain_unregister(&gdev->line_state_notifier,
diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c
index 380b68f879e5..e6cb43a8df5b 100644
--- a/drivers/gpio/gpiolib-kunit.c
+++ b/drivers/gpio/gpiolib-kunit.c
@@ -3,6 +3,8 @@
* Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
*/
+#include <linux/cleanup.h>
+#include <linux/err.h>
#include <linux/fwnode.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
@@ -11,26 +13,78 @@
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/types.h>
+#include <kunit/fwnode.h>
#include <kunit/platform_device.h>
#include <kunit/test.h>
#define GPIO_TEST_PROVIDER "gpio-test-provider"
#define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer"
+#define GPIO_PROBE_ORDER_TEST_CONSUMER "gpio-probe-order-test-consumer"
+#define GPIO_PROBE_DEFER_TEST_CONSUMER "gpio-probe-defer-test-consumer"
#define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer"
+#define GPIO_CONSUMER_NAME "gpio-swnode-consumer-test-device"
+
+#define GPIO_TEST_PROVIDER_NGPIO 4
+
+/*
+ * The test provider tracks per-line direction and value so that lines can be
+ * driven as both inputs and outputs - this is needed to exercise input as well
+ * as output GPIO hogs.
+ */
+struct gpio_test_provider_data {
+ DECLARE_BITMAP(is_output, GPIO_TEST_PROVIDER_NGPIO);
+ DECLARE_BITMAP(values, GPIO_TEST_PROVIDER_NGPIO);
+};
static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset)
{
- return GPIO_LINE_DIRECTION_OUT;
+ struct gpio_test_provider_data *data = gpiochip_get_data(gc);
+
+ return test_bit(offset, data->is_output) ?
+ GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int gpio_test_provider_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_test_provider_data *data = gpiochip_get_data(gc);
+
+ clear_bit(offset, data->is_output);
+
+ return 0;
+}
+
+static int gpio_test_provider_direction_output(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct gpio_test_provider_data *data = gpiochip_get_data(gc);
+
+ set_bit(offset, data->is_output);
+ __assign_bit(offset, data->values, value);
+
+ return 0;
+}
+
+static int gpio_test_provider_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_test_provider_data *data = gpiochip_get_data(gc);
+
+ return test_bit(offset, data->values);
}
static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value)
{
+ struct gpio_test_provider_data *data = gpiochip_get_data(gc);
+
+ __assign_bit(offset, data->values, value);
+
return 0;
}
static int gpio_test_provider_probe(struct platform_device *pdev)
{
+ struct gpio_test_provider_data *data;
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
@@ -38,16 +92,26 @@ static int gpio_test_provider_probe(struct platform_device *pdev)
if (!gc)
return -ENOMEM;
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* Lines start as outputs to preserve the default for lookup tests. */
+ bitmap_fill(data->is_output, GPIO_TEST_PROVIDER_NGPIO);
+
gc->base = -1;
- gc->ngpio = 4;
- gc->label = "gpio-swnode-consumer-test-device";
+ gc->ngpio = GPIO_TEST_PROVIDER_NGPIO;
+ gc->label = GPIO_CONSUMER_NAME;
gc->parent = dev;
gc->owner = THIS_MODULE;
gc->get_direction = gpio_test_provider_get_direction;
+ gc->direction_input = gpio_test_provider_direction_input;
+ gc->direction_output = gpio_test_provider_direction_output;
+ gc->get = gpio_test_provider_get;
gc->set = gpio_test_provider_set;
- return devm_gpiochip_add_data(dev, gc, NULL);
+ return devm_gpiochip_add_data(dev, gc, data);
}
static struct platform_driver gpio_test_provider_driver = {
@@ -63,10 +127,12 @@ static const struct software_node gpio_test_provider_swnode = {
struct gpio_swnode_consumer_pdata {
bool gpio_ok;
+ int errno;
};
static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = {
.gpio_ok = false,
+ .errno = 0,
};
static int gpio_swnode_consumer_probe(struct platform_device *pdev)
@@ -76,8 +142,10 @@ static int gpio_swnode_consumer_probe(struct platform_device *pdev)
struct gpio_desc *desc;
desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH);
- if (IS_ERR(desc))
+ if (IS_ERR(desc)) {
+ pdata->errno = PTR_ERR(desc);
return PTR_ERR(desc);
+ }
pdata->gpio_ok = true;
@@ -91,13 +159,8 @@ static struct platform_driver gpio_swnode_consumer_driver = {
},
};
-static void gpio_swnode_lookup_by_primary(struct kunit *test)
+static int gpio_swnode_register_drivers(struct kunit *test)
{
- struct gpio_swnode_consumer_pdata *pdata;
- struct platform_device_info pdevinfo;
- struct property_entry properties[2];
- struct platform_device *pdev;
- bool bound = false;
int ret;
ret = kunit_platform_driver_register(test, &gpio_test_provider_driver);
@@ -106,6 +169,17 @@ static void gpio_swnode_lookup_by_primary(struct kunit *test)
ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
+ return 0;
+}
+
+static void gpio_swnode_lookup_by_primary(struct kunit *test)
+{
+ struct gpio_swnode_consumer_pdata *pdata;
+ struct platform_device_info pdevinfo;
+ struct property_entry properties[2];
+ struct platform_device *pdev;
+ bool bound = false;
+
pdevinfo = (struct platform_device_info){
.name = GPIO_TEST_PROVIDER,
.id = PLATFORM_DEVID_NONE,
@@ -149,7 +223,6 @@ static void gpio_swnode_lookup_by_secondary(struct kunit *test)
struct fwnode_handle *primary;
struct platform_device *pdev;
bool bound = false;
- int ret;
/*
* Can't live on the stack as it will still get referenced in cleanup
@@ -158,12 +231,6 @@ static void gpio_swnode_lookup_by_secondary(struct kunit *test)
primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary);
- ret = kunit_platform_driver_register(test, &gpio_test_provider_driver);
- KUNIT_ASSERT_EQ(test, ret, 0);
-
- ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver);
- KUNIT_ASSERT_EQ(test, ret, 0);
-
fwnode_init(primary, NULL);
pdevinfo = (struct platform_device_info){
@@ -211,6 +278,266 @@ static struct kunit_case gpio_swnode_lookup_tests[] = {
static struct kunit_suite gpio_swnode_lookup_test_suite = {
.name = "gpio-swnode-lookup",
.test_cases = gpio_swnode_lookup_tests,
+ .init = gpio_swnode_register_drivers,
+};
+
+static void gpio_swnode_unregister_swnode(void *data)
+{
+ software_node_unregister(data);
+}
+
+struct gpio_probe_order_pdata {
+ unsigned int probe_count;
+ bool gpio_ok;
+};
+
+static const struct gpio_probe_order_pdata gpio_probe_order_pdata_template = {
+ .probe_count = 0,
+ .gpio_ok = false,
+};
+
+static int gpio_probe_order_consumer_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_probe_order_pdata *pdata = dev_get_platdata(dev);
+ struct gpio_desc *desc;
+
+ pdata->probe_count++;
+
+ desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ pdata->gpio_ok = true;
+
+ return 0;
+}
+
+static struct platform_driver gpio_probe_order_consumer_driver = {
+ .probe = gpio_probe_order_consumer_probe,
+ .driver = {
+ .name = GPIO_PROBE_ORDER_TEST_CONSUMER,
+ },
+};
+
+/*
+ * Verify that fw_devlink orders the probe of a GPIO consumer after its
+ * provider. The consumer references the provider through a software node and
+ * is registered first. fw_devlink must defer it before its driver's probe()
+ * is ever entered, so the consumer probes exactly once - only after the
+ * provider is added and bound.
+ */
+static void gpio_swnode_probe_order(struct kunit *test)
+{
+ struct property_entry properties[2] = { };
+ struct gpio_probe_order_pdata *pdata;
+ struct platform_device_info pdevinfo;
+ struct platform_device *prvd, *cons;
+ bool bound = false;
+ int ret;
+
+ ret = kunit_platform_driver_register(test, &gpio_test_provider_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_platform_driver_register(test, &gpio_probe_order_consumer_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = software_node_register(&gpio_test_provider_swnode);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_add_action_or_reset(test, gpio_swnode_unregister_swnode,
+ (void *)&gpio_test_provider_swnode);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
+ &gpio_test_provider_swnode,
+ 0, GPIO_ACTIVE_HIGH);
+
+ pdevinfo = (struct platform_device_info){
+ .name = GPIO_PROBE_ORDER_TEST_CONSUMER,
+ .id = PLATFORM_DEVID_NONE,
+ .data = &gpio_probe_order_pdata_template,
+ .size_data = sizeof(gpio_probe_order_pdata_template),
+ .properties = properties,
+ };
+
+ cons = kunit_platform_device_register_full(test, &pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons);
+
+ wait_for_device_probe();
+ scoped_guard(device, &cons->dev)
+ bound = device_is_bound(&cons->dev);
+
+ KUNIT_ASSERT_FALSE(test, bound);
+
+ pdata = dev_get_platdata(&cons->dev);
+ KUNIT_ASSERT_EQ(test, pdata->probe_count, 0);
+ KUNIT_ASSERT_FALSE(test, pdata->gpio_ok);
+
+ pdevinfo = (struct platform_device_info){
+ .name = GPIO_TEST_PROVIDER,
+ .id = PLATFORM_DEVID_NONE,
+ .swnode = &gpio_test_provider_swnode,
+ };
+
+ prvd = kunit_platform_device_register_full(test, &pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd);
+
+ wait_for_device_probe();
+
+ scoped_guard(device, &prvd->dev)
+ bound = device_is_bound(&prvd->dev);
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ scoped_guard(device, &cons->dev)
+ bound = device_is_bound(&cons->dev);
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ pdata = dev_get_platdata(&cons->dev);
+ KUNIT_ASSERT_EQ(test, pdata->probe_count, 1);
+ KUNIT_ASSERT_TRUE(test, pdata->gpio_ok);
+}
+
+struct gpio_probe_defer_pdata {
+ unsigned int probe_count;
+ int gpio_err;
+};
+
+static const struct gpio_probe_defer_pdata gpio_probe_defer_pdata_template = {
+ .probe_count = 0,
+ .gpio_err = 0,
+};
+
+static int gpio_probe_defer_consumer_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_probe_defer_pdata *pdata = dev_get_platdata(dev);
+ struct gpio_desc *desc;
+
+ pdata->probe_count++;
+
+ desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH);
+ if (IS_ERR(desc)) {
+ pdata->gpio_err = PTR_ERR(desc);
+ return pdata->gpio_err;
+ }
+
+ pdata->gpio_err = 0;
+
+ return 0;
+}
+
+static struct platform_driver gpio_probe_defer_consumer_driver = {
+ .probe = gpio_probe_defer_consumer_probe,
+ .driver = {
+ .name = GPIO_PROBE_DEFER_TEST_CONSUMER,
+ },
+};
+
+/*
+ * Verify that a GPIO consumer referencing a provider whose software node is
+ * not registered yet, defers its probe instead of failing.
+ *
+ * The provider software node is deliberately left unregistered when the
+ * consumer is added. fw_devlink cannot resolve the reference, so it creates no
+ * supplier link and does not order the consumer - the consumer's probe() runs
+ * and reaches devm_gpiod_get(). The swnode GPIO lookup returns -ENOTCONN for a
+ * reference to an unregistered node, which gpiolib maps to -EPROBE_DEFER. Once
+ * the provider software node and device appear, the deferred consumer probes
+ * again and binds.
+ */
+static void gpio_swnode_probe_defer_on_unregistered(struct kunit *test)
+{
+ struct property_entry properties[2] = { };
+ struct gpio_probe_defer_pdata *pdata;
+ struct platform_device_info pdevinfo;
+ struct platform_device *prvd, *cons;
+ struct fwnode_handle *fwnode;
+ bool bound = false;
+ int ret;
+
+ ret = kunit_platform_driver_register(test, &gpio_test_provider_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_platform_driver_register(test, &gpio_probe_defer_consumer_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
+ &gpio_test_provider_swnode,
+ 0, GPIO_ACTIVE_HIGH);
+
+ pdevinfo = (struct platform_device_info){
+ .name = GPIO_PROBE_DEFER_TEST_CONSUMER,
+ .id = PLATFORM_DEVID_NONE,
+ .data = &gpio_probe_defer_pdata_template,
+ .size_data = sizeof(gpio_probe_defer_pdata_template),
+ .properties = properties,
+ };
+
+ cons = kunit_platform_device_register_full(test, &pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons);
+
+ wait_for_device_probe();
+ scoped_guard(device, &cons->dev)
+ bound = device_is_bound(&cons->dev);
+
+ KUNIT_ASSERT_FALSE(test, bound);
+
+ pdata = dev_get_platdata(&cons->dev);
+ KUNIT_ASSERT_GT(test, pdata->probe_count, 0);
+ KUNIT_ASSERT_EQ(test, pdata->gpio_err, -EPROBE_DEFER);
+
+ fwnode = kunit_software_node_register(test, &gpio_test_provider_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ pdevinfo = (struct platform_device_info){
+ .name = GPIO_TEST_PROVIDER,
+ .id = PLATFORM_DEVID_NONE,
+ .swnode = &gpio_test_provider_swnode,
+ };
+
+ prvd = kunit_platform_device_register_full(test, &pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd);
+
+ wait_for_device_probe();
+
+ scoped_guard(device, &prvd->dev)
+ bound = device_is_bound(&prvd->dev);
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ scoped_guard(device, &cons->dev)
+ bound = device_is_bound(&cons->dev);
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ pdata = dev_get_platdata(&cons->dev);
+ KUNIT_ASSERT_EQ(test, pdata->gpio_err, 0);
+
+ /* Tear down the consumer before the provider to free the GPIO. */
+ kunit_platform_device_unregister(test, cons);
+}
+
+static int gpio_swnode_probe_order_test_init(struct kunit *test)
+{
+ /*
+ * A prior test may have left a managed device link teardown queued on
+ * the device_link_mq. Flush it so that software_node_register()
+ * doesn't spuriously see the node as registered and fail with -EEXIST.
+ */
+ device_link_wait_removal();
+
+ return 0;
+}
+
+static struct kunit_case gpio_swnode_probe_order_tests[] = {
+ KUNIT_CASE(gpio_swnode_probe_order),
+ KUNIT_CASE(gpio_swnode_probe_defer_on_unregistered),
+ { }
+};
+
+static struct kunit_suite gpio_swnode_probe_order_test_suite = {
+ .name = "gpio-swnode-probe-order",
+ .test_cases = gpio_swnode_probe_order_tests,
+ .init = gpio_swnode_probe_order_test_init,
};
static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier);
@@ -310,15 +637,24 @@ static void gpio_unbind_with_consumers(struct kunit *test)
0, GPIO_ACTIVE_HIGH);
properties[1] = (struct property_entry){ };
- pdevinfo = (struct platform_device_info){
- .name = GPIO_UNBIND_TEST_CONSUMER,
- .id = PLATFORM_DEVID_NONE,
- .properties = properties,
- };
-
- cons = kunit_platform_device_register_full(test, &pdevinfo);
+ /*
+ * This test deliberately keeps the consumer bound while the provider
+ * is unregistered. fw_devlink would force-unbind the consumer before
+ * the provider so use the FWNODE_FLAG_LINKS_ADDED flag to opt out of
+ * it as a workaround.
+ */
+ cons = kunit_platform_device_alloc(test, GPIO_UNBIND_TEST_CONSUMER,
+ PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons);
+ ret = device_create_managed_software_node(&cons->dev, properties, NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode_set_flag(dev_fwnode(&cons->dev), FWNODE_FLAG_LINKS_ADDED);
+
+ ret = kunit_platform_device_add(test, cons);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
wait_for_device_probe();
scoped_guard(device, &cons->dev)
bound = device_is_bound(&cons->dev);
@@ -346,11 +682,196 @@ static struct kunit_case gpio_unbind_with_consumers_tests[] = {
static struct kunit_suite gpio_unbind_with_consumers_test_suite = {
.name = "gpio-unbind-with-consumers",
.test_cases = gpio_unbind_with_consumers_tests,
+ /* We need this here too to clean any left over links. */
+ .init = gpio_swnode_probe_order_test_init,
+};
+
+/*
+ * GPIO line hogs are described by child software nodes of the provider
+ * carrying the "gpio-hog" property. They are picked up automatically when the
+ * gpiochip is registered. Each hog below sits on a distinct line of the
+ * provider.
+ */
+#define GPIO_HOG_OUTPUT_HIGH_OFFSET 0
+#define GPIO_HOG_OUTPUT_LOW_OFFSET 1
+#define GPIO_HOG_INPUT_OFFSET 2
+
+static const u32 gpio_hog_output_high_gpios[] = {
+ GPIO_HOG_OUTPUT_HIGH_OFFSET, GPIO_ACTIVE_HIGH,
+};
+
+static const struct property_entry gpio_hog_output_high_properties[] = {
+ PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_output_high_gpios),
+ PROPERTY_ENTRY_STRING("line-name", "hog-output-high"),
+ PROPERTY_ENTRY_BOOL("output-high"),
+ PROPERTY_ENTRY_BOOL("gpio-hog"),
+ { }
+};
+
+static const struct software_node gpio_hog_output_high_swnode =
+ SOFTWARE_NODE("hog-output-high", gpio_hog_output_high_properties,
+ &gpio_test_provider_swnode);
+
+static const u32 gpio_hog_output_low_gpios[] = {
+ GPIO_HOG_OUTPUT_LOW_OFFSET, GPIO_ACTIVE_HIGH,
+};
+
+static const struct property_entry gpio_hog_output_low_properties[] = {
+ PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_output_low_gpios),
+ PROPERTY_ENTRY_STRING("line-name", "hog-output-low"),
+ PROPERTY_ENTRY_BOOL("output-low"),
+ PROPERTY_ENTRY_BOOL("gpio-hog"),
+ { }
+};
+
+static const struct software_node gpio_hog_output_low_swnode =
+ SOFTWARE_NODE("hog-output-low", gpio_hog_output_low_properties,
+ &gpio_test_provider_swnode);
+
+static const u32 gpio_hog_input_gpios[] = {
+ GPIO_HOG_INPUT_OFFSET, GPIO_ACTIVE_HIGH,
+};
+
+static const struct property_entry gpio_hog_input_properties[] = {
+ PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_input_gpios),
+ PROPERTY_ENTRY_STRING("line-name", "hog-input"),
+ PROPERTY_ENTRY_BOOL("input"),
+ PROPERTY_ENTRY_BOOL("gpio-hog"),
+ { }
+};
+
+static const struct software_node gpio_hog_input_swnode =
+ SOFTWARE_NODE("hog-input", gpio_hog_input_properties,
+ &gpio_test_provider_swnode);
+
+static const struct software_node *const gpio_hog_swnodes[] = {
+ &gpio_test_provider_swnode,
+ &gpio_hog_output_high_swnode,
+ &gpio_hog_output_low_swnode,
+ &gpio_hog_input_swnode,
+ NULL
+};
+
+/*
+ * Bring up the provider with a single hog child registered and verify both
+ * that the line was configured with the expected direction and that it is now
+ * exclusively owned (a consumer asking for the same line fails to bind).
+ *
+ * The provider node is referenced by the device through its fwnode rather than
+ * being handed to .swnode, so the device takes no software node reference of
+ * its own. Both the provider and the hog child are therefore test-managed and
+ * torn down (child first) once the test case completes.
+ */
+static void gpio_hog_assert(struct kunit *test, unsigned int offset,
+ int expected_direction)
+{
+ struct gpio_swnode_consumer_pdata *pdata;
+ struct platform_device_info pdevinfo;
+ struct property_entry properties[2];
+ struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ struct gpio_desc *desc;
+ bool bound = true;
+ int ret;
+
+ fwnode = software_node_fwnode(&gpio_test_provider_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ pdevinfo = (struct platform_device_info){
+ .name = GPIO_TEST_PROVIDER,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ };
+
+ pdev = kunit_platform_device_register_full(test, &pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+
+ /* The hog must have configured the line with the expected direction. */
+ struct gpio_device *gdev __free(gpio_device_put) =
+ gpio_device_find_by_label(GPIO_CONSUMER_NAME);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gdev);
+
+ desc = gpio_device_get_desc(gdev, offset);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc);
+
+ ret = gpiod_get_direction(desc);
+ KUNIT_ASSERT_EQ(test, ret, expected_direction);
+
+ /* A hogged line is owned exclusively, so a consumer must fail to bind. */
+ properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
+ &gpio_test_provider_swnode,
+ offset, GPIO_ACTIVE_HIGH);
+ properties[1] = (struct property_entry){ };
+
+ pdevinfo = (struct platform_device_info){
+ .name = GPIO_SWNODE_TEST_CONSUMER,
+ .id = PLATFORM_DEVID_NONE,
+ .data = &gpio_swnode_pdata_template,
+ .size_data = sizeof(gpio_swnode_pdata_template),
+ .properties = properties,
+ };
+
+ pdev = kunit_platform_device_register_full(test, &pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+ scoped_guard(device, &pdev->dev)
+ bound = device_is_bound(&pdev->dev);
+
+ KUNIT_ASSERT_FALSE(test, bound);
+
+ pdata = dev_get_platdata(&pdev->dev);
+ KUNIT_ASSERT_FALSE(test, pdata->gpio_ok);
+ KUNIT_ASSERT_EQ(test, pdata->errno, -EBUSY);
+}
+
+static void gpio_hog_output_high(struct kunit *test)
+{
+ gpio_hog_assert(test, GPIO_HOG_OUTPUT_HIGH_OFFSET, GPIO_LINE_DIRECTION_OUT);
+}
+
+static void gpio_hog_output_low(struct kunit *test)
+{
+ gpio_hog_assert(test, GPIO_HOG_OUTPUT_LOW_OFFSET, GPIO_LINE_DIRECTION_OUT);
+}
+
+static void gpio_hog_input(struct kunit *test)
+{
+ gpio_hog_assert(test, GPIO_HOG_INPUT_OFFSET, GPIO_LINE_DIRECTION_IN);
+}
+
+static int gpio_hog_suite_init(struct kunit_suite *suite)
+{
+ return software_node_register_node_group(gpio_hog_swnodes);
+}
+
+static void gpio_hog_suite_exit(struct kunit_suite *suite)
+{
+ software_node_unregister_node_group(gpio_hog_swnodes);
+}
+
+static struct kunit_case gpio_swnode_hog_tests[] = {
+ KUNIT_CASE(gpio_hog_output_high),
+ KUNIT_CASE(gpio_hog_output_low),
+ KUNIT_CASE(gpio_hog_input),
+ { }
+};
+
+static struct kunit_suite gpio_swnode_hog_test_suite = {
+ .name = "gpio-swnode-hog",
+ .test_cases = gpio_swnode_hog_tests,
+ .suite_init = gpio_hog_suite_init,
+ .suite_exit = gpio_hog_suite_exit,
+ .init = gpio_swnode_register_drivers,
};
kunit_test_suites(
&gpio_swnode_lookup_test_suite,
+ &gpio_swnode_probe_order_test_suite,
&gpio_unbind_with_consumers_test_suite,
+ &gpio_swnode_hog_test_suite,
);
MODULE_DESCRIPTION("Test module for the GPIO subsystem");
diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c
index 8d9591aa9304..304994c5c7d0 100644
--- a/drivers/gpio/gpiolib-swnode.c
+++ b/drivers/gpio/gpiolib-swnode.c
@@ -26,7 +26,6 @@
static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
{
const struct software_node *gdev_node;
- struct gpio_device *gdev;
gdev_node = to_software_node(fwnode);
if (!gdev_node)
@@ -41,27 +40,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
return ERR_PTR(-ENOENT);
fwnode_lookup:
- gdev = gpio_device_find_by_fwnode(fwnode);
- if (!gdev && gdev_node && gdev_node->name)
- /*
- * FIXME: We shouldn't need to compare the GPIO controller's
- * label against the software node that is supposedly attached
- * to it. However there are currently GPIO users that - knowing
- * the expected label of the GPIO chip whose pins they want to
- * control - set up dummy software nodes named after those GPIO
- * controllers, which aren't actually attached to them. In this
- * case gpio_device_find_by_fwnode() will fail as no device on
- * the GPIO bus is actually associated with the fwnode we're
- * looking for.
- *
- * As a fallback: continue checking the label if we have no
- * match. However, the situation described above is an abuse
- * of the software node API and should be phased out and the
- * following line - eventually removed.
- */
- gdev = gpio_device_find_by_label(gdev_node->name);
-
- return gdev ?: ERR_PTR(-EPROBE_DEFER);
+ return gpio_device_find_by_fwnode(fwnode) ?: ERR_PTR(-EPROBE_DEFER);
}
static int swnode_gpio_get_reference(const struct fwnode_handle *fwnode,
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index 9ee65d63c525..f78e758706ed 100644
--- a/drivers/iio/adc/ad7173.c
+++ b/drivers/iio/adc/ad7173.c
@@ -560,8 +560,8 @@ out:
return ret;
}
-static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
- unsigned int offset, unsigned int *reg,
+static int ad7173_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
*mask = AD7173_GPO_DATA(offset);
@@ -569,8 +569,8 @@ static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
return 0;
}
-static int ad4111_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
- unsigned int offset, unsigned int *reg,
+static int ad4111_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
*mask = AD4111_GPO01_DATA(offset);
diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c
index 7bdf2cb94176..6207eb2fac74 100644
--- a/drivers/iio/addac/stx104.c
+++ b/drivers/iio/addac/stx104.c
@@ -349,9 +349,9 @@ static const struct iio_chan_spec stx104_channels_diff[] = {
STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
};
-static int stx104_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
- unsigned int offset, unsigned int *const reg,
- unsigned int *const mask)
+static int stx104_reg_mask_xlate(struct gpio_regmap *const gpio, enum gpio_regmap_operation op,
+ const unsigned int base, unsigned int offset,
+ unsigned int *const reg, unsigned int *const mask)
{
/* Output lines are located at same register bit offsets as input lines */
if (offset >= 4)
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index e19617485679..129fc4212a5e 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -23,8 +23,8 @@
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/legacy.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
@@ -528,6 +528,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
*/
bdata->gpiod = NULL;
}
+#ifdef CONFIG_GPIOLIB_LEGACY
} else if (gpio_is_valid(button->gpio)) {
/*
* Legacy GPIO number, so request the GPIO here and
@@ -546,6 +547,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
gpiod_toggle_active_low(bdata->gpiod);
+#endif
}
if (bdata->gpiod) {
@@ -583,8 +585,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (irq < 0) {
error = irq;
dev_err_probe(dev, error,
- "Unable to get irq number for GPIO %d\n",
- button->gpio);
+ "Unable to get irq number for GPIO\n");
return error;
}
bdata->irq = irq;
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index e6707d72210e..4e7a366ff05b 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -18,8 +18,8 @@
#include <linux/input.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/legacy.h>
#include <linux/gpio_keys.h>
#include <linux/property.h>
@@ -301,6 +301,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(bdata->gpiod),
"failed to get gpio\n");
}
+#ifdef CONFIG_GPIOLIB_LEGACY
} else if (gpio_is_valid(button->gpio)) {
/*
* Legacy GPIO number so request the GPIO here and
@@ -323,6 +324,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
gpiod_toggle_active_low(bdata->gpiod);
+#endif
}
bdata->last_state = -1;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7154eaf5a60b..abee43158d84 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -904,6 +904,9 @@ config INPUT_IDEAPAD_SLIDEBAR
config INPUT_SOC_BUTTON_ARRAY
tristate "Windows-compatible SoC Button Array"
depends on KEYBOARD_GPIO && ACPI
+ depends on X86
+ depends on GPIOLIB
+ select GPIOLIB_LEGACY
help
Say Y here if you have a SoC-based tablet that originally runs
Windows 8 or a Microsoft Surface Book 2, Pro 5, Laptop 1 or later.
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index b8cad415c62c..a6c984205123 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -15,7 +15,6 @@
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio_keys.h>
-#include <linux/gpio.h>
#include <linux/platform_device.h>
static bool use_low_level_irq;
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
index 34d66ba9646a..0d56cd6fbc6a 100644
--- a/drivers/mfd/aat2870-core.c
+++ b/drivers/mfd/aat2870-core.c
@@ -13,7 +13,7 @@
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/mfd/core.h>
#include <linux/mfd/aat2870.h>
#include <linux/regulator/machine.h>
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
index 544016d420fe..14f9cb2c4b67 100644
--- a/drivers/mfd/arizona-irq.c
+++ b/drivers/mfd/arizona-irq.c
@@ -8,7 +8,7 @@
*/
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
diff --git a/drivers/mfd/lp3943.c b/drivers/mfd/lp3943.c
index 6764553147e4..1918b5c7a5e7 100644
--- a/drivers/mfd/lp3943.c
+++ b/drivers/mfd/lp3943.c
@@ -28,7 +28,7 @@
*/
#include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/lp3943.h>
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index b5bda477ebfc..9d5bb67e8084 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -35,7 +35,7 @@ struct sm501_device {
struct sm501_gpio;
#ifdef CONFIG_MFD_SM501_GPIO
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
struct sm501_gpio_chip {
struct gpio_chip gpio;
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
index e2f6858d101e..b11cd2c03311 100644
--- a/drivers/mfd/tps6105x.c
+++ b/drivers/mfd/tps6105x.c
@@ -16,7 +16,7 @@
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/err.h>
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
index 7098712ea008..cc8545a972bc 100644
--- a/drivers/mfd/tps65911-comparator.c
+++ b/drivers/mfd/tps65911-comparator.c
@@ -14,7 +14,7 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mfd/tps65910.h>
#define COMP1 0
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index a46cea948763..a15483489b9d 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c
index 724fd6ee0fd0..1612b5d76975 100644
--- a/drivers/pcmcia/bcm63xx_pcmcia.c
+++ b/drivers/pcmcia/bcm63xx_pcmcia.c
@@ -14,7 +14,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pci.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_io.h>
diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c
index 7b896d7dbc9f..410d24762f13 100644
--- a/drivers/pcmcia/db1xxx_ss.c
+++ b/drivers/pcmcia/db1xxx_ss.c
@@ -23,7 +23,7 @@
*/
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/module.h>
diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c
index 10cb99c20a7f..6c52d90795eb 100644
--- a/drivers/pcmcia/sa1100_h3600.c
+++ b/drivers/pcmcia/sa1100_h3600.c
@@ -11,7 +11,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <mach/hardware.h>
#include <asm/irq.h>
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index 87aa3f667117..23585e3968e9 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -32,8 +32,8 @@
#include <linux/cpufreq.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/legacy.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c
index 8a8aae1843b5..ba1bd0f9d9f8 100644
--- a/drivers/pcmcia/xxs1500_ss.c
+++ b/drivers/pcmcia/xxs1500_ss.c
@@ -7,7 +7,7 @@
*/
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
index 8473fa574529..171bf097a8b8 100644
--- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
+++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
@@ -3,7 +3,6 @@
#include <linux/delay.h>
#include <linux/extcon-provider.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index f68de14366db..07a839e41db7 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -11,7 +11,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mutex.h>
diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c
index 7531a8a04912..1f5dba49ace4 100644
--- a/drivers/phy/ti/phy-j721e-wiz.c
+++ b/drivers/phy/ti/phy-j721e-wiz.c
@@ -11,7 +11,6 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm63xx.c b/drivers/pinctrl/bcm/pinctrl-bcm63xx.c
index a4aa4146b530..b41a3d16aabe 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm63xx.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm63xx.c
@@ -19,6 +19,7 @@
#define BCM63XX_DATA_REG 0x0c
static int bcm63xx_reg_mask_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
diff --git a/drivers/pinctrl/pinctrl-tps6594.c b/drivers/pinctrl/pinctrl-tps6594.c
index 456a3cfc8de9..937fc9cb54b7 100644
--- a/drivers/pinctrl/pinctrl-tps6594.c
+++ b/drivers/pinctrl/pinctrl-tps6594.c
@@ -346,6 +346,7 @@ static struct tps6594_pinctrl tps6594_template_pinctrl = {
};
static int tps6594_gpio_regmap_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
diff --git a/include/kunit/fwnode.h b/include/kunit/fwnode.h
new file mode 100644
index 000000000000..e250bb87e144
--- /dev/null
+++ b/include/kunit/fwnode.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * KUnit resource management helpers for firmware nodes.
+ *
+ * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
+ */
+
+#ifndef _KUNIT_FWNODE_H
+#define _KUNIT_FWNODE_H
+
+struct device;
+struct fwnode_handle;
+struct kunit;
+struct property_entry;
+struct software_node;
+
+struct fwnode_handle *
+kunit_fwnode_create_software_node(struct kunit *test,
+ const struct property_entry *properties,
+ const struct fwnode_handle *parent);
+struct fwnode_handle *
+kunit_software_node_register(struct kunit *test,
+ const struct software_node *node);
+int kunit_software_node_register_node_group(struct kunit *test,
+ const struct software_node *const *nodes);
+int kunit_device_add_software_node(struct kunit *test, struct device *dev,
+ const struct software_node *node);
+
+#endif /* _KUNIT_FWNODE_H */
diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h
index 5eb88f5d0630..1a141056716f 100644
--- a/include/linux/gpio/machine.h
+++ b/include/linux/gpio/machine.h
@@ -54,7 +54,7 @@ static struct gpiod_lookup_table _name = { \
.dev_id = _dev_id, \
.table = { \
GPIO_LOOKUP(_key, _chip_hwnum, _con_id, _flags), \
- {}, \
+ { } \
}, \
}
diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h
index 06255756710d..6f52c140e50d 100644
--- a/include/linux/gpio/regmap.h
+++ b/include/linux/gpio/regmap.h
@@ -3,6 +3,8 @@
#ifndef _LINUX_GPIO_REGMAP_H
#define _LINUX_GPIO_REGMAP_H
+#include <linux/types.h>
+
struct device;
struct fwnode_handle;
struct gpio_regmap;
@@ -14,6 +16,32 @@ struct regmap;
#define GPIO_REGMAP_ADDR(addr) ((addr) ? : GPIO_REGMAP_ADDR_ZERO)
/**
+ * enum gpio_regmap_operation - Operation type for gpio_regmap callbacks
+ *
+ * Traditionally, the operation type was inferred from the base register.
+ * However, that approach does not always work — for example, when all control
+ * bits of a single GPIO reside in the same register. This enum allows the
+ * callbacks (reg_mask_xlate and value_xlate) to explicitly distinguish between
+ * operation types. The user is free to choose which method to use.
+ *
+ * Read operation:
+ * @GPIO_REGMAP_GET_OP: Indicates a read operation to get the current GPIO value.
+ *
+ * Write operation:
+ * @GPIO_REGMAP_SET_OP: Indicates a write operation to set the GPIO output value.
+ *
+ * Direction operations:
+ * @GPIO_REGMAP_GET_DIR_OP: Indicates a read operation to get the GPIO direction.
+ * @GPIO_REGMAP_SET_DIR_OP: Indicates a write operation to set the GPIO direction.
+ */
+enum gpio_regmap_operation {
+ GPIO_REGMAP_GET_OP,
+ GPIO_REGMAP_SET_OP,
+ GPIO_REGMAP_GET_DIR_OP,
+ GPIO_REGMAP_SET_DIR_OP,
+};
+
+/**
* struct gpio_regmap_config - Description of a generic regmap gpio_chip.
* @parent: The parent device
* @regmap: The regmap used to access the registers
@@ -34,10 +62,6 @@ struct regmap;
* @ngpio_per_reg: (Optional) Number of GPIOs per register
* @irq_domain: (Optional) IRQ domain if the controller is
* interrupt-capable
- * @reg_mask_xlate: (Optional) Translates base address and GPIO
- * offset to a register/bitmask pair. If not
- * given the default gpio_regmap_simple_xlate()
- * is used.
* @fixed_direction_mask:
* (Optional) Bitmap representing the GPIO lines that
* make use of the @fixed_direction_output list to
@@ -48,16 +72,28 @@ struct regmap;
* (Optional) Bitmap representing the fixed direction of
* the GPIO lines. Useful when there are GPIO lines with a
* fixed direction mixed together in the same register.
- * @drvdata: (Optional) Pointer to driver specific data which is
- * not used by gpio-remap but is provided "as is" to the
- * driver callback(s).
- * @init_valid_mask: (Optional) Routine to initialize @valid_mask, to be used
- * if not all GPIOs are valid.
* @regmap_irq_chip: (Optional) Pointer on an regmap_irq_chip structure. If
* set, a regmap-irq device will be created and the IRQ
* domain will be set accordingly.
* @regmap_irq_line: (Optional) The IRQ the device uses to signal interrupts.
* @regmap_irq_flags: (Optional) The IRQF_ flags to use for the interrupt.
+ * @reg_mask_xlate: (Optional) Translates base address and GPIO
+ * offset to a register/bitmask pair. If not
+ * given the default gpio_regmap_simple_xlate()
+ * is used.
+ * @init_valid_mask: (Optional) Routine to initialize @valid_mask, to be used
+ * if not all GPIOs are valid.
+ * @value_xlate: (Optional) Routine to translate the register value and
+ * mask before writing. This allows driver-specific logic
+ * to append additional bits (like write-enable masks)
+ * dynamically based on the current operation
+ * (GPIO_REGMAP_SET_OP and GPIO_REGMAP_SET_DIR_OP).
+ * @set_config: (Optional) Callback for setting GPIO configuration such
+ * as debounce, drive strength, or other hardware specific
+ * settings.
+ * @drvdata: (Optional) Pointer to driver specific data which is
+ * not used by gpio-remap but is provided "as is" to the
+ * driver callback(s).
*
* The ->reg_mask_xlate translates a given base address and GPIO offset to
* register and mask pair. The base address is one of the given register
@@ -104,14 +140,21 @@ struct gpio_regmap_config {
unsigned long regmap_irq_flags;
#endif
- int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
- unsigned int offset, unsigned int *reg,
- unsigned int *mask);
+ int (*reg_mask_xlate)(struct gpio_regmap *gpio, enum gpio_regmap_operation,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask);
int (*init_valid_mask)(struct gpio_chip *gc,
unsigned long *valid_mask,
unsigned int ngpios);
+ int (*value_xlate)(struct gpio_regmap *gpio, enum gpio_regmap_operation,
+ unsigned int base, unsigned int offset, unsigned int reg,
+ unsigned int *mask, unsigned int *val);
+
+ int (*set_config)(struct gpio_regmap *gpio, struct gpio_chip *chip,
+ unsigned int offset, unsigned long config);
+
void *drvdata;
};
@@ -121,4 +164,10 @@ struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
const struct gpio_regmap_config *config);
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio);
+int gpio_regmap_reqres_irq(struct gpio_regmap *gpio, unsigned int offset);
+void gpio_regmap_relres_irq(struct gpio_regmap *gpio, unsigned int offset);
+
+void gpio_regmap_enable_irq(struct gpio_regmap *gpio, irq_hw_number_t hwirq);
+void gpio_regmap_disable_irq(struct gpio_regmap *gpio, irq_hw_number_t hwirq);
+
#endif /* _LINUX_GPIO_REGMAP_H */
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 80fa930b04c6..e8d6dc290efb 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -25,7 +25,9 @@ struct device;
*/
struct gpio_keys_button {
unsigned int code;
+#ifdef CONFIG_GPIOLIB_LEGACY
int gpio;
+#endif
int active_low;
const char *desc;
unsigned int type;
diff --git a/include/linux/mfd/lp3943.h b/include/linux/mfd/lp3943.h
index 402f01078fcc..5d2d172d3598 100644
--- a/include/linux/mfd/lp3943.h
+++ b/include/linux/mfd/lp3943.h
@@ -10,7 +10,7 @@
#ifndef __MFD_LP3943_H__
#define __MFD_LP3943_H__
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
/* Registers */
diff --git a/include/linux/mfd/ti-lmu.h b/include/linux/mfd/ti-lmu.h
index 0bc0e8199798..2089ec5124e8 100644
--- a/include/linux/mfd/ti-lmu.h
+++ b/include/linux/mfd/ti-lmu.h
@@ -10,7 +10,6 @@
#ifndef __MFD_TI_LMU_H__
#define __MFD_TI_LMU_H__
-#include <linux/gpio.h>
#include <linux/notifier.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h
index f67ef0a4e041..3813fc9c2b55 100644
--- a/include/linux/mfd/tps65910.h
+++ b/include/linux/mfd/tps65910.h
@@ -12,7 +12,7 @@
#ifndef __LINUX_MFD_TPS65910_H
#define __LINUX_MFD_TPS65910_H
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
/* TPS chip id list */
diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h
index ede237384723..4ad54e22ed33 100644
--- a/include/linux/mfd/ucb1x00.h
+++ b/include/linux/mfd/ucb1x00.h
@@ -9,7 +9,7 @@
#include <linux/device.h>
#include <linux/mfd/mcp.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/mutex.h>
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index df44cb30f53b..370baa19db87 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1770,6 +1770,8 @@ struct regmap_irq_chip {
void *irq_drv_data);
unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
unsigned int base, int index);
+ int (*irq_reqres)(void *irq_drv_data, irq_hw_number_t hwirq);
+ void (*irq_relres)(void *irq_drv_data, irq_hw_number_t hwirq);
void *irq_drv_data;
};
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 2e8a6b71a2ab..204e02b10eba 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -11,6 +11,7 @@ kunit-objs += test.o \
attributes.o \
device.o \
platform.o \
+ fwnode.o \
bug.o
ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
diff --git a/lib/kunit/fwnode.c b/lib/kunit/fwnode.c
new file mode 100644
index 000000000000..a58ce0984d76
--- /dev/null
+++ b/lib/kunit/fwnode.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
+ */
+
+#include <kunit/fwnode.h>
+#include <kunit/test.h>
+
+#include <linux/fwnode.h>
+#include <linux/property.h>
+
+KUNIT_DEFINE_ACTION_WRAPPER(fwnode_remove_software_node_wrapper,
+ fwnode_remove_software_node,
+ struct fwnode_handle *);
+
+/**
+ * kunit_fwnode_create_software_node() - Create a kunit-managed software node
+ * @test: Test context
+ * @properties: Properties to use to create the new software node
+ * @parent: Parent of this software node
+ *
+ * Create a test-managed software node and return its firmware node handle.
+ * The software node is removed after the test case completes.
+ *
+ * Returns:
+ * Firmware node handle of the newly created software node or IS_ERR() on
+ * failure.
+ */
+struct fwnode_handle *
+kunit_fwnode_create_software_node(struct kunit *test,
+ const struct property_entry *properties,
+ const struct fwnode_handle *parent)
+{
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ fwnode = fwnode_create_software_node(properties, parent);
+ if (IS_ERR(fwnode))
+ return fwnode;
+
+ ret = kunit_add_action_or_reset(test, fwnode_remove_software_node_wrapper,
+ fwnode);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return fwnode;
+}
+EXPORT_SYMBOL_GPL(kunit_fwnode_create_software_node);
+
+KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_wrapper,
+ software_node_unregister,
+ const struct software_node *);
+
+/**
+ * kunit_software_node_register() - Register a kunit-managed software node
+ * @test: Test context
+ * @swnode: Software node to register
+ *
+ * Register a test-managed software node and return its firmware node handle.
+ * The software node is unregistered after the test case completes.
+ *
+ * Returns:
+ * Firmware node handle of the registered software node or IS_ERR() on failure.
+ */
+struct fwnode_handle *
+kunit_software_node_register(struct kunit *test,
+ const struct software_node *swnode)
+{
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ ret = software_node_register(swnode);
+ if (ret)
+ return ERR_PTR(ret);
+
+ fwnode = software_node_fwnode(swnode);
+ if (WARN_ON(!fwnode))
+ return ERR_PTR(-ENOENT);
+
+ ret = kunit_add_action_or_reset(test, software_node_unregister_wrapper,
+ (void *)swnode);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return fwnode;
+}
+EXPORT_SYMBOL_GPL(kunit_software_node_register);
+
+KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_node_group_wrapper,
+ software_node_unregister_node_group,
+ const struct software_node *const *);
+
+/**
+ * kunit_software_node_register_node_group() - Register a kunit-managed software node group
+ * @test: Test context
+ * @nodes: Software node group to register
+ *
+ * Register a test-managed software node group. The nodes are unregistered
+ * after the test case completes.
+ *
+ * Returns:
+ * 0 on success, negative error number on failure.
+ */
+int kunit_software_node_register_node_group(struct kunit *test,
+ const struct software_node *const *nodes)
+{
+ int ret;
+
+ ret = software_node_register_node_group(nodes);
+ if (ret)
+ return ret;
+
+ return kunit_add_action_or_reset(test, software_node_unregister_node_group_wrapper,
+ (void *)nodes);
+}
+EXPORT_SYMBOL_GPL(kunit_software_node_register_node_group);
+
+KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
+ device_remove_software_node,
+ struct device *);
+
+/**
+ * kunit_device_add_software_node() - Assign a kunit-managed software node to a device
+ * @test: Test context
+ * @dev: Device to assign the software node for
+ * @node: The software node to assign
+ *
+ * Make @node the secondary firmware node of @dev. If @dev has no primary
+ * firmware node, @node will become the primary node. The software node will
+ * be automatically removed from @dev when the test case completes.
+ *
+ * Returns:
+ * 0 on success, negative error number on failure.
+ */
+int kunit_device_add_software_node(struct kunit *test, struct device *dev,
+ const struct software_node *node)
+{
+ int ret;
+
+ ret = device_add_software_node(dev, node);
+ if (ret)
+ return ret;
+
+ return kunit_add_action_or_reset(test, device_remove_software_node_wrapper, dev);
+}
+EXPORT_SYMBOL_GPL(kunit_device_add_software_node);
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index e0996a9d90b0..6366f72b3667 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -300,6 +300,7 @@ config SND_CS5535AUDIO
tristate "CS5535/CS5536 Audio"
depends on X86_32 || MIPS || COMPILE_TEST
depends on HAS_IOPORT
+ depends on GPIOLIB_LEGACY || !OLPC
select SND_PCM
select SND_AC97_CODEC
help
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
index 122170a410d9..cfdcc5bf4341 100644
--- a/sound/pci/cs5535audio/cs5535audio_olpc.c
+++ b/sound/pci/cs5535audio/cs5535audio_olpc.c
@@ -9,7 +9,7 @@
#include <sound/info.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <asm/olpc.h>
#include "cs5535audio.h"
diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c
index f2a58163de0e..f2448b4c11fc 100644
--- a/sound/soc/codecs/cs42l84.c
+++ b/sound/soc/codecs/cs42l84.c
@@ -16,7 +16,6 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/acpi.h>
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
index 83c6cbd40804..0dd35fa86cc5 100644
--- a/sound/soc/codecs/cx2072x.c
+++ b/sound/soc/codecs/cx2072x.c
@@ -11,7 +11,7 @@
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/module.h>
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 61e1bf1b3c9e..cbed11136935 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -6,7 +6,6 @@
*/
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>