diff --git a/Documentation/devicetree/bindings/gpio/gpio-delay.yaml b/Documentation/devicetree/bindings/gpio/gpio-delay.yaml index 1cebc4058e27..b99ceff6c5f6 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-delay.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-delay.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: GPIO delay controller maintainers: - - Alexander Stein + - Alexander Stein description: | This binding describes an electrical setup where setting an GPIO output diff --git a/Documentation/devicetree/bindings/gpio/gpio-thunderx.txt b/Documentation/devicetree/bindings/gpio/gpio-thunderx.txt deleted file mode 100644 index 3f883ae29d11..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-thunderx.txt +++ /dev/null @@ -1,27 +0,0 @@ -Cavium ThunderX/OCTEON-TX GPIO controller bindings - -Required Properties: -- reg: The controller bus address. -- gpio-controller: Marks the device node as a GPIO controller. -- #gpio-cells: Must be 2. - - First cell is the GPIO pin number relative to the controller. - - Second cell is a standard generic flag bitfield as described in gpio.txt. - -Optional Properties: -- compatible: "cavium,thunder-8890-gpio", unused as PCI driver binding is used. -- interrupt-controller: Marks the device node as an interrupt controller. -- #interrupt-cells: Must be present and have value of 2 if - "interrupt-controller" is present. - - First cell is the GPIO pin number relative to the controller. - - Second cell is triggering flags as defined in interrupts.txt. - -Example: - -gpio_6_0: gpio@6,0 { - compatible = "cavium,thunder-8890-gpio"; - reg = <0x3000 0 0 0 0>; /* DEVFN = 0x30 (6:0) */ - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml index f42c54653d52..3da2cbcb652e 100644 --- a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml @@ -33,6 +33,9 @@ properties: clocks: maxItems: 1 + resets: + maxItems: 1 + "#gpio-cells": const: 2 @@ -62,6 +65,11 @@ allOf: contains: const: microchip,mpfs-gpio then: + properties: + ngpios: + enum: [14, 24, 32] + interrupts: + minItems: 14 required: - interrupts - "#interrupt-cells" @@ -82,18 +90,19 @@ examples: compatible = "microchip,mpfs-gpio"; reg = <0x20122000 0x1000>; clocks = <&clkcfg 25>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; gpio-controller; #gpio-cells = <2>; + ngpios = <32>; interrupt-controller; #interrupt-cells = <2>; - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; + interrupts = <64>, <65>, <66>, <67>, + <68>, <69>, <70>, <71>, + <72>, <73>, <74>, <75>, + <76>, <77>, <78>, <79>, + <80>, <81>, <82>, <83>, + <84>, <85>, <86>, <87>, + <88>, <89>, <90>, <91>, + <92>, <93>, <94>, <95>; }; ... diff --git a/Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml b/Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml new file mode 100644 index 000000000000..a05cd339253a --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/pin-control-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Pin control based generic GPIO controller + +description: + The pin control-based GPIO will facilitate a pin controller's ability + to drive electric lines high/low and other generic properties of a + pin controller to perform general-purpose one-bit binary I/O. + +maintainers: + - Dan Carpenter + +properties: + compatible: + const: scmi-pinctrl-gpio + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-line-names: true + + gpio-ranges: true + + ngpios: true + +patternProperties: + "^.+-hog(-[0-9]+)?$": + type: object + + required: + - gpio-hog + +required: + - compatible + - gpio-controller + - "#gpio-cells" + - gpio-ranges + - ngpios + +additionalProperties: false + +examples: + - | + gpio { + compatible = "scmi-pinctrl-gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-line-names = "gpio_5_17", "gpio_5_20", "gpio_5_22", "gpio_2_1"; + gpio-ranges = <&scmi_pinctrl 0 30 4>; + pinctrl-names = "default"; + pinctrl-0 = <&keys_pins>; + }; diff --git a/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml b/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml index 728099c65824..b18f8f0ca0ae 100644 --- a/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml @@ -30,6 +30,7 @@ properties: - realtek,rtl8390-gpio - realtek,rtl9300-gpio - realtek,rtl9310-gpio + - realtek,rtl9607-gpio - const: realtek,otto-gpio reg: true diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index 069b54d8591b..4ac1e12cf872 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -108,9 +108,8 @@ macro, which ties a software node representing the GPIO controller with consumer device. It allows consumers to use regular gpiolib APIs, such as gpiod_get(), gpiod_get_optional(). -The software node representing a GPIO controller need not be attached to the -GPIO controller device. The only requirement is that the node must be -registered and its name must match the GPIO controller's label. +The software node representing a GPIO controller must be attached to the +GPIO controller device - either as the primary or the secondary firmware node. For example, here is how to describe a single GPIO-connected LED. This is an alternative to using platform_data on legacy systems. @@ -122,8 +121,7 @@ alternative to using platform_data on legacy systems. #include /* - * 1. Define a node for the GPIO controller. Its .name must match the - * controller's label. + * 1. Define a node for the GPIO controller. */ static const struct software_node gpio_controller_node = { .name = "gpio-foo", @@ -153,6 +151,21 @@ alternative to using platform_data on legacy systems. }; software_node_register_node_group(swnodes); + /* + * 5. Attach the GPIO controller's software node to the device and + * register it. + */ + static void gpio_foo_register(void) + { + struct platform_device_info pdev_info = { + .name = "gpio-foo", + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_controller_node + }; + + platform_device_register_full(&pdev_info); + } + // Then register a platform_device for "leds-gpio" and associate // it with &led_device_swnode via .fwnode. @@ -239,22 +252,6 @@ mapping and is thus transparent to GPIO consumers. A set of functions such as gpiod_set_value() is available to work with the new descriptor-oriented interface. -Boards using platform data can also hog GPIO lines by defining GPIO hog tables. - -.. code-block:: c - - struct gpiod_hog gpio_hog_table[] = { - GPIO_HOG("gpio.0", 10, "foo", GPIO_ACTIVE_LOW, GPIOD_OUT_HIGH), - { } - }; - -And the table can be added to the board code as follows:: - - gpiod_add_hogs(gpio_hog_table); - -The line will be hogged as soon as the gpiochip is created or - in case the -chip was created earlier - when the hog table is registered. - Arrays of pins -------------- In addition to requesting pins belonging to a function one by one, a device may diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index 85d86f92c41b..a4f160b95089 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -87,6 +87,33 @@ atomic context on realtime kernels (inside hard IRQ handlers and similar contexts). Normally this should not be required. +GPIO level semantics +-------------------- + +The gpip_chip .get/set[_multiple]() line values are clamped to the boolean +space [0, 1], low level or high level. + +Low and high values are defined as physical low on the line in/out to the +connector such as a physical pad, pin or rail. + +The GPIO library has internal logic to handle lines that are active low, such +as indicated by overstrike or #name in a schematic, and the driver should not +try to second-guess the logic value of a line. + +The way GPIO values are handled by the consumers is that the library present +the *logical* value to the consumer. A line is *asserted* if its *logical* +value is 1, and *de-asserted* if its logical value is 0. If inversion is +required, this is handled by gpiolib and configured using hardware descriptions +such as device tree or ACPI that can clearly indicate if a line is active +high or low. + +Since electronics commonly insert inverters as driving stages or protection +buffers in front of a GPIO line it is necessary that this semantic is part +of the hardware description, so that consumers such as kernel drivers need +not worry about this, and can for example assert a RESET line tied to a GPIO +pin by setting it to logic 1 even if it is physically active low. + + GPIO electrical configuration ----------------------------- diff --git a/Documentation/driver-api/gpio/legacy-boards.rst b/Documentation/driver-api/gpio/legacy-boards.rst index 46e3a26dba77..a9d33bcbb176 100644 --- a/Documentation/driver-api/gpio/legacy-boards.rst +++ b/Documentation/driver-api/gpio/legacy-boards.rst @@ -36,12 +36,10 @@ Requirements for GPIO Properties When using software nodes to describe GPIO connections, the following requirements must be met for the GPIO core to correctly resolve the reference: -1. **The GPIO controller's software node "name" must match the controller's - "label".** The gpiolib core uses this name to find the corresponding - struct gpio_chip at runtime. - This software node has to be registered, but need not be attached to the - device representing the GPIO controller that is providing the GPIO in - question. It may be left as a "free floating" node. +1. **The GPIO controller's software node must be registered and attached to + the controller's ``struct device`` either as its primary or secondary + firmware node.** The gpiolib core uses the address of the firmware node to + find the corresponding ``struct gpio_chip`` at runtime. 2. **The GPIO property must be a reference.** The ``PROPERTY_ENTRY_GPIO()`` macro handles this as it is an alias for ``PROPERTY_ENTRY_REF()``. @@ -121,13 +119,21 @@ A typical legacy board file might look like this: /* Device registration */ static int __init myboard_init(void) { + struct platform_device_info pdev_info = { + .name = MYBOARD_GPIO_CONTROLLER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_controller_node + }; + gpiod_add_lookup_table(&myboard_leds_gpios); gpiod_add_lookup_table(&myboard_buttons_gpios); + platform_device_register_full(&pdev_info); platform_device_register_data(NULL, "leds-gpio", -1, &myboard_leds_pdata, sizeof(myboard_leds_pdata)); platform_device_register_data(NULL, "gpio-keys", -1, - &myboard_buttons_pdata, sizeof(myboard_buttons_pdata)); + &myboard_buttons_pdata, + sizeof(myboard_buttons_pdata)); return 0; } @@ -141,8 +147,7 @@ Step 1: Define the GPIO Controller Node *************************************** First, define a software node that represents the GPIO controller that the -LEDs and buttons are connected to. The ``name`` of this node must match the -name of the driver for the GPIO controller (e.g., "gpio-foo"). +LEDs and buttons are connected to. The ``name`` of this node is optional. .. code-block:: c @@ -257,6 +262,16 @@ software nodes using the ``fwnode`` field in struct platform_device_info. if (error) return error; + memset(&pdev_info, 0, sizeof(pdev_info)); + pdev_info.name = MYBOARD_GPIO_CONTROLLER; + pdev_info.id = PLATFORM_DEVID_NONE; + pdev_info.swnode = &myboard_gpio_controller_node; + gpio_pdev = platform_device_register_full(&pdev_info); + if (IS_ERR(gpio_pdev)) { + error = PTR_ERR(gpio_pdev); + goto err_unregister_nodes; + } + memset(&pdev_info, 0, sizeof(pdev_info)); pdev_info.name = "leds-gpio"; pdev_info.id = PLATFORM_DEVID_NONE; @@ -264,6 +279,7 @@ software nodes using the ``fwnode`` field in struct platform_device_info. leds_pdev = platform_device_register_full(&pdev_info); if (IS_ERR(leds_pdev)) { error = PTR_ERR(leds_pdev); + platform_device_unregister(gpio_pdev); goto err_unregister_nodes; } @@ -274,6 +290,7 @@ software nodes using the ``fwnode`` field in struct platform_device_info. keys_pdev = platform_device_register_full(&pdev_info); if (IS_ERR(keys_pdev)) { error = PTR_ERR(keys_pdev); + platform_device_unregister(gpio_pdev); platform_device_unregister(leds_pdev); goto err_unregister_nodes; } @@ -289,6 +306,7 @@ software nodes using the ``fwnode`` field in struct platform_device_info. { platform_device_unregister(keys_pdev); platform_device_unregister(leds_pdev); + platform_device_unregister(gpio_pdev); software_node_unregister_node_group(myboard_swnodes); } diff --git a/MAINTAINERS b/MAINTAINERS index 20c2b5b0bb8d..763a20069fea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10982,7 +10982,6 @@ F: drivers/gpio/ F: include/dt-bindings/gpio/ F: include/linux/gpio.h F: include/linux/gpio/ -F: include/linux/of_gpio.h K: (devm_)?gpio_(request|free|direction|get|set) K: GPIOD_FLAGS_BIT_NONEXCLUSIVE K: devm_gpiod_unhinge @@ -12872,6 +12871,13 @@ F: drivers/gpio/gpio-sodaville.c F: drivers/gpio/gpio-tangier.c F: drivers/gpio/gpio-tangier.h +INTEL GPIO GPE DRIVER +M: Alan Borzeszkowski +R: Mika Westerberg +L: linux-gpio@vger.kernel.org +S: Supported +F: drivers/gpio/gpio-novalake-events.c + INTEL GVT-g DRIVERS (Intel GPU Virtualization) R: Zhenyu Wang R: Zhi Wang diff --git a/arch/arc/plat-axs10x/Kconfig b/arch/arc/plat-axs10x/Kconfig index b9652c69d1b9..40f2a74d404a 100644 --- a/arch/arc/plat-axs10x/Kconfig +++ b/arch/arc/plat-axs10x/Kconfig @@ -7,7 +7,6 @@ menuconfig ARC_PLAT_AXS10X bool "Synopsys ARC AXS10x Software Development Platforms" select DW_APB_ICTL select GPIO_DWAPB - select OF_GPIO select HAVE_PCI select GENERIC_IRQ_CHIP select GPIOLIB diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index 16392720296c..1bec4fa0bd5e 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -556,10 +556,30 @@ static struct gpiod_lookup_table *ams_delta_gpio_tables[] __initdata = { &ams_delta_nand_gpio_table, }; -static struct gpiod_hog ams_delta_gpio_hogs[] = { - GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_KEYBRD_DATAOUT, "keybrd_dataout", - GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW), - {}, +static const struct software_node latch2_gpio_swnode = { + .name = LATCH2_LABEL, +}; + +static const u32 latch2_hog_gpios[] = { LATCH2_PIN_KEYBRD_DATAOUT, 0 }; + +static const struct property_entry latch2_gpio_hog_props[] = { + PROPERTY_ENTRY_BOOL("gpio-hog"), + PROPERTY_ENTRY_U32_ARRAY("gpios", latch2_hog_gpios), + PROPERTY_ENTRY_STRING("line-name", "keybrd_dataout"), + PROPERTY_ENTRY_BOOL("output-low"), + { } +}; + +static const struct software_node latch2_gpio_hog_swnode = { + .parent = &latch2_gpio_swnode, + .name = "latch2-hog", + .properties = latch2_gpio_hog_props, +}; + +static const struct software_node *const latch2_gpio_swnodes[] = { + &latch2_gpio_swnode, + &latch2_gpio_hog_swnode, + NULL }; static struct plat_serial8250_port ams_delta_modem_ports[]; @@ -684,7 +704,6 @@ static void __init ams_delta_init(void) omap_gpio_deps_init(); ams_delta_latch2_init(); - gpiod_add_hogs(ams_delta_gpio_hogs); omap_serial_init(); omap_register_i2c_bus(1, 100, NULL, 0); @@ -693,6 +712,9 @@ static void __init ams_delta_init(void) platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices)); platform_device_register_full(&latch1_gpio_devinfo); + + software_node_register_node_group(latch2_gpio_swnodes); + latch2_gpio_devinfo.fwnode = software_node_fwnode(&latch2_gpio_swnode); platform_device_register_full(&latch2_gpio_devinfo); /* diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 604c1b4b6d45..6805c19ac8a9 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -217,7 +217,6 @@ config GE_IMP3A config SGY_CTS1000 tristate "Servergy CTS-1000 support" select GPIOLIB - select OF_GPIO depends on CORENET_GENERIC help Enable this to support functionality in Servergy's CTS-1000 systems. diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c index a020e23d7c49..42cb1aef1fe1 100644 --- a/drivers/firmware/arm_scmi/pinctrl.c +++ b/drivers/firmware/arm_scmi/pinctrl.c @@ -578,6 +578,8 @@ static int scmi_pinctrl_request_free(const struct scmi_protocol_handle *ph, tx->flags = cpu_to_le32(type); ret = ph->xops->do_xfer(ph, t); + if (ret == -EOPNOTSUPP) + ret = 0; ph->xops->xfer_put(ph, t); return ret; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b45fb799e36c..c5ede0e4a32a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -95,7 +95,6 @@ config GPIO_CDEV config GPIO_CDEV_V1 bool "Support GPIO ABI Version 1" - default y depends on GPIO_CDEV help Say Y here to support version 1 of the GPIO CDEV ABI. @@ -103,8 +102,6 @@ config GPIO_CDEV_V1 This ABI version is deprecated. Please use the latest ABI for new developments. - If unsure, say Y. - config GPIO_GENERIC depends on HAS_IOMEM # Only for IOMEM drivers tristate @@ -145,7 +142,7 @@ menu "Memory mapped GPIO drivers" config GPIO_74XX_MMIO tristate "GPIO driver for 74xx-ICs with MMIO access" - depends on OF_GPIO + depends on OF select GPIO_GENERIC help Say yes here to support GPIO functionality for 74xx-compatible ICs @@ -175,14 +172,14 @@ config GPIO_AMDPT config GPIO_ASPEED tristate "Aspeed GPIO support" - depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO + depends on ARCH_ASPEED || COMPILE_TEST select GPIOLIB_IRQCHIP help Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers. config GPIO_ASPEED_SGPIO bool "Aspeed SGPIO support" - depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO + depends on ARCH_ASPEED || COMPILE_TEST select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -201,7 +198,6 @@ config GPIO_ATH79 config GPIO_RASPBERRYPI_EXP tristate "Raspberry Pi 3 GPIO Expander" default RASPBERRYPI_FIRMWARE - depends on OF_GPIO # Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only # happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE. depends on (ARCH_BCM2835 && RASPBERRYPI_FIRMWARE) || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE) @@ -218,7 +214,7 @@ config GPIO_BCM_KONA config GPIO_BCM_XGS_IPROC tristate "BRCM XGS iProc GPIO support" - depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST) + depends on ARCH_BCM_IPROC || COMPILE_TEST select GPIO_GENERIC select GPIOLIB_IRQCHIP default ARCH_BCM_IPROC @@ -229,7 +225,6 @@ config GPIO_BLZP1600 tristate "Blaize BLZP1600 GPIO support" default y if ARCH_BLAIZE depends on ARCH_BLAIZE || COMPILE_TEST - depends on OF_GPIO select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -240,15 +235,29 @@ config GPIO_BLZP1600 config GPIO_BRCMSTB tristate "BRCMSTB GPIO support" default y if (ARCH_BRCMSTB || BMIPS_GENERIC) - depends on OF_GPIO && (ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST) + depends on ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST + depends on OF select GPIO_GENERIC select IRQ_DOMAIN help Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs. +config GPIO_BY_PINCTRL + tristate "GPIO support based on a pure pin control backend" + depends on GPIOLIB + help + Support for generic GPIO handling based on top of pin control. + Traditionally, firmware creates a GPIO interface or a pin + controller interface and we have a driver to support it. But + in SCMI, the pin control interface is generic and we can + create a simple GPIO device based on the pin control interface + without doing anything custom. + + This driver is used to access GPIOs over the ARM SCMI protocol. + config GPIO_CADENCE tristate "Cadence GPIO support" - depends on OF_GPIO + depends on OF select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -280,14 +289,13 @@ config GPIO_DWAPB config GPIO_EIC_SPRD tristate "Spreadtrum EIC support" depends on ARCH_SPRD || COMPILE_TEST - depends on OF_GPIO select GPIOLIB_IRQCHIP help Say yes here to support Spreadtrum EIC device. config GPIO_EM tristate "Emma Mobile GPIO" - depends on (ARCH_EMEV2 || COMPILE_TEST) && OF_GPIO + depends on ARCH_EMEV2 || COMPILE_TEST help Say yes here to support GPIO on Renesas Emma Mobile SoCs. @@ -329,7 +337,7 @@ config GPIO_GE_FPGA config GPIO_FTGPIO010 bool "Faraday FTGPIO010 GPIO" - depends on OF_GPIO + depends on OF select GPIO_GENERIC select GPIOLIB_IRQCHIP default (ARCH_GEMINI || ARCH_MOXART) @@ -383,7 +391,7 @@ config GPIO_HISI config GPIO_HLWD tristate "Nintendo Wii (Hollywood) GPIO" - depends on OF_GPIO + depends on OF select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -434,7 +442,6 @@ config GPIO_LOONGSON config GPIO_LOONGSON_64BIT tristate "Loongson 64 bit GPIO support" depends on LOONGARCH || COMPILE_TEST - depends on OF_GPIO select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -447,7 +454,7 @@ config GPIO_LOONGSON_64BIT config GPIO_LPC18XX tristate "NXP LPC18XX/43XX GPIO support" default y if ARCH_LPC18XX - depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST) + depends on ARCH_LPC18XX || COMPILE_TEST select IRQ_DOMAIN_HIERARCHY select GPIOLIB_IRQCHIP help @@ -456,7 +463,8 @@ config GPIO_LPC18XX config GPIO_LPC32XX tristate "NXP LPC32XX GPIO support" - depends on OF_GPIO && (ARCH_LPC32XX || COMPILE_TEST) + depends on ARCH_LPC32XX || COMPILE_TEST + depends on OF help Select this option to enable GPIO driver for NXP LPC32XX devices. @@ -499,7 +507,7 @@ config GPIO_MPC8XXX config GPIO_MT7621 bool "Mediatek MT7621 GPIO Support" depends on SOC_MT7620 || SOC_MT7621 || COMPILE_TEST - depends on OF_GPIO + depends on OF select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -592,7 +600,6 @@ config GPIO_RCAR config GPIO_RDA bool "RDA Micro GPIO controller support" depends on ARCH_RDA || COMPILE_TEST - depends on OF_GPIO select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -641,8 +648,8 @@ config GPIO_RTD config GPIO_SAMA5D2_PIOBU tristate "SAMA5D2 PIOBU GPIO support" + depends on OF depends on MFD_SYSCON - depends on OF_GPIO depends on ARCH_AT91 || COMPILE_TEST select GPIO_SYSCON help @@ -654,7 +661,7 @@ config GPIO_SAMA5D2_PIOBU config GPIO_SIFIVE tristate "SiFive GPIO support" - depends on OF_GPIO + depends on OF select IRQ_DOMAIN_HIERARCHY select GPIO_GENERIC select GPIOLIB_IRQCHIP @@ -673,7 +680,6 @@ config GPIO_SIOX config GPIO_SNPS_CREG bool "Synopsys GPIO via CREG (Control REGisters) driver" depends on ARC || COMPILE_TEST - depends on OF_GPIO help This driver supports GPIOs via CREG on various Synopsys SoCs. This is a single-register MMIO GPIO driver for complex cases @@ -683,7 +689,7 @@ config GPIO_SNPS_CREG config GPIO_SPACEMIT_K1 tristate "SPACEMIT K1 GPIO support" depends on ARCH_SPACEMIT || COMPILE_TEST - depends on OF_GPIO + depends on OF select GPIO_GENERIC select GPIOLIB_IRQCHIP help @@ -699,7 +705,6 @@ config GPIO_SPEAR_SPICS config GPIO_SPRD tristate "Spreadtrum GPIO support" depends on ARCH_SPRD || COMPILE_TEST - depends on OF_GPIO select GPIOLIB_IRQCHIP help Say yes here to support Spreadtrum GPIO device. @@ -707,7 +712,6 @@ config GPIO_SPRD config GPIO_STP_XWAY bool "XWAY STP GPIOs" depends on SOC_XWAY || COMPILE_TEST - depends on OF_GPIO help This enables support for the Serial To Parallel (STP) unit found on XWAY SoC. The STP allows the SoC to drive a shift registers cascade, @@ -742,7 +746,6 @@ config GPIO_TEGRA tristate "NVIDIA Tegra GPIO support" default ARCH_TEGRA depends on ARCH_TEGRA || COMPILE_TEST - depends on OF_GPIO select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY help @@ -752,7 +755,7 @@ config GPIO_TEGRA186 tristate "NVIDIA Tegra186 GPIO support" default ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC depends on ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC || COMPILE_TEST - depends on OF_GPIO + depends on OF select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY help @@ -760,7 +763,6 @@ config GPIO_TEGRA186 config GPIO_TS4800 tristate "TS-4800 DIO blocks and compatibles" - depends on OF_GPIO depends on SOC_IMX51 || COMPILE_TEST select GPIO_GENERIC help @@ -780,7 +782,6 @@ config GPIO_THUNDERX config GPIO_UNIPHIER tristate "UniPhier GPIO support" depends on ARCH_UNIPHIER || COMPILE_TEST - depends on OF_GPIO select IRQ_DOMAIN_HIERARCHY help Say yes here to support UniPhier GPIOs. @@ -797,7 +798,6 @@ config GPIO_VF610 config GPIO_VISCONTI tristate "Toshiba Visconti GPIO support" depends on ARCH_VISCONTI || COMPILE_TEST - depends on OF_GPIO select GPIOLIB_IRQCHIP select GPIO_GENERIC select IRQ_DOMAIN_HIERARCHY @@ -806,14 +806,14 @@ config GPIO_VISCONTI config GPIO_WCD934X tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" - depends on MFD_WCD934X && OF_GPIO + depends on MFD_WCD934X help This driver is to support GPIO block found on the Qualcomm Technologies Inc WCD9340/WCD9341 Audio Codec. config GPIO_XGENE bool "APM X-Gene GPIO controller support" - depends on ARM64 && OF_GPIO + depends on ARM64 help This driver is to support the GPIO block within the APM X-Gene SoC platform's generic flash controller. The GPIO pins are muxed with @@ -1057,6 +1057,32 @@ config GPIO_SCH The Intel Quark X1000 SoC has 2 GPIOs powered by the core power well and 6 from the suspend power well. +config GPIO_NOVALAKE + tristate "Intel Nova Lake GPIO-signaled ACPI events support" + depends on (X86 || COMPILE_TEST) && ACPI + select GPIOLIB_IRQCHIP + help + Select this to enable GPIO-signaled ACPI events support on platforms + with the following SoCs: + + - Intel Nova Lake + + This driver adds support for new mode of handling platform events, + through the use of GPIO-signaled ACPI events. Main purpose is to + handle platform IRQs that originate in PCH components, for example + interrupt triggered by Power Management Event (PME). + + This driver, at this time, is not required to handle platform events. + Listed platform(s) will stay in legacy mode, handling ACPI events as + in previous generations. However, future platforms will eventually + switch to new handling mode, requiring this driver to run events + properly. + + Driver supports up to 128 GPIO pins per GPE block. + + To compile this driver as a module, choose M here: the module will + be called gpio-novalake-events. + config GPIO_SCH311X tristate "SMSC SCH311x SuperI/O GPIO" help @@ -1111,7 +1137,7 @@ menu "I2C GPIO expanders" config GPIO_ADNP tristate "Avionic Design N-bit GPIO expander" - depends on OF_GPIO + depends on OF select GPIOLIB_IRQCHIP help This option enables support for N GPIOs found on Avionic Design @@ -1144,7 +1170,7 @@ config GPIO_DS4520 config GPIO_GW_PLD tristate "Gateworks PLD GPIO Expander" - depends on OF_GPIO + depends on OF help Say yes here to provide access to the Gateworks I2C PLD GPIO Expander. This is used at least on the Cambria GW2358-4. @@ -1440,6 +1466,7 @@ config GPIO_JANZ_TTL config GPIO_KEMPLD tristate "Kontron ETX / COMexpress GPIO" depends on MFD_KEMPLD + select GPIOLIB_IRQCHIP help This enables support for the PLD GPIO interface on some Kontron ETX and COMexpress (ETXexpress) modules. @@ -1567,7 +1594,6 @@ config GPIO_PALMAS config GPIO_PMIC_EIC_SPRD tristate "Spreadtrum PMIC EIC support" depends on MFD_SC27XX_PMIC || COMPILE_TEST - depends on OF_GPIO select GPIOLIB_IRQCHIP help Say yes here to support Spreadtrum PMIC EIC device. @@ -1606,7 +1632,6 @@ config GPIO_SL28CPLD config GPIO_STMPE tristate "STMPE GPIOs" depends on MFD_STMPE - depends on OF_GPIO select GPIOLIB_IRQCHIP help This enables support for the GPIOs found on the STMPE I/O @@ -1615,7 +1640,6 @@ config GPIO_STMPE config GPIO_TC3589X bool "TC3589X GPIOs" depends on MFD_TC3589X - depends on OF_GPIO select GPIOLIB_IRQCHIP help This enables support for the GPIOs found on the TC3589X @@ -1986,7 +2010,6 @@ menu "Virtual GPIO drivers" config GPIO_AGGREGATOR tristate "GPIO Aggregator" select CONFIGFS_FS - select DEV_SYNC_PROBE help Say yes here to enable the GPIO Aggregator, which provides a way to aggregate existing GPIO lines into a new virtual GPIO chip. @@ -2005,7 +2028,7 @@ config GPIO_LATCH config GPIO_LINE_MUX tristate "GPIO line mux driver" - depends on OF_GPIO + depends on OF select MULTIPLEXER help Say Y here to support the GPIO line mux, which can provide virtual @@ -2038,7 +2061,6 @@ config GPIO_SIM tristate "GPIO Simulator Module" select IRQ_SIM select CONFIGFS_FS - select DEV_SYNC_PROBE help This enables the GPIO simulator - a configfs-based GPIO testing driver. @@ -2076,7 +2098,6 @@ config GPIO_VIRTUSER select DEBUG_FS select CONFIGFS_FS select IRQ_WORK - select DEV_SYNC_PROBE help Say Y here to enable the configurable, configfs-based virtual GPIO consumer testing driver. @@ -2087,6 +2108,3 @@ config GPIO_VIRTUSER endmenu endif - -config DEV_SYNC_PROBE - tristate diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c05f7d795c43..b267598b517d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -21,9 +21,6 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o # directly supported by gpio-generic gpio-generic-$(CONFIG_GPIO_GENERIC) += gpio-mmio.o -# Utilities for drivers that need synchronous fake device creation -obj-$(CONFIG_DEV_SYNC_PROBE) += dev-sync-probe.o - obj-$(CONFIG_GPIO_104_DIO_48E) += gpio-104-dio-48e.o obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o @@ -51,6 +48,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o +obj-$(CONFIG_GPIO_BY_PINCTRL) += gpio-by-pinctrl.o obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o obj-$(CONFIG_GPIO_CGBC) += gpio-cgbc.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o @@ -134,6 +132,7 @@ obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o obj-$(CONFIG_GPIO_NCT6694) += gpio-nct6694.o obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o +obj-$(CONFIG_GPIO_NOVALAKE) += gpio-novalake-events.o obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 5acaeab029ec..7ce80fde1f17 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -58,34 +58,6 @@ Work items: ------------------------------------------------------------------------------- -Get rid of - -This header and helpers appeared at one point when there was no proper -driver infrastructure for doing simpler MMIO GPIO devices and there was -no core support for parsing device tree GPIOs from the core library with -the [devm_]gpiod_get() calls we have today that will implicitly go into -the device tree back-end. It is legacy and should not be used in new code. - -Work items: - -- Change all consumer drivers that #include to - #include and stop doing custom parsing of the - GPIO lines from the device tree. This can be tricky and often involves - changing board files, etc. - -- Pull semantics for legacy device tree (OF) GPIO lookups into - gpiolib-of.c: in some cases subsystems are doing custom flags and - lookups for polarity inversion, open drain and what not. As we now - handle this with generic OF bindings, pull all legacy handling into - gpiolib so the library API becomes narrow and deep and handle all - legacy bindings internally. (See e.g. commits 6953c57ab172, - 6a537d48461d etc) - -- Delete when all the above is complete and everything - uses or instead. - -------------------------------------------------------------------------------- - Collect drivers Collect GPIO drivers from arch/* and other places that should be placed diff --git a/drivers/gpio/dev-sync-probe.c b/drivers/gpio/dev-sync-probe.c deleted file mode 100644 index 9ea733b863b2..000000000000 --- a/drivers/gpio/dev-sync-probe.c +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Common code for drivers creating fake platform devices. - * - * Provides synchronous device creation: waits for probe completion and - * returns the probe success or error status to the device creator. - * - * Copyright (C) 2021 Bartosz Golaszewski - * Copyright (C) 2025 Koichiro Den - */ - -#include -#include - -#include "dev-sync-probe.h" - -static int dev_sync_probe_notifier_call(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct dev_sync_probe_data *pdata; - struct device *dev = data; - - pdata = container_of(nb, struct dev_sync_probe_data, bus_notifier); - if (!device_match_name(dev, pdata->name)) - return NOTIFY_DONE; - - switch (action) { - case BUS_NOTIFY_BOUND_DRIVER: - pdata->driver_bound = true; - break; - case BUS_NOTIFY_DRIVER_NOT_BOUND: - pdata->driver_bound = false; - break; - default: - return NOTIFY_DONE; - } - - complete(&pdata->probe_completion); - return NOTIFY_OK; -} - -void dev_sync_probe_init(struct dev_sync_probe_data *data) -{ - memset(data, 0, sizeof(*data)); - init_completion(&data->probe_completion); - data->bus_notifier.notifier_call = dev_sync_probe_notifier_call; -} -EXPORT_SYMBOL_GPL(dev_sync_probe_init); - -int dev_sync_probe_register(struct dev_sync_probe_data *data, - struct platform_device_info *pdevinfo) -{ - struct platform_device *pdev; - char *name; - - name = kasprintf(GFP_KERNEL, "%s.%d", pdevinfo->name, pdevinfo->id); - if (!name) - return -ENOMEM; - - data->driver_bound = false; - data->name = name; - reinit_completion(&data->probe_completion); - bus_register_notifier(&platform_bus_type, &data->bus_notifier); - - pdev = platform_device_register_full(pdevinfo); - if (IS_ERR(pdev)) { - bus_unregister_notifier(&platform_bus_type, &data->bus_notifier); - kfree(data->name); - return PTR_ERR(pdev); - } - - wait_for_completion(&data->probe_completion); - bus_unregister_notifier(&platform_bus_type, &data->bus_notifier); - - if (!data->driver_bound) { - platform_device_unregister(pdev); - kfree(data->name); - return -ENXIO; - } - - data->pdev = pdev; - return 0; -} -EXPORT_SYMBOL_GPL(dev_sync_probe_register); - -void dev_sync_probe_unregister(struct dev_sync_probe_data *data) -{ - platform_device_unregister(data->pdev); - kfree(data->name); - data->pdev = NULL; -} -EXPORT_SYMBOL_GPL(dev_sync_probe_unregister); - -MODULE_AUTHOR("Bartosz Golaszewski "); -MODULE_AUTHOR("Koichiro Den "); -MODULE_DESCRIPTION("Utilities for synchronous fake device creation"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/dev-sync-probe.h b/drivers/gpio/dev-sync-probe.h deleted file mode 100644 index 4b3d52b70519..000000000000 --- a/drivers/gpio/dev-sync-probe.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef DEV_SYNC_PROBE_H -#define DEV_SYNC_PROBE_H - -#include -#include -#include - -struct dev_sync_probe_data { - struct platform_device *pdev; - const char *name; - - /* Synchronize with probe */ - struct notifier_block bus_notifier; - struct completion probe_completion; - bool driver_bound; -}; - -void dev_sync_probe_init(struct dev_sync_probe_data *data); -int dev_sync_probe_register(struct dev_sync_probe_data *data, - struct platform_device_info *pdevinfo); -void dev_sync_probe_unregister(struct dev_sync_probe_data *data); - -#endif /* DEV_SYNC_PROBE_H */ diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 9adf3228c12a..5915209e1e21 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -32,8 +32,6 @@ #include #include -#include "dev-sync-probe.h" - #define AGGREGATOR_MAX_GPIOS 512 #define AGGREGATOR_LEGACY_PREFIX "_sysfs" @@ -42,7 +40,7 @@ */ struct gpio_aggregator { - struct dev_sync_probe_data probe_data; + struct platform_device *pdev; struct config_group group; struct gpiod_lookup_table *lookups; struct mutex lock; @@ -135,7 +133,7 @@ static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr) { lockdep_assert_held(&aggr->lock); - return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev); + return aggr->pdev && platform_get_drvdata(aggr->pdev); } /* Only aggregators created via legacy sysfs can be "activating". */ @@ -143,7 +141,7 @@ static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr) { lockdep_assert_held(&aggr->lock); - return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev); + return aggr->pdev && !platform_get_drvdata(aggr->pdev); } static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) @@ -909,6 +907,7 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) { struct platform_device_info pdevinfo; struct gpio_aggregator_line *line; + struct platform_device *pdev; struct fwnode_handle *swnode; unsigned int n = 0; int ret = 0; @@ -962,12 +961,23 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) gpiod_add_lookup_table(aggr->lookups); - ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo); - if (ret) + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); goto err_remove_lookup_table; + } + wait_for_device_probe(); + if (!device_is_bound(&pdev->dev)) { + ret = -ENXIO; + goto err_unregister_pdev; + } + + aggr->pdev = pdev; return 0; +err_unregister_pdev: + platform_device_unregister(pdev); err_remove_lookup_table: kfree(aggr->lookups->dev_id); gpiod_remove_lookup_table(aggr->lookups); @@ -981,7 +991,8 @@ err_remove_lookups: static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) { - dev_sync_probe_unregister(&aggr->probe_data); + platform_device_unregister(aggr->pdev); + aggr->pdev = NULL; gpiod_remove_lookup_table(aggr->lookups); kfree(aggr->lookups->dev_id); kfree(aggr->lookups); @@ -1145,7 +1156,7 @@ gpio_aggregator_device_dev_name_show(struct config_item *item, char *page) guard(mutex)(&aggr->lock); - pdev = aggr->probe_data.pdev; + pdev = aggr->pdev; if (pdev) return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); @@ -1322,7 +1333,6 @@ gpio_aggregator_make_group(struct config_group *group, const char *name) return ERR_PTR(ret); config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); - dev_sync_probe_init(&aggr->probe_data); return &aggr->group; } @@ -1471,12 +1481,6 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id); config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); - /* - * Since the device created by sysfs might be toggled via configfs - * 'live' attribute later, this initialization is needed. - */ - dev_sync_probe_init(&aggr->probe_data); - /* Expose to configfs */ res = configfs_register_group(&gpio_aggregator_subsys.su_group, &aggr->group); @@ -1495,7 +1499,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, goto remove_table; } - aggr->probe_data.pdev = pdev; + aggr->pdev = pdev; module_put(THIS_MODULE); return count; diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 9115e56a1626..e6af7f3fba5e 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -655,7 +655,7 @@ static void aspeed_init_irq_valid_mask(struct gpio_chip *gc, while (!is_bank_props_sentinel(props)) { unsigned int offset; - const unsigned long int input = props->input; + const unsigned long input = props->input; /* Pretty crummy approach, but similar to GPIO core */ for_each_clear_bit(offset, &input, 32) { diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 208b71c59d58..b1d32d590cf8 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -58,15 +58,6 @@ #define LOCK_CODE 0xffffffff #define UNLOCK_CODE 0x00000000 -struct bcm_kona_gpio { - void __iomem *reg_base; - int num_bank; - raw_spinlock_t lock; - struct gpio_chip gpio_chip; - struct irq_domain *irq_domain; - struct bcm_kona_gpio_bank *banks; -}; - struct bcm_kona_gpio_bank { int id; int irq; @@ -90,6 +81,15 @@ struct bcm_kona_gpio_bank { struct bcm_kona_gpio *kona_gpio; }; +struct bcm_kona_gpio { + void __iomem *reg_base; + int num_bank; + raw_spinlock_t lock; + struct gpio_chip gpio_chip; + struct irq_domain *irq_domain; + struct bcm_kona_gpio_bank banks[] __counted_by(num_bank); +}; + static inline void bcm_kona_gpio_write_lock_regs(void __iomem *reg_base, int bank_id, u32 lockcode) { @@ -584,12 +584,6 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) int ret; int i; - kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL); - if (!kona_gpio) - return -ENOMEM; - - kona_gpio->gpio_chip = template_chip; - chip = &kona_gpio->gpio_chip; ret = platform_irq_count(pdev); if (!ret) { dev_err(dev, "Couldn't determine # GPIO banks\n"); @@ -597,6 +591,11 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) } else if (ret < 0) { return dev_err_probe(dev, ret, "Couldn't determine GPIO banks\n"); } + + kona_gpio = devm_kzalloc(dev, struct_size(kona_gpio, banks, ret), GFP_KERNEL); + if (!kona_gpio) + return -ENOMEM; + kona_gpio->num_bank = ret; if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) { @@ -604,13 +603,9 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) GPIO_MAX_BANK_NUM); return -ENXIO; } - kona_gpio->banks = devm_kcalloc(dev, - kona_gpio->num_bank, - sizeof(*kona_gpio->banks), - GFP_KERNEL); - if (!kona_gpio->banks) - return -ENOMEM; + kona_gpio->gpio_chip = template_chip; + chip = &kona_gpio->gpio_chip; chip->parent = dev; chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK; diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index 7c95bb36511e..cc5b1746f2fe 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -69,7 +69,7 @@ static int bd9571mwv_gpio_get(struct gpio_chip *chip, unsigned int offset) if (ret < 0) return ret; - return val & BIT(offset); + return !!(val & BIT(offset)); } static int bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 2352d099709c..44ca798cf832 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -// Copyright (C) 2015-2017 Broadcom +// Copyright (C) 2015-2017, 2026 Broadcom #include #include @@ -54,6 +54,7 @@ struct brcmstb_gpio_priv { int parent_irq; int num_gpios; int parent_wake_irq; + bool suspended; }; #define MAX_GPIO_PER_BANK 32 @@ -95,15 +96,13 @@ static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq, return hwirq - bank->chip.gc.offset; } -static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, - unsigned int hwirq, bool enable) +static void __brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, + irq_hw_number_t hwirq, bool enable) { struct brcmstb_gpio_priv *priv = bank->parent_priv; u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank)); u32 imask; - guard(gpio_generic_lock_irqsave)(&bank->chip); - imask = gpio_generic_read_reg(&bank->chip, priv->reg_base + GIO_MASK(bank->id)); if (enable) @@ -114,6 +113,13 @@ static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, priv->reg_base + GIO_MASK(bank->id), imask); } +static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, + irq_hw_number_t hwirq, bool enable) +{ + guard(gpio_generic_lock_irqsave)(&bank->chip); + __brcmstb_gpio_set_imask(bank, hwirq, enable); +} + static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); @@ -132,7 +138,21 @@ static void brcmstb_gpio_irq_mask(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); - brcmstb_gpio_set_imask(bank, d->hwirq, false); + brcmstb_gpio_set_imask(bank, irqd_to_hwirq(d), false); +} + +static void brcmstb_gpio_irq_mask_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); + struct brcmstb_gpio_priv *priv = bank->parent_priv; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank)); + + guard(gpio_generic_lock_irqsave)(&bank->chip); + __brcmstb_gpio_set_imask(bank, hwirq, false); + gpio_generic_write_reg(&bank->chip, + priv->reg_base + GIO_STAT(bank->id), mask); } static void brcmstb_gpio_irq_unmask(struct irq_data *d) @@ -140,7 +160,7 @@ static void brcmstb_gpio_irq_unmask(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); - brcmstb_gpio_set_imask(bank, d->hwirq, true); + brcmstb_gpio_set_imask(bank, irqd_to_hwirq(d), true); } static void brcmstb_gpio_irq_ack(struct irq_data *d) @@ -148,7 +168,7 @@ static void brcmstb_gpio_irq_ack(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); struct brcmstb_gpio_priv *priv = bank->parent_priv; - u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); + u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank)); gpio_generic_write_reg(&bank->chip, priv->reg_base + GIO_STAT(bank->id), mask); @@ -159,7 +179,7 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); struct brcmstb_gpio_priv *priv = bank->parent_priv; - u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); + u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank)); u32 edge_insensitive, iedge_insensitive; u32 edge_config, iedge_config; u32 level, ilevel; @@ -221,6 +241,9 @@ static int brcmstb_gpio_priv_set_wake(struct brcmstb_gpio_priv *priv, { int ret = 0; + if (priv->parent_wake_irq == priv->parent_irq) + return ret; + if (enable) ret = enable_irq_wake(priv->parent_wake_irq); else @@ -236,7 +259,7 @@ static int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); struct brcmstb_gpio_priv *priv = bank->parent_priv; - u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); + u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank)); /* * Do not do anything specific for now, suspend/resume callbacks will @@ -271,6 +294,11 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) while ((status = brcmstb_gpio_get_active_irqs(bank))) { unsigned int offset; + if (priv->suspended && bank->wake_active & status) { + priv->suspended = false; + pm_wakeup_event(&priv->pdev->dev, 0); + } + for_each_set_bit(offset, &status, 32) { if (offset >= bank->width) dev_warn(&priv->pdev->dev, @@ -444,18 +472,18 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, } if (of_property_read_bool(np, "wakeup-source")) { + /* + * Set wakeup capability so we can process boot-time + * "wakeups" (e.g., from S5 cold boot). + */ + device_set_wakeup_capable(dev, true); + device_wakeup_enable(dev); priv->parent_wake_irq = platform_get_irq(pdev, 1); if (priv->parent_wake_irq < 0) { - priv->parent_wake_irq = 0; + priv->parent_wake_irq = priv->parent_irq; dev_warn(dev, "Couldn't get wake IRQ - GPIOs will not be able to wake from sleep"); } else { - /* - * Set wakeup capability so we can process boot-time - * "wakeups" (e.g., from S5 cold boot) - */ - device_set_wakeup_capable(dev, true); - device_wakeup_enable(dev); err = devm_request_irq(dev, priv->parent_wake_irq, brcmstb_gpio_wake_irq_handler, IRQF_SHARED, @@ -466,18 +494,17 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, goto out_free_domain; } } + priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake; } priv->irq_chip.name = dev_name(dev); priv->irq_chip.irq_disable = brcmstb_gpio_irq_mask; priv->irq_chip.irq_mask = brcmstb_gpio_irq_mask; + priv->irq_chip.irq_mask_ack = brcmstb_gpio_irq_mask_ack; priv->irq_chip.irq_unmask = brcmstb_gpio_irq_unmask; priv->irq_chip.irq_ack = brcmstb_gpio_irq_ack; priv->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type; - if (priv->parent_wake_irq) - priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake; - irq_set_chained_handler_and_data(priv->parent_irq, brcmstb_gpio_irq_handler, priv); irq_set_status_flags(priv->parent_irq, IRQ_DISABLE_UNLAZY); @@ -500,16 +527,11 @@ static void brcmstb_gpio_bank_save(struct brcmstb_gpio_priv *priv, priv->reg_base + GIO_BANK_OFF(bank->id, i)); } -static void brcmstb_gpio_quiesce(struct device *dev, bool save) +static void brcmstb_gpio_quiesce(struct brcmstb_gpio_priv *priv, bool save) { - struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); struct brcmstb_gpio_bank *bank; u32 imask; - /* disable non-wake interrupt */ - if (priv->parent_irq >= 0) - disable_irq(priv->parent_irq); - list_for_each_entry(bank, &priv->bank_list, node) { if (save) brcmstb_gpio_bank_save(priv, bank); @@ -527,8 +549,13 @@ static void brcmstb_gpio_quiesce(struct device *dev, bool save) static void brcmstb_gpio_shutdown(struct platform_device *pdev) { + struct brcmstb_gpio_priv *priv = dev_get_drvdata(&pdev->dev); + + if (priv->parent_irq > 0) + disable_irq(priv->parent_irq); + /* Enable GPIO for S5 cold boot */ - brcmstb_gpio_quiesce(&pdev->dev, false); + brcmstb_gpio_quiesce(priv, false); } static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv, @@ -544,7 +571,30 @@ static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv, static int brcmstb_gpio_suspend(struct device *dev) { - brcmstb_gpio_quiesce(dev, true); + struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); + + if (priv->parent_irq > 0) + priv->suspended = true; + + return 0; +} + +static int brcmstb_gpio_suspend_noirq(struct device *dev) +{ + struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); + + /* Catch any wakeup sources occurring between suspend and noirq */ + if (!priv->suspended) + return -EBUSY; + + if (priv->parent_irq > 0) + disable_irq(priv->parent_irq); + + brcmstb_gpio_quiesce(priv, true); + + if (priv->parent_wake_irq) + enable_irq(priv->parent_irq); + return 0; } @@ -552,25 +602,24 @@ static int brcmstb_gpio_resume(struct device *dev) { struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); struct brcmstb_gpio_bank *bank; - bool need_wakeup_event = false; - list_for_each_entry(bank, &priv->bank_list, node) { - need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank); + if (priv->parent_wake_irq) + disable_irq(priv->parent_irq); + + priv->suspended = false; + + list_for_each_entry(bank, &priv->bank_list, node) brcmstb_gpio_bank_restore(priv, bank); - } - if (priv->parent_wake_irq && need_wakeup_event) - pm_wakeup_event(dev, 0); - - /* enable non-wake interrupt */ - if (priv->parent_irq >= 0) + if (priv->parent_irq > 0) enable_irq(priv->parent_irq); return 0; } static const struct dev_pm_ops brcmstb_gpio_pm_ops = { - .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend), + .suspend = pm_sleep_ptr(brcmstb_gpio_suspend), + .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend_noirq), .resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume), }; diff --git a/drivers/gpio/gpio-by-pinctrl.c b/drivers/gpio/gpio-by-pinctrl.c new file mode 100644 index 000000000000..ddfdc479d38a --- /dev/null +++ b/drivers/gpio/gpio-by-pinctrl.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2026 Linaro Inc. +// Author: AKASHI takahiro + +#include +#include +#include +#include +#include +#include +#include + +#include "gpiolib.h" + +static int pin_control_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long config; + int ret; + + config = PIN_CONFIG_OUTPUT_ENABLE; + ret = pinctrl_gpio_get_config(gc, offset, &config); + if (ret) + return ret; + if (config) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int pin_control_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int val) +{ + return pinctrl_gpio_direction_output(chip, offset); +} + +static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + unsigned long config; + int ret; + + config = PIN_CONFIG_LEVEL; + ret = pinctrl_gpio_get_config(chip, offset, &config); + if (ret) + return ret; + + return !!config; +} + +static int pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) +{ + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_LEVEL, val); + return pinctrl_gpio_set_config(chip, offset, config); +} + +static int pin_control_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_chip *chip; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->label = dev_name(dev); + chip->parent = dev; + chip->base = -1; + + chip->request = gpiochip_generic_request; + chip->free = gpiochip_generic_free; + chip->get_direction = pin_control_gpio_get_direction; + chip->direction_input = pinctrl_gpio_direction_input; + chip->direction_output = pin_control_gpio_direction_output; + chip->get = pin_control_gpio_get; + chip->set = pin_control_gpio_set; + chip->set_config = gpiochip_generic_config; + + return devm_gpiochip_add_data(dev, chip, NULL); +} + +static const struct of_device_id pin_control_gpio_match[] = { + { .compatible = "scmi-pinctrl-gpio" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pin_control_gpio_match); + +static struct platform_driver pin_control_gpio_driver = { + .probe = pin_control_gpio_probe, + .driver = { + .name = "pin-control-gpio", + .of_match_table = pin_control_gpio_match, + }, +}; +module_platform_driver(pin_control_gpio_driver); + +MODULE_AUTHOR("AKASHI Takahiro "); +MODULE_DESCRIPTION("Pinctrl based GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c index 0efa1b61001a..84b5ed3c6e22 100644 --- a/drivers/gpio/gpio-cgbc.c +++ b/drivers/gpio/gpio-cgbc.c @@ -47,8 +47,8 @@ static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset) if (ret) return ret; - else - return (int)(val & (u8)BIT(offset)); + + return !!(val & BIT(offset)); } static int __cgbc_gpio_set(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c index 8affe4e9f90e..5f5373d86397 100644 --- a/drivers/gpio/gpio-cs5535.c +++ b/drivers/gpio/gpio-cs5535.c @@ -39,10 +39,6 @@ static ulong mask = GPIO_DEFAULT_MASK; module_param_named(mask, mask, ulong, 0444); MODULE_PARM_DESC(mask, "GPIO channel mask."); -/* - * FIXME: convert this singleton driver to use the state container - * design pattern, see Documentation/driver-api/driver-model/design-patterns.rst - */ static struct cs5535_gpio_chip { struct gpio_chip chip; resource_size_t base; @@ -285,30 +281,29 @@ static const char * const cs5535_gpio_names[] = { "GPIO28", NULL, NULL, NULL, }; -static struct cs5535_gpio_chip cs5535_gpio_chip = { - .chip = { - .owner = THIS_MODULE, - .label = DRV_NAME, - - .base = 0, - .ngpio = 32, - .names = cs5535_gpio_names, - .request = chip_gpio_request, - - .get = chip_gpio_get, - .set = chip_gpio_set, - - .direction_input = chip_direction_input, - .direction_output = chip_direction_output, - }, -}; - static int cs5535_gpio_probe(struct platform_device *pdev) { + struct cs5535_gpio_chip *priv; + struct gpio_chip *gc; struct resource *res; int err = -EIO; ulong mask_orig = mask; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + gc = &priv->chip; + gc->owner = THIS_MODULE; + gc->label = DRV_NAME; + gc->ngpio = 32; + gc->names = cs5535_gpio_names; + gc->request = chip_gpio_request; + gc->get = chip_gpio_get; + gc->set = chip_gpio_set; + gc->direction_input = chip_direction_input; + gc->direction_output = chip_direction_output; + /* There are two ways to get the GPIO base address; one is by * fetching it from MSR_LBAR_GPIO, the other is by reading the * PCI BAR info. The latter method is easier (especially across @@ -329,9 +324,9 @@ static int cs5535_gpio_probe(struct platform_device *pdev) } /* set up the driver-specific struct */ - cs5535_gpio_chip.base = res->start; - cs5535_gpio_chip.pdev = pdev; - spin_lock_init(&cs5535_gpio_chip.lock); + priv->base = res->start; + priv->pdev = pdev; + spin_lock_init(&priv->lock); dev_info(&pdev->dev, "reserved resource region %pR\n", res); @@ -347,8 +342,7 @@ static int cs5535_gpio_probe(struct platform_device *pdev) mask_orig, mask); /* finally, register with the generic GPIO API */ - return devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip, - &cs5535_gpio_chip); + return devm_gpiochip_add_data(&pdev->dev, gc, priv); } static struct platform_driver cs5535_gpio_driver = { diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c index a09bd6eb93cf..1949a6ea8ec6 100644 --- a/drivers/gpio/gpio-da9055.c +++ b/drivers/gpio/gpio-da9055.c @@ -55,7 +55,7 @@ static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset) return ret; } - return ret & (1 << offset); + return !!(ret & (1 << offset)); } diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 4986c465c9a8..15cebc8b5d66 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -75,8 +75,8 @@ struct dwapb_port_property { }; struct dwapb_platform_data { - struct dwapb_port_property *properties; unsigned int nports; + struct dwapb_port_property properties[] __counted_by(nports); }; /* Store GPIO context across system-wide suspend/resume transitions */ @@ -114,11 +114,11 @@ static inline struct dwapb_gpio *to_dwapb_gpio(struct gpio_chip *gc) struct dwapb_gpio { struct device *dev; void __iomem *regs; - struct dwapb_gpio_port *ports; unsigned int nr_ports; unsigned int flags; struct reset_control *rst; struct clk_bulk_data clks[DWAPB_NR_CLOCKS]; + struct dwapb_gpio_port ports[] __counted_by(nr_ports); }; static inline u32 gpio_reg_v2_convert(unsigned int offset) @@ -585,14 +585,10 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev) if (nports == 0) return ERR_PTR(-ENODEV); - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, struct_size(pdata, properties, nports), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); - pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL); - if (!pdata->properties) - return ERR_PTR(-ENOMEM); - pdata->nports = nports; i = 0; @@ -714,22 +710,17 @@ static int dwapb_gpio_probe(struct platform_device *pdev) if (IS_ERR(pdata)) return PTR_ERR(pdata); - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, ports, pdata->nports), GFP_KERNEL); if (!gpio) return -ENOMEM; - gpio->dev = &pdev->dev; gpio->nr_ports = pdata->nports; + gpio->dev = &pdev->dev; err = dwapb_get_reset(gpio); if (err) return err; - gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports, - sizeof(*gpio->ports), GFP_KERNEL); - if (!gpio->ports) - return -ENOMEM; - gpio->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gpio->regs)) return PTR_ERR(gpio->regs); diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 72935d6dbebf..d15423c718d0 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -46,8 +46,8 @@ struct egpio_info { uint chained_irq; /* egpio info */ - struct egpio_chip *chip; int nchips; + struct egpio_chip chip[] __counted_by(nchips); }; static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg) @@ -270,10 +270,12 @@ static int __init egpio_probe(struct platform_device *pdev) int i; /* Initialize ei data structure. */ - ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL); + ei = devm_kzalloc(&pdev->dev, struct_size(ei, chip, pdata->num_chips), GFP_KERNEL); if (!ei) return -ENOMEM; + ei->nchips = pdata->num_chips; + spin_lock_init(&ei->lock); /* Find chained irq */ @@ -302,13 +304,6 @@ static int __init egpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ei); - ei->nchips = pdata->num_chips; - ei->chip = devm_kcalloc(&pdev->dev, - ei->nchips, sizeof(struct egpio_chip), - GFP_KERNEL); - if (!ei->chip) - return -ENOMEM; - for (i = 0; i < ei->nchips; i++) { ei->chip[i].reg_start = pdata->chip[i].reg_start; ei->chip[i].cached_values = pdata->chip[i].initial_values; diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c index 923aad3ab4d4..5a63df3ea5fa 100644 --- a/drivers/gpio/gpio-kempld.c +++ b/drivers/gpio/gpio-kempld.c @@ -11,20 +11,35 @@ #include #include #include +#include #include #include #include #define KEMPLD_GPIO_MAX_NUM 16 #define KEMPLD_GPIO_MASK(x) (BIT((x) % 8)) -#define KEMPLD_GPIO_DIR_NUM(x) (0x40 + (x) / 8) -#define KEMPLD_GPIO_LVL_NUM(x) (0x42 + (x) / 8) +#define KEMPLD_GPIO_DIR 0x40 +#define KEMPLD_GPIO_LVL 0x42 +#define KEMPLD_GPIO_STS 0x44 #define KEMPLD_GPIO_EVT_LVL_EDGE 0x46 +#define KEMPLD_GPIO_EVT_LOW_HIGH 0x48 #define KEMPLD_GPIO_IEN 0x4A +#define KEMPLD_GPIO_OUT_LVL 0x4E + +/* The IRQ to use if none was configured in the BIOS */ +static unsigned int gpio_irq; +module_param_hw(gpio_irq, uint, irq, 0444); +MODULE_PARM_DESC(gpio_irq, "Set legacy GPIO IRQ (1-15)"); struct kempld_gpio_data { struct gpio_chip chip; struct kempld_device_data *pld; + u8 out_lvl_reg; + + struct mutex irq_lock; + u16 ien; + u16 evt_low_high; + u16 evt_lvl_edge; }; /* @@ -32,24 +47,25 @@ struct kempld_gpio_data { * kempld_get_mutex must be called prior to calling this function. */ static void kempld_gpio_bitop(struct kempld_device_data *pld, - u8 reg, u8 bit, u8 val) + u8 reg, unsigned int bit, bool val) { u8 status; - status = kempld_read8(pld, reg); + status = kempld_read8(pld, reg + (bit / 8)); if (val) status |= KEMPLD_GPIO_MASK(bit); else status &= ~KEMPLD_GPIO_MASK(bit); - kempld_write8(pld, reg, status); + kempld_write8(pld, reg + (bit / 8), status); } -static int kempld_gpio_get_bit(struct kempld_device_data *pld, u8 reg, u8 bit) +static int kempld_gpio_get_bit(struct kempld_device_data *pld, + u8 reg, unsigned int bit) { u8 status; kempld_get_mutex(pld); - status = kempld_read8(pld, reg); + status = kempld_read8(pld, reg + (bit / 8)); kempld_release_mutex(pld); return !!(status & KEMPLD_GPIO_MASK(bit)); @@ -60,7 +76,34 @@ static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset) struct kempld_gpio_data *gpio = gpiochip_get_data(chip); struct kempld_device_data *pld = gpio->pld; - return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset), offset); + return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL, offset); +} + +static int kempld_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); + struct kempld_device_data *pld = gpio->pld; + u8 reg = KEMPLD_GPIO_LVL; + unsigned int shift; + + bits[0] &= ~mask[0]; + + kempld_get_mutex(pld); + + /* Try to reduce to a single 8 bits access if possible */ + for (shift = 0; shift < gpio->chip.ngpio; shift += 8, reg++) { + unsigned long msk = (mask[0] >> shift) & 0xff; + + if (!msk) + continue; + + bits[0] |= (kempld_read8(pld, reg) & msk) << shift; + } + + kempld_release_mutex(pld); + + return 0; } static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset, @@ -70,7 +113,38 @@ static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset, struct kempld_device_data *pld = gpio->pld; kempld_get_mutex(pld); - kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value); + kempld_gpio_bitop(pld, gpio->out_lvl_reg, offset, value); + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); + struct kempld_device_data *pld = gpio->pld; + u8 reg = gpio->out_lvl_reg; + unsigned int shift; + + kempld_get_mutex(pld); + + /* Try to reduce to a single 8 bits access if possible */ + for (shift = 0; shift < gpio->chip.ngpio; shift += 8, reg++) { + u8 val, msk = mask[0] >> shift; + + if (!msk) + continue; + + if (msk != 0xFF) + val = kempld_read8(pld, reg) & ~msk; + else + val = 0; + + val |= (bits[0] >> shift) & msk; + kempld_write8(pld, reg, val); + } + kempld_release_mutex(pld); return 0; @@ -82,7 +156,7 @@ static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset) struct kempld_device_data *pld = gpio->pld; kempld_get_mutex(pld); - kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 0); + kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR, offset, 0); kempld_release_mutex(pld); return 0; @@ -95,8 +169,8 @@ static int kempld_gpio_direction_output(struct gpio_chip *chip, unsigned offset, struct kempld_device_data *pld = gpio->pld; kempld_get_mutex(pld); - kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value); - kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 1); + kempld_gpio_bitop(pld, gpio->out_lvl_reg, offset, value); + kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR, offset, 1); kempld_release_mutex(pld); return 0; @@ -107,7 +181,7 @@ static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset) struct kempld_gpio_data *gpio = gpiochip_get_data(chip); struct kempld_device_data *pld = gpio->pld; - if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset)) + if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR, offset)) return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_IN; @@ -133,6 +207,180 @@ static int kempld_gpio_pincount(struct kempld_device_data *pld) return evt ? __ffs(evt) : 16; } +static void kempld_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); + + gpio->ien &= ~BIT(irqd_to_hwirq(data)); + gpiochip_disable_irq(chip, irqd_to_hwirq(data)); +} + +static void kempld_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); + + gpiochip_enable_irq(chip, irqd_to_hwirq(data)); + gpio->ien |= BIT(irqd_to_hwirq(data)); +} + +static int kempld_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + gpio->evt_low_high |= BIT(data->hwirq); + gpio->evt_lvl_edge |= BIT(data->hwirq); + break; + case IRQ_TYPE_EDGE_FALLING: + gpio->evt_low_high &= ~BIT(data->hwirq); + gpio->evt_lvl_edge |= BIT(data->hwirq); + break; + case IRQ_TYPE_LEVEL_HIGH: + gpio->evt_low_high |= BIT(data->hwirq); + gpio->evt_lvl_edge &= ~BIT(data->hwirq); + break; + case IRQ_TYPE_LEVEL_LOW: + gpio->evt_low_high &= ~BIT(data->hwirq); + gpio->evt_lvl_edge &= ~BIT(data->hwirq); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void kempld_irq_bus_lock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); + + mutex_lock(&gpio->irq_lock); +} + +static void kempld_irq_bus_sync_unlock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); + struct kempld_device_data *pld = gpio->pld; + + kempld_get_mutex(pld); + kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, gpio->evt_lvl_edge); + kempld_write16(pld, KEMPLD_GPIO_EVT_LOW_HIGH, gpio->evt_low_high); + kempld_write16(pld, KEMPLD_GPIO_IEN, gpio->ien); + kempld_release_mutex(pld); + + mutex_unlock(&gpio->irq_lock); +} + +static const struct irq_chip kempld_irqchip = { + .name = "kempld-gpio", + .irq_mask = kempld_irq_mask, + .irq_unmask = kempld_irq_unmask, + .irq_set_type = kempld_irq_set_type, + .irq_bus_lock = kempld_irq_bus_lock, + .irq_bus_sync_unlock = kempld_irq_bus_sync_unlock, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static irqreturn_t kempld_gpio_irq_handler(int irq, void *data) +{ + struct kempld_gpio_data *gpio = data; + struct gpio_chip *chip = &gpio->chip; + unsigned int pin, child_irq; + unsigned long status; + + kempld_get_mutex(gpio->pld); + + status = kempld_read16(gpio->pld, KEMPLD_GPIO_STS); + if (status) + kempld_write16(gpio->pld, KEMPLD_GPIO_STS, status); + + kempld_release_mutex(gpio->pld); + + status &= gpio->ien; + if (!status) + return IRQ_NONE; + + for_each_set_bit(pin, &status, chip->ngpio) { + child_irq = irq_find_mapping(chip->irq.domain, pin); + handle_nested_irq(child_irq); + } + + return IRQ_HANDLED; +} + +static int kempld_gpio_irq_init(struct device *dev, + struct kempld_gpio_data *gpio) +{ + struct kempld_device_data *pld = gpio->pld; + struct gpio_chip *chip = &gpio->chip; + struct gpio_irq_chip *girq; + unsigned int irq; + int ret; + + /* Get the IRQ configured by the BIOS in the PLD */ + kempld_get_mutex(pld); + irq = kempld_read8(pld, KEMPLD_IRQ_GPIO); + kempld_release_mutex(pld); + + if (irq == 0xff) { + dev_info(dev, "GPIO controller has no IRQ support\n"); + return 0; + } + + /* Allow overriding the IRQ with the module parameter */ + if (gpio_irq > 0) { + dev_warn(dev, "Forcing IRQ to %d\n", gpio_irq); + irq &= ~KEMPLD_IRQ_GPIO_MASK; + irq |= gpio_irq & KEMPLD_IRQ_GPIO_MASK; + } + + if (!(irq & KEMPLD_IRQ_GPIO_MASK)) { + dev_warn(dev, "No IRQ configured\n"); + return 0; + } + + /* Get the current config, disable all child interrupts, clear them + * and set the parent IRQ + */ + kempld_get_mutex(pld); + gpio->evt_low_high = kempld_read16(pld, KEMPLD_GPIO_EVT_LOW_HIGH); + gpio->evt_lvl_edge = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); + kempld_write16(pld, KEMPLD_GPIO_IEN, 0); + kempld_write16(pld, KEMPLD_GPIO_STS, 0xFFFF); + kempld_write16(pld, KEMPLD_IRQ_GPIO, irq); + kempld_release_mutex(pld); + + girq = &chip->irq; + gpio_irq_chip_set_chip(girq, &kempld_irqchip); + + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + + mutex_init(&gpio->irq_lock); + + ret = devm_request_threaded_irq(dev, irq & KEMPLD_IRQ_GPIO_MASK, + NULL, kempld_gpio_irq_handler, + IRQF_ONESHOT, chip->label, + gpio); + if (ret) { + dev_err(dev, "failed to request irq %d\n", irq); + return ret; + } + + return 0; +} + static int kempld_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -152,6 +400,15 @@ static int kempld_gpio_probe(struct platform_device *pdev) if (!gpio) return -ENOMEM; + /* Starting with version 2.8 there is a dedicated register for the + * output state, earlier versions share the register used to read + * the line level. + */ + if (pld->info.spec_major > 2 || pld->info.spec_minor >= 8) + gpio->out_lvl_reg = KEMPLD_GPIO_OUT_LVL; + else + gpio->out_lvl_reg = KEMPLD_GPIO_LVL; + gpio->pld = pld; platform_set_drvdata(pdev, gpio); @@ -169,13 +426,19 @@ static int kempld_gpio_probe(struct platform_device *pdev) chip->direction_output = kempld_gpio_direction_output; chip->get_direction = kempld_gpio_get_direction; chip->get = kempld_gpio_get; + chip->get_multiple = kempld_gpio_get_multiple; chip->set = kempld_gpio_set; + chip->set_multiple = kempld_gpio_set_multiple; chip->ngpio = kempld_gpio_pincount(pld); if (chip->ngpio == 0) { dev_err(dev, "No GPIO pins detected\n"); return -ENODEV; } + ret = kempld_gpio_irq_init(dev, gpio); + if (ret) + return ret; + ret = devm_gpiochip_add_data(dev, chip, gpio); if (ret) { dev_err(dev, "Could not register GPIO chip\n"); diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index 5376708a81bf..f4413fa5a811 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -55,7 +55,7 @@ static int lp873x_gpio_get(struct gpio_chip *chip, unsigned int offset) if (ret < 0) return ret; - return val & BIT(offset * BITS_PER_GPO); + return !!(val & BIT(offset * BITS_PER_GPO)); } static int lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index a61d670ceeda..281ba1740a6a 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -10,14 +10,18 @@ * Derived from drivers/gpio/pca953x.c */ -#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include -#include -#include -#include -#include /* * Each port of MAX732x (including MAX7319) falls into one of the @@ -207,22 +211,20 @@ static void max732x_gpio_set_mask(struct gpio_chip *gc, unsigned off, int mask, uint8_t reg_out; int ret; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0]; reg_out = (reg_out & ~mask) | (val & mask); ret = max732x_writeb(chip, is_group_a(chip, off), reg_out); if (ret < 0) - goto out; + return; /* update the shadow register then */ if (off > 7) chip->reg_out[1] = reg_out; else chip->reg_out[0] = reg_out; -out: - mutex_unlock(&chip->lock); } static int max732x_gpio_set_value(struct gpio_chip *gc, unsigned int off, @@ -329,7 +331,7 @@ static void max732x_irq_update_mask(struct max732x_chip *chip) if (chip->irq_features == INT_NO_MASK) return; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); switch (chip->irq_features) { case INT_INDEP_MASK: @@ -342,8 +344,6 @@ static void max732x_irq_update_mask(struct max732x_chip *chip) max732x_writeb(chip, 1, (uint8_t)msg); break; } - - mutex_unlock(&chip->lock); } static void max732x_irq_mask(struct irq_data *d) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index edbcaad57d00..0941d034a49c 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -42,18 +42,16 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #include #include -#include #include -#include #include #include +#include #include #include #include #include #include #include -#include #include #include diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 12191aeb6566..a859deab2bca 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -548,13 +548,6 @@ static void gpio_mpsse_ida_remove(void *data) ida_free(&gpio_mpsse_ida, priv->id); } -static void gpio_mpsse_usb_put_dev(void *data) -{ - struct mpsse_priv *priv = data; - - usb_put_dev(priv->udev); -} - static int mpsse_init_valid_mask(struct gpio_chip *chip, unsigned long *valid_mask, unsigned int ngpios) @@ -598,11 +591,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface, INIT_LIST_HEAD(&priv->workers); - priv->udev = usb_get_dev(interface_to_usbdev(interface)); - err = devm_add_action_or_reset(dev, gpio_mpsse_usb_put_dev, priv); - if (err) - return err; - + priv->udev = interface_to_usbdev(interface); priv->intf = interface; priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber; diff --git a/drivers/gpio/gpio-novalake-events.c b/drivers/gpio/gpio-novalake-events.c new file mode 100644 index 000000000000..b3bf0038f84a --- /dev/null +++ b/drivers/gpio/gpio-novalake-events.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Nova Lake GPIO-signaled ACPI events driver + * + * Copyright (c) 2026, Intel Corporation. + * + * Author: Alan Borzeszkowski + * + * Intel client platforms released in 2026 and later (starting with Intel Nova + * Lake) support two modes of handling ACPI General Purpose Events (GPE): + * exposed GPIO interrupt mode and legacy mode. + * + * By default, the platform uses legacy mode, handling GPEs as usual. If this + * driver is installed, it signals to the platform (on every boot) that exposed + * GPIO interrupt mode is supported. The platform then switches to exposed + * mode, which takes effect on next boot. From the user perspective, this + * change is transparent. + * + * However, if driver is uninstalled while in exposed interrupt mode, GPEs will + * _not_ be handled until platform falls back to legacy mode. This means that + * USB keyboard, mouse might not function properly for the fallback duration. + * Fallback requires two reboots to take effect: on first reboot, platform no + * longer receives signal from this driver and switches to legacy mode, which + * takes effect on second boot. + * + * Example ACPI event: Power Management Event coming from motherboard PCH, + * waking system from sleep following USB mouse hotplug. + * + * This driver supports up to 128 GPIO pins in each GPE block, per ACPI + * specification v6.6 section 5.6.4. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * GPE block has two registers, each register takes half the block size. + * Convert size to bits to get total GPIO pin count. + */ +#define GPE_BLK_REG_SIZE(block_size) ((block_size) / 2) +#define GPE_REG_PIN_COUNT(block_size) BYTES_TO_BITS(GPE_BLK_REG_SIZE(block_size)) +#define GPE_STS_REG_OFFSET 0 +#define GPE_EN_REG_OFFSET(block_size) GPE_BLK_REG_SIZE(block_size) + +/** + * struct nvl_gpio - Intel Nova Lake GPIO driver state + * @gc: GPIO controller interface + * @reg_base: Base address of the GPE registers + * @lock: Guard register access + * @blk_size: GPE block length + */ +struct nvl_gpio { + struct gpio_chip gc; + void __iomem *reg_base; + raw_spinlock_t lock; + size_t blk_size; +}; + +static void __iomem *nvl_gpio_get_byte_addr(struct nvl_gpio *priv, + unsigned int reg_offset, + unsigned long gpio) +{ + return priv->reg_base + reg_offset + gpio; +} + +static int nvl_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct nvl_gpio *priv = gpiochip_get_data(gc); + unsigned int byte_idx = gpio / BITS_PER_BYTE; + unsigned int bit_idx = gpio % BITS_PER_BYTE; + void __iomem *addr; + u8 reg; + + addr = nvl_gpio_get_byte_addr(priv, GPE_STS_REG_OFFSET, byte_idx); + + guard(raw_spinlock_irqsave)(&priv->lock); + + reg = ioread8(addr); + + return !!(reg & BIT(bit_idx)); +} + +static const struct gpio_chip nvl_gpio_chip = { + .owner = THIS_MODULE, + .get = nvl_gpio_get, +}; + +static int nvl_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + else if (type & IRQ_TYPE_LEVEL_MASK) + irq_set_handler_locked(d, handle_level_irq); + + return 0; +} + +static void nvl_gpio_irq_mask_unmask(struct gpio_chip *gc, unsigned long hwirq, + bool mask) +{ + struct nvl_gpio *priv = gpiochip_get_data(gc); + unsigned int byte_idx = hwirq / BITS_PER_BYTE; + unsigned int bit_idx = hwirq % BITS_PER_BYTE; + void __iomem *addr; + u8 reg; + + addr = nvl_gpio_get_byte_addr(priv, GPE_EN_REG_OFFSET(priv->blk_size), byte_idx); + + guard(raw_spinlock_irqsave)(&priv->lock); + + reg = ioread8(addr); + if (mask) + reg &= ~BIT(bit_idx); + else + reg |= BIT(bit_idx); + iowrite8(reg, addr); +} + +static void nvl_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + nvl_gpio_irq_mask_unmask(gc, hwirq, false); +} + +static void nvl_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + nvl_gpio_irq_mask_unmask(gc, hwirq, true); + gpiochip_disable_irq(gc, hwirq); +} + +static void nvl_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct nvl_gpio *priv = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned int byte_idx = hwirq / BITS_PER_BYTE; + unsigned int bit_idx = hwirq % BITS_PER_BYTE; + void __iomem *addr; + u8 reg; + + addr = nvl_gpio_get_byte_addr(priv, GPE_STS_REG_OFFSET, byte_idx); + + guard(raw_spinlock_irqsave)(&priv->lock); + + reg = ioread8(addr); + reg |= BIT(bit_idx); + iowrite8(reg, addr); +} + +static const struct irq_chip nvl_gpio_irq_chip = { + .name = "gpio-novalake", + .irq_ack = nvl_gpio_irq_ack, + .irq_mask = nvl_gpio_irq_mask, + .irq_unmask = nvl_gpio_irq_unmask, + .irq_set_type = nvl_gpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static irqreturn_t nvl_gpio_irq(int irq, void *data) +{ + struct nvl_gpio *priv = data; + const size_t block_size = priv->blk_size; + unsigned int handled = 0; + + for (unsigned int i = 0; i < block_size; i++) { + const void __iomem *reg = priv->reg_base + i; + unsigned long pending; + unsigned long enabled; + unsigned int bit_idx; + + scoped_guard(raw_spinlock, &priv->lock) { + pending = ioread8(reg + GPE_STS_REG_OFFSET); + enabled = ioread8(reg + GPE_EN_REG_OFFSET(block_size)); + } + pending &= enabled; + + for_each_set_bit(bit_idx, &pending, BITS_PER_BYTE) { + unsigned int hwirq = i * BITS_PER_BYTE + bit_idx; + + generic_handle_domain_irq(priv->gc.irq.domain, hwirq); + } + + handled += pending ? 1 : 0; + } + + return IRQ_RETVAL(handled); +} + +/* UUID for GPE device _DSM: 079406e6-bdea-49cf-8563-03e2811901cb */ +static const guid_t nvl_gpe_dsm_guid = + GUID_INIT(0x079406e6, 0xbdea, 0x49cf, + 0x85, 0x63, 0x03, 0xe2, 0x81, 0x19, 0x01, 0xcb); + +#define DSM_GPE_MODE_REV 1 +#define DSM_GPE_MODE_FN_INDEX 1 +#define DSM_ENABLE_GPE_MODE 1 + +static int nvl_acpi_enable_gpe_mode(struct device *dev) +{ + union acpi_object argv4[2]; + union acpi_object *obj; + + argv4[0].type = ACPI_TYPE_PACKAGE; + argv4[0].package.count = 1; + argv4[0].package.elements = &argv4[1]; + argv4[1].integer.type = ACPI_TYPE_INTEGER; + argv4[1].integer.value = DSM_ENABLE_GPE_MODE; + + obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(dev), &nvl_gpe_dsm_guid, + DSM_GPE_MODE_REV, DSM_GPE_MODE_FN_INDEX, + argv4, ACPI_TYPE_BUFFER); + if (!obj) + return -EIO; + ACPI_FREE(obj); + + return 0; +} + +static int nvl_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + resource_size_t ioresource_size; + struct gpio_irq_chip *girq; + struct nvl_gpio *priv; + struct resource *res; + void __iomem *regs; + int ret, irq; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -ENXIO; + + /* + * GPE block length should be non-negative multiple of two and allow up + * to 128 pins. ACPI v6.6 section 5.2.9 and 5.6.4. + */ + ioresource_size = resource_size(res); + if (!ioresource_size || ioresource_size % 2 || ioresource_size > 0x20) + return dev_err_probe(dev, -EINVAL, + "invalid GPE block length, resource: %pR\n", + res); + + regs = devm_ioport_map(dev, res->start, ioresource_size); + if (!regs) + return -ENOMEM; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + raw_spin_lock_init(&priv->lock); + + priv->reg_base = regs; + priv->blk_size = ioresource_size; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, nvl_gpio_irq, IRQF_SHARED, dev_name(dev), priv); + if (ret) + return ret; + + priv->gc = nvl_gpio_chip; + priv->gc.label = dev_name(dev); + priv->gc.parent = dev; + priv->gc.ngpio = GPE_REG_PIN_COUNT(priv->blk_size); + priv->gc.base = -1; + + girq = &priv->gc.irq; + gpio_irq_chip_set_chip(girq, &nvl_gpio_irq_chip); + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + + ret = devm_gpiochip_add_data(dev, &priv->gc, priv); + if (ret) + return ret; + + return nvl_acpi_enable_gpe_mode(dev); +} + +static const struct acpi_device_id nvl_gpio_acpi_match[] = { + { "INTC1114" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, nvl_gpio_acpi_match); + +static struct platform_driver nvl_gpio_driver = { + .driver = { + .name = "gpio-novalake-events", + .acpi_match_table = nvl_gpio_acpi_match, + }, + .probe = nvl_gpio_probe, +}; +module_platform_driver(nvl_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alan Borzeszkowski "); +MODULE_DESCRIPTION("Intel Nova Lake ACPI GPIO events driver"); diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index 4cf91528f547..5e3152c2e51a 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -351,6 +351,10 @@ static const struct of_device_id realtek_gpio_of_match[] = { { .compatible = "realtek,rtl9310-gpio", }, + { + .compatible = "realtek,rtl9607-gpio", + .data = (void *)GPIO_PORTS_REVERSED, + }, {} }; MODULE_DEVICE_TABLE(of, realtek_gpio_of_match); diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 0fff4a699f12..50f3733a455d 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -582,7 +582,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) bank->gpio_chip = rockchip_gpiolib_chip; gc = &bank->gpio_chip; - gc->base = bank->pin_base; + gc->base = -1; gc->ngpio = bank->nr_pins; gc->label = bank->name; gc->parent = bank->dev; diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 13b87c8e6d0c..e19701c2ed67 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -36,10 +36,9 @@ #include #include -#include "dev-sync-probe.h" - #define GPIO_SIM_NGPIO_MAX 1024 #define GPIO_SIM_PROP_MAX 5 /* Max 4 properties + sentinel. */ +#define GPIO_SIM_HOG_PROP_MAX 5 #define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */ static DEFINE_IDA(gpio_sim_ida); @@ -545,7 +544,7 @@ static struct platform_driver gpio_sim_driver = { }; struct gpio_sim_device { - struct dev_sync_probe_data probe_data; + struct platform_device *pdev; struct config_group group; int id; @@ -561,8 +560,6 @@ struct gpio_sim_device { */ struct mutex lock; - struct gpiod_hog *hogs; - struct list_head bank_list; }; @@ -655,6 +652,7 @@ struct gpio_sim_hog { char *name; int dir; + bool active_low; }; static struct gpio_sim_hog *to_gpio_sim_hog(struct config_item *item) @@ -673,7 +671,7 @@ static bool gpio_sim_device_is_live(struct gpio_sim_device *dev) { lockdep_assert_held(&dev->lock); - return !!dev->probe_data.pdev; + return !!dev->pdev; } static char *gpio_sim_strdup_trimmed(const char *str, size_t count) @@ -695,7 +693,7 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, guard(mutex)(&dev->lock); - pdev = dev->probe_data.pdev; + pdev = dev->pdev; if (pdev) return sprintf(page, "%s\n", dev_name(&pdev->dev)); @@ -774,102 +772,6 @@ static void gpio_sim_set_reserved_ranges(struct gpio_sim_bank *bank, } } -static void gpio_sim_remove_hogs(struct gpio_sim_device *dev) -{ - struct gpiod_hog *hog; - - if (!dev->hogs) - return; - - gpiod_remove_hogs(dev->hogs); - - for (hog = dev->hogs; hog->chip_label; hog++) { - kfree(hog->chip_label); - kfree(hog->line_name); - } - - kfree(dev->hogs); - dev->hogs = NULL; -} - -static int gpio_sim_add_hogs(struct gpio_sim_device *dev) -{ - unsigned int num_hogs = 0, idx = 0; - struct gpio_sim_bank *bank; - struct gpio_sim_line *line; - struct gpiod_hog *hog; - - list_for_each_entry(bank, &dev->bank_list, siblings) { - list_for_each_entry(line, &bank->line_list, siblings) { - if (line->offset >= bank->num_lines) - continue; - - if (line->hog) - num_hogs++; - } - } - - if (!num_hogs) - return 0; - - /* Allocate one more for the sentinel. */ - dev->hogs = kzalloc_objs(*dev->hogs, num_hogs + 1); - if (!dev->hogs) - return -ENOMEM; - - list_for_each_entry(bank, &dev->bank_list, siblings) { - list_for_each_entry(line, &bank->line_list, siblings) { - if (line->offset >= bank->num_lines) - continue; - - if (!line->hog) - continue; - - hog = &dev->hogs[idx++]; - - /* - * We need to make this string manually because at this - * point the device doesn't exist yet and so dev_name() - * is not available. - */ - if (gpio_sim_bank_has_label(bank)) - hog->chip_label = kstrdup(bank->label, - GFP_KERNEL); - else - hog->chip_label = kasprintf(GFP_KERNEL, - "gpio-sim.%u:%pfwP", - dev->id, - bank->swnode); - if (!hog->chip_label) { - gpio_sim_remove_hogs(dev); - return -ENOMEM; - } - - /* - * We need to duplicate this because the hog config - * item can be removed at any time (and we can't block - * it) and gpiolib doesn't make a deep copy of the hog - * data. - */ - if (line->hog->name) { - hog->line_name = kstrdup(line->hog->name, - GFP_KERNEL); - if (!hog->line_name) { - gpio_sim_remove_hogs(dev); - return -ENOMEM; - } - } - - hog->chip_hwnum = line->offset; - hog->dflags = line->hog->dir; - } - } - - gpiod_add_hogs(dev->hogs); - - return 0; -} - static struct fwnode_handle * gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, struct fwnode_handle *parent) @@ -917,12 +819,61 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, return fwnode_create_software_node(properties, parent); } +static int gpio_sim_bank_add_hogs(struct gpio_sim_bank *bank) +{ + struct property_entry properties[GPIO_SIM_HOG_PROP_MAX]; + struct fwnode_handle *swnode; + struct gpio_sim_line *line; + struct gpio_sim_hog *hog; + unsigned int idx; + u32 gpios[2]; + + list_for_each_entry(line, &bank->line_list, siblings) { + if (!line->hog) + continue; + + hog = line->hog; + + gpios[0] = line->offset; + gpios[1] = hog->active_low ? 1 : 0; + + memset(properties, 0, sizeof(properties)); + + idx = 0; + properties[idx++] = PROPERTY_ENTRY_BOOL("gpio-hog"); + properties[idx++] = PROPERTY_ENTRY_U32_ARRAY("gpios", gpios); + properties[idx++] = PROPERTY_ENTRY_STRING("line-name", hog->name); + + switch (hog->dir) { + case GPIOD_IN: + properties[idx++] = PROPERTY_ENTRY_BOOL("input"); + break; + case GPIOD_OUT_HIGH: + properties[idx++] = PROPERTY_ENTRY_BOOL("output-high"); + break; + case GPIOD_OUT_LOW: + properties[idx++] = PROPERTY_ENTRY_BOOL("output-low"); + break; + default: + /* Would have been validated at configfs store. */ + WARN(1, "Unexpected hog direction value: %d", hog->dir); + return -EINVAL; + } + + swnode = fwnode_create_software_node(properties, bank->swnode); + if (IS_ERR(swnode)) + return PTR_ERR(swnode); + } + + return 0; +} + static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode) { struct fwnode_handle *child; fwnode_for_each_child_node(swnode, child) - fwnode_remove_software_node(child); + gpio_sim_remove_swnode_recursive(child); fwnode_remove_software_node(swnode); } @@ -947,6 +898,7 @@ static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev) static int gpio_sim_device_activate(struct gpio_sim_device *dev) { struct platform_device_info pdevinfo; + struct platform_device *pdev; struct fwnode_handle *swnode; struct gpio_sim_bank *bank; int ret; @@ -974,29 +926,39 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev) bank->swnode = gpio_sim_make_bank_swnode(bank, swnode); if (IS_ERR(bank->swnode)) { ret = PTR_ERR(bank->swnode); - gpio_sim_remove_swnode_recursive(swnode); - return ret; + goto err_remove_swnode; } - } - ret = gpio_sim_add_hogs(dev); - if (ret) { - gpio_sim_remove_swnode_recursive(swnode); - return ret; + ret = gpio_sim_bank_add_hogs(bank); + if (ret) + goto err_remove_swnode; } pdevinfo.name = "gpio-sim"; pdevinfo.fwnode = swnode; pdevinfo.id = dev->id; - ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo); - if (ret) { - gpio_sim_remove_hogs(dev); - gpio_sim_remove_swnode_recursive(swnode); - return ret; + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + goto err_remove_swnode; } + wait_for_device_probe(); + if (!device_is_bound(&pdev->dev)) { + ret = -ENXIO; + goto err_unregister_pdev; + } + + dev->pdev = pdev; return 0; + +err_unregister_pdev: + platform_device_unregister(pdev); +err_remove_swnode: + gpio_sim_remove_swnode_recursive(swnode); + + return ret; } static void gpio_sim_device_deactivate(struct gpio_sim_device *dev) @@ -1005,9 +967,9 @@ static void gpio_sim_device_deactivate(struct gpio_sim_device *dev) lockdep_assert_held(&dev->lock); - swnode = dev_fwnode(&dev->probe_data.pdev->dev); - dev_sync_probe_unregister(&dev->probe_data); - gpio_sim_remove_hogs(dev); + swnode = dev_fwnode(&dev->pdev->dev); + platform_device_unregister(dev->pdev); + dev->pdev = NULL; gpio_sim_remove_swnode_recursive(swnode); } @@ -1109,7 +1071,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, guard(mutex)(&dev->lock); if (gpio_sim_device_is_live(dev)) - return device_for_each_child(&dev->probe_data.pdev->dev, &ctx, + return device_for_each_child(&dev->pdev->dev, &ctx, gpio_sim_emit_chip_name); return sprintf(page, "none\n"); @@ -1365,9 +1327,46 @@ gpio_sim_hog_config_direction_store(struct config_item *item, CONFIGFS_ATTR(gpio_sim_hog_config_, direction); +static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item, + char *page) +{ + struct gpio_sim_hog *hog = to_gpio_sim_hog(item); + struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); + + guard(mutex)(&dev->lock); + + return sprintf(page, "%c\n", hog->active_low ? '1' : '0'); +} + +static ssize_t +gpio_sim_hog_config_active_low_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_hog *hog = to_gpio_sim_hog(item); + struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); + bool active_low; + int ret; + + guard(mutex)(&dev->lock); + + if (gpio_sim_device_is_live(dev)) + return -EBUSY; + + ret = kstrtobool(page, &active_low); + if (ret) + return ret; + + hog->active_low = active_low; + + return count; +} + +CONFIGFS_ATTR(gpio_sim_hog_config_, active_low); + static struct configfs_attribute *gpio_sim_hog_config_attrs[] = { &gpio_sim_hog_config_attr_name, &gpio_sim_hog_config_attr_direction, + &gpio_sim_hog_config_attr_active_low, NULL }; @@ -1583,8 +1582,6 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name) mutex_init(&dev->lock); INIT_LIST_HEAD(&dev->bank_list); - dev_sync_probe_init(&dev->probe_data); - return &no_free_ptr(dev)->group; } diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 493c027afdd6..78d6c78d4aab 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -102,7 +102,7 @@ static int xway_stp_get(struct gpio_chip *gc, unsigned int gpio) { struct xway_stp *chip = gpiochip_get_data(gc); - return (xway_stp_r32(chip->virt, XWAY_STP_CPU0) & BIT(gpio)); + return !!(xway_stp_r32(chip->virt, XWAY_STP_CPU0) & BIT(gpio)); } /** diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 9c874f07be75..aa7c3e44234f 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -125,7 +125,6 @@ struct tegra_gpio_soc { struct tegra_gpio { struct gpio_chip gpio; unsigned int num_irq; - unsigned int *irq; const struct tegra_gpio_soc *soc; unsigned int num_irqs_per_bank; @@ -133,6 +132,8 @@ struct tegra_gpio { void __iomem *secure; void __iomem *base; + + unsigned int irq[] __counted_by(num_irq); }; static const struct tegra_gpio_port * @@ -857,12 +858,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev) struct device_node *np; struct resource *res; char **names; - int err; + int node, err; - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + err = platform_irq_count(pdev); + if (err < 0) + return err; + + gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, irq, err), GFP_KERNEL); if (!gpio) return -ENOMEM; + gpio->num_irq = err; + gpio->soc = device_get_match_data(&pdev->dev); gpio->gpio.label = gpio->soc->name; gpio->gpio.parent = &pdev->dev; @@ -889,21 +896,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base); - err = platform_irq_count(pdev); - if (err < 0) - return err; - - gpio->num_irq = err; - err = tegra186_gpio_irqs_per_bank(gpio); if (err < 0) return err; - gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), - GFP_KERNEL); - if (!gpio->irq) - return -ENOMEM; - for (i = 0; i < gpio->num_irq; i++) { err = platform_get_irq(pdev, i); if (err < 0) @@ -937,16 +933,22 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (!names) return -ENOMEM; + node = dev_to_node(&pdev->dev); + for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; char *name; for (j = 0; j < port->pins; j++) { - if (gpio->soc->prefix) - name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "%s-P%s.%02x", - gpio->soc->prefix, port->name, j); + if (node >= 0) + name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, + "%d-%sP%s.%02x", node, + gpio->soc->prefix ?: "", + port->name, j); else - name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "P%s.%02x", + name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, + "%sP%s.%02x", + gpio->soc->prefix ?: "", port->name, j); if (!name) return -ENOMEM; @@ -1373,6 +1375,9 @@ static const struct tegra_gpio_soc tegra256_main_soc = { .has_vm_support = true, }; +/* Macro to define GPIO name prefix with separator */ +#define TEGRA_GPIO_PREFIX(_x) _x "-" + #define TEGRA410_COMPUTE_GPIO_PORT(_name, _bank, _port, _pins) \ TEGRA_GPIO_PORT(TEGRA410_COMPUTE, _name, _bank, _port, _pins) @@ -1388,7 +1393,7 @@ static const struct tegra_gpio_soc tegra410_compute_soc = { .num_ports = ARRAY_SIZE(tegra410_compute_ports), .ports = tegra410_compute_ports, .name = "tegra410-gpio-compute", - .prefix = "COMPUTE", + .prefix = TEGRA_GPIO_PREFIX("COMPUTE"), .num_irqs_per_bank = 8, .instance = 0, }; @@ -1418,7 +1423,7 @@ static const struct tegra_gpio_soc tegra410_system_soc = { .num_ports = ARRAY_SIZE(tegra410_system_ports), .ports = tegra410_system_ports, .name = "tegra410-gpio-system", - .prefix = "SYSTEM", + .prefix = TEGRA_GPIO_PREFIX("SYSTEM"), .num_irqs_per_bank = 8, .instance = 0, }; diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 84b17b83476f..df770ecf28bc 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -50,7 +50,7 @@ static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) if (ret < 0) return ret; - return val & BIT(4 + offset); + return !!(val & BIT(4 + offset)); } static int tps65086_gpio_set(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c index 992ee231db9f..0207c2b813f4 100644 --- a/drivers/gpio/gpio-ts4800.c +++ b/drivers/gpio/gpio-ts4800.c @@ -11,7 +11,6 @@ #include #include -#define DEFAULT_PIN_NUMBER 16 #define INPUT_REG_OFFSET 0x00 #define OUTPUT_REG_OFFSET 0x02 #define DIRECTION_REG_OFFSET 0x04 @@ -23,7 +22,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev) struct gpio_generic_chip *chip; void __iomem *base_addr; int retval; - u32 ngpios; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -33,12 +31,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev) if (IS_ERR(base_addr)) return PTR_ERR(base_addr); - retval = device_property_read_u32(dev, "ngpios", &ngpios); - if (retval == -EINVAL) - ngpios = DEFAULT_PIN_NUMBER; - else if (retval) - return retval; - config = (struct gpio_generic_chip_config) { .dev = dev, .sz = 2, @@ -52,8 +44,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev) return dev_err_probe(dev, retval, "failed to initialize the generic GPIO chip\n"); - chip->gc.ngpio = ngpios; - return devm_gpiochip_add_data(dev, &chip->gc, NULL); } diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index 15e495c109d2..89087fd48a81 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -288,7 +288,7 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip, /* if io is set to output, just return the saved value */ if (gpio->gpiob_out & (1 << offset)) - return gpio->gpiob_val & (1 << offset); + return !!(gpio->gpiob_val & (1 << offset)); mutex_lock(&vb->lock); diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index 955b5efc283e..fe0eac920ced 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -36,8 +36,6 @@ #include #include -#include "dev-sync-probe.h" - #define GPIO_VIRTUSER_NAME_BUF_LEN 32 static DEFINE_IDA(gpio_virtuser_ida); @@ -978,7 +976,7 @@ static struct platform_driver gpio_virtuser_driver = { }; struct gpio_virtuser_device { - struct dev_sync_probe_data probe_data; + struct platform_device *pdev; struct config_group group; int id; @@ -1002,7 +1000,7 @@ gpio_virtuser_device_is_live(struct gpio_virtuser_device *dev) { lockdep_assert_held(&dev->lock); - return !!dev->probe_data.pdev; + return !!dev->pdev; } struct gpio_virtuser_lookup { @@ -1342,7 +1340,7 @@ gpio_virtuser_device_config_dev_name_show(struct config_item *item, guard(mutex)(&dev->lock); - pdev = dev->probe_data.pdev; + pdev = dev->pdev; if (pdev) return sprintf(page, "%s\n", dev_name(&pdev->dev)); @@ -1450,6 +1448,7 @@ static int gpio_virtuser_device_activate(struct gpio_virtuser_device *dev) { struct platform_device_info pdevinfo; + struct platform_device *pdev; struct fwnode_handle *swnode; int ret; @@ -1471,12 +1470,23 @@ gpio_virtuser_device_activate(struct gpio_virtuser_device *dev) if (ret) goto err_remove_swnode; - ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo); - if (ret) + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); goto err_remove_lookup_table; + } + wait_for_device_probe(); + if (!device_is_bound(&pdev->dev)) { + ret = -ENXIO; + goto err_unregister_pdev; + } + + dev->pdev = pdev; return 0; +err_unregister_pdev: + platform_device_unregister(pdev); err_remove_lookup_table: gpio_virtuser_remove_lookup_table(dev); err_remove_swnode: @@ -1492,8 +1502,9 @@ gpio_virtuser_device_deactivate(struct gpio_virtuser_device *dev) lockdep_assert_held(&dev->lock); - swnode = dev_fwnode(&dev->probe_data.pdev->dev); - dev_sync_probe_unregister(&dev->probe_data); + swnode = dev_fwnode(&dev->pdev->dev); + platform_device_unregister(dev->pdev); + dev->pdev = NULL; gpio_virtuser_remove_lookup_table(dev); fwnode_remove_software_node(swnode); } @@ -1723,7 +1734,6 @@ gpio_virtuser_config_make_device_group(struct config_group *group, &gpio_virtuser_device_config_group_type); mutex_init(&dev->lock); INIT_LIST_HEAD(&dev->lookup_list); - dev_sync_probe_init(&dev->probe_data); return &no_free_ptr(dev)->group; } diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index ced6375d1bad..09f860200a05 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -1220,75 +1220,6 @@ static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip) } } -static struct gpio_desc * -acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip, - struct fwnode_handle *fwnode, - const char **name, - unsigned long *lflags, - enum gpiod_flags *dflags) -{ - struct gpio_chip *chip = achip->chip; - struct gpio_desc *desc; - u32 gpios[2]; - int ret; - - *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; - *dflags = GPIOD_ASIS; - *name = NULL; - - ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, - ARRAY_SIZE(gpios)); - if (ret < 0) - return ERR_PTR(ret); - - desc = gpiochip_get_desc(chip, gpios[0]); - if (IS_ERR(desc)) - return desc; - - if (gpios[1]) - *lflags |= GPIO_ACTIVE_LOW; - - if (fwnode_property_present(fwnode, "input")) - *dflags |= GPIOD_IN; - else if (fwnode_property_present(fwnode, "output-low")) - *dflags |= GPIOD_OUT_LOW; - else if (fwnode_property_present(fwnode, "output-high")) - *dflags |= GPIOD_OUT_HIGH; - else - return ERR_PTR(-EINVAL); - - fwnode_property_read_string(fwnode, "line-name", name); - - return desc; -} - -static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip) -{ - struct gpio_chip *chip = achip->chip; - - device_for_each_child_node_scoped(chip->parent, fwnode) { - unsigned long lflags; - enum gpiod_flags dflags; - struct gpio_desc *desc; - const char *name; - int ret; - - if (!fwnode_property_present(fwnode, "gpio-hog")) - continue; - - desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name, - &lflags, &dflags); - if (IS_ERR(desc)) - continue; - - ret = gpiod_hog(desc, name, lflags, dflags); - if (ret) { - dev_err(chip->parent, "Failed to hog GPIO\n"); - return; - } - } -} - void acpi_gpiochip_add(struct gpio_chip *chip) { struct acpi_gpio_chip *acpi_gpio; @@ -1321,7 +1252,6 @@ void acpi_gpiochip_add(struct gpio_chip *chip) } acpi_gpiochip_request_regions(acpi_gpio); - acpi_gpiochip_scan_gpios(acpi_gpio); acpi_dev_clear_dependencies(adev); } diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 73ae77f0f213..f36b7c06996d 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1586,15 +1586,16 @@ static const struct file_operations line_fileops = { #endif }; +DEFINE_FREE(linereq_free, struct linereq *, if (!IS_ERR_OR_NULL(_T)) linereq_free(_T)) + static int linereq_create(struct gpio_device *gdev, void __user *ip) { struct gpio_v2_line_request ulr; struct gpio_v2_line_config *lc; - struct linereq *lr; - struct file *file; + struct linereq *lr __free(linereq_free) = NULL; u64 flags, edflags; unsigned int i; - int fd, ret; + int ret; if (copy_from_user(&ulr, ip, sizeof(ulr))) return -EFAULT; @@ -1627,10 +1628,8 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) /* label is only initialized if consumer is set */ lr->label = kstrndup(ulr.consumer, sizeof(ulr.consumer) - 1, GFP_KERNEL); - if (!lr->label) { - ret = -ENOMEM; - goto out_free_linereq; - } + if (!lr->label) + return -ENOMEM; } mutex_init(&lr->config_mutex); @@ -1649,14 +1648,12 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) u32 offset = ulr.offsets[i]; struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); - if (IS_ERR(desc)) { - ret = PTR_ERR(desc); - goto out_free_linereq; - } + if (IS_ERR(desc)) + return PTR_ERR(desc); ret = gpiod_request_user(desc, lr->label); if (ret) - goto out_free_linereq; + return ret; lr->lines[i].desc = desc; flags = gpio_v2_line_config_flags(lc, i); @@ -1664,7 +1661,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_set_transitory(desc, false); if (ret < 0) - goto out_free_linereq; + return ret; edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* @@ -1676,16 +1673,16 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_direction_output_nonotify(desc, val); if (ret) - goto out_free_linereq; + return ret; } else if (flags & GPIO_V2_LINE_FLAG_INPUT) { ret = gpiod_direction_input_nonotify(desc); if (ret) - goto out_free_linereq; + return ret; ret = edge_detector_setup(&lr->lines[i], lc, i, edflags); if (ret) - goto out_free_linereq; + return ret; } lr->lines[i].edflags = edflags; @@ -1700,44 +1697,25 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) ret = blocking_notifier_chain_register(&gdev->device_notifier, &lr->device_unregistered_nb); if (ret) - goto out_free_linereq; + return ret; - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_linereq; - } + FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, + anon_inode_getfile("gpio-line", &line_fileops, lr, + O_RDONLY | O_CLOEXEC)); + if (fdf.err) + return fdf.err; + retain_and_null_ptr(lr); - file = anon_inode_getfile("gpio-line", &line_fileops, lr, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - ulr.fd = fd; - if (copy_to_user(ip, &ulr, sizeof(ulr))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); + ulr.fd = fd_prepare_fd(fdf); + if (copy_to_user(ip, &ulr, sizeof(ulr))) return -EFAULT; - } - fd_install(fd, file); + fd_publish(fdf); dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", - lr->num_lines); + ulr.num_lines); return 0; - -out_put_unused_fd: - put_unused_fd(fd); -out_free_linereq: - linereq_free(lr); - return ret; } #ifdef CONFIG_GPIO_CDEV_V1 @@ -2010,16 +1988,16 @@ static irqreturn_t lineevent_irq_handler(int irq, void *p) return IRQ_WAKE_THREAD; } +DEFINE_FREE(lineevent_free, struct lineevent_state *, if (!IS_ERR_OR_NULL(_T)) lineevent_free(_T)) + static int lineevent_create(struct gpio_device *gdev, void __user *ip) { struct gpioevent_request eventreq; - struct lineevent_state *le; + struct lineevent_state *le __free(lineevent_free) = NULL; struct gpio_desc *desc; - struct file *file; u32 offset; u32 lflags; u32 eflags; - int fd; int ret; int irq, irqflags = 0; char *label; @@ -2064,15 +2042,13 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) le->label = kstrndup(eventreq.consumer_label, sizeof(eventreq.consumer_label) - 1, GFP_KERNEL); - if (!le->label) { - ret = -ENOMEM; - goto out_free_le; - } + if (!le->label) + return -ENOMEM; } ret = gpiod_request_user(desc, le->label); if (ret) - goto out_free_le; + return ret; le->desc = desc; le->eflags = eflags; @@ -2080,15 +2056,13 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_direction_input(desc); if (ret) - goto out_free_le; + return ret; gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); irq = gpiod_to_irq(desc); - if (irq <= 0) { - ret = -ENODEV; - goto out_free_le; - } + if (irq <= 0) + return -ENODEV; if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) irqflags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) ? @@ -2105,13 +2079,11 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ret = blocking_notifier_chain_register(&gdev->device_notifier, &le->device_unregistered_nb); if (ret) - goto out_free_le; + return ret; label = make_irq_label(le->label); - if (IS_ERR(label)) { - ret = PTR_ERR(label); - goto out_free_le; - } + if (IS_ERR(label)) + return PTR_ERR(label); /* Request a thread to read the events */ ret = request_threaded_irq(irq, @@ -2122,46 +2094,25 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) le); if (ret) { free_irq_label(label); - goto out_free_le; + return ret; } le->irq = irq; - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_le; - } + FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, + anon_inode_getfile("gpio-event", &lineevent_fileops, le, + O_RDONLY | O_CLOEXEC)); + if (fdf.err) + return fdf.err; + retain_and_null_ptr(le); - file = anon_inode_getfile("gpio-event", - &lineevent_fileops, - le, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - eventreq.fd = fd; - if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); + eventreq.fd = fd_prepare_fd(fdf); + if (copy_to_user(ip, &eventreq, sizeof(eventreq))) return -EFAULT; - } - fd_install(fd, file); + fd_publish(fdf); return 0; - -out_put_unused_fd: - put_unused_fd(fd); -out_free_le: - lineevent_free(le); - return ret; } static void gpio_v2_line_info_to_v1(struct gpio_v2_line_info *info_v2, @@ -2689,12 +2640,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) struct gpio_chardev_data *cdev; int ret = -ENOMEM; - guard(srcu)(&gdev->srcu); - - /* Fail on open if the backing gpiochip is gone */ - if (!rcu_access_pointer(gdev->chip)) - return -ENODEV; - cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return -ENOMEM; @@ -2782,9 +2727,9 @@ static const struct file_operations gpio_fileops = { #endif }; -int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) +int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt) { - struct gpio_chip *gc; + struct gpio_device *gdev = gc->gpiodev; int ret; cdev_init(&gdev->chrdev, &gpio_fileops); @@ -2802,14 +2747,6 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) return ret; } - guard(srcu)(&gdev->srcu); - gc = srcu_dereference(gdev->chip, &gdev->srcu); - if (!gc) { - cdev_device_del(&gdev->chrdev, &gdev->dev); - destroy_workqueue(gdev->line_state_wq); - return -ENODEV; - } - gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); return 0; diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h index b42644cbffb8..4a9cb3335d99 100644 --- a/drivers/gpio/gpiolib-cdev.h +++ b/drivers/gpio/gpiolib-cdev.h @@ -7,7 +7,7 @@ struct gpio_device; -int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt); +int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt); void gpiolib_cdev_unregister(struct gpio_device *gdev); #endif /* GPIOLIB_CDEV_H */ diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index ef1ac68b94b7..2c923d17541f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -10,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -446,32 +446,6 @@ out: return desc; } -/** - * of_get_named_gpio() - Get a GPIO number to use with GPIO API - * @np: device node to get GPIO from - * @propname: Name of property containing gpio specifier(s) - * @index: index of the GPIO - * - * **DEPRECATED** This function is deprecated and must not be used in new code. - * - * Returns: - * GPIO number to use with Linux generic GPIO API, or one of the errno - * value on the error condition. - */ -int of_get_named_gpio(const struct device_node *np, const char *propname, - int index) -{ - struct gpio_desc *desc; - - desc = of_get_named_gpiod_flags(np, propname, index, NULL); - - if (IS_ERR(desc)) - return PTR_ERR(desc); - else - return desc_to_gpio(desc); -} -EXPORT_SYMBOL_GPL(of_get_named_gpio); - /* Converts gpio_lookup_flags into bitmask of GPIO_* values */ static unsigned long of_convert_gpio_flags(enum of_gpio_flags flags) { @@ -542,6 +516,10 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np, { "reset", "reset-n-io", "marvell,nfc-uart" }, { "reset", "reset-n-io", "mrvl,nfc-uart" }, #endif +#if IS_ENABLED(CONFIG_NFC_S3FWRN5_I2C) + { "en", "s3fwrn5,en-gpios", "samsung,s3fwrn5-i2c" }, + { "wake", "s3fwrn5,fw-gpios", "samsung,s3fwrn5-i2c" }, +#endif #if IS_ENABLED(CONFIG_PCI_LANTIQ) /* MIPS Lantiq PCI */ { "reset", "gpio-reset", "lantiq,pci-xway" }, @@ -735,139 +713,26 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id, return desc; } -/** - * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API - * @np: device node to get GPIO from - * @chip: GPIO chip whose hog is parsed - * @idx: Index of the GPIO to parse - * @name: GPIO line name - * @lflags: bitmask of gpio_lookup_flags GPIO_* values - returned from - * of_find_gpio() or of_parse_own_gpio() - * @dflags: gpiod_flags - optional GPIO initialization flags - * - * Returns: - * GPIO descriptor to use with Linux GPIO API, or one of the errno - * value on the error condition. - */ -static struct gpio_desc *of_parse_own_gpio(struct device_node *np, - struct gpio_chip *chip, - unsigned int idx, const char **name, - unsigned long *lflags, - enum gpiod_flags *dflags) +int of_gpiochip_get_lflags(struct gpio_chip *chip, + struct fwnode_reference_args *gpiospec, + unsigned long *lflags) { - struct device_node *chip_np; enum of_gpio_flags xlate_flags; - struct of_phandle_args gpiospec; + struct of_phandle_args args; struct gpio_desc *desc; - unsigned int i; - u32 tmp; - int ret; - chip_np = dev_of_node(&chip->gpiodev->dev); - if (!chip_np) - return ERR_PTR(-EINVAL); + args.np = to_of_node(gpiospec->fwnode); + args.args_count = gpiospec->nargs; - xlate_flags = 0; - *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; - *dflags = GPIOD_ASIS; + for (int i = 0; i < args.args_count; i++) + args.args[i] = gpiospec->args[i]; - ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp); - if (ret) - return ERR_PTR(ret); - - gpiospec.np = chip_np; - gpiospec.args_count = tmp; - - for (i = 0; i < tmp; i++) { - ret = of_property_read_u32_index(np, "gpios", idx * tmp + i, - &gpiospec.args[i]); - if (ret) - return ERR_PTR(ret); - } - - desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags); + desc = of_xlate_and_get_gpiod_flags(chip, &args, &xlate_flags); if (IS_ERR(desc)) - return desc; + return PTR_ERR(desc); *lflags = of_convert_gpio_flags(xlate_flags); - if (of_property_read_bool(np, "input")) - *dflags |= GPIOD_IN; - else if (of_property_read_bool(np, "output-low")) - *dflags |= GPIOD_OUT_LOW; - else if (of_property_read_bool(np, "output-high")) - *dflags |= GPIOD_OUT_HIGH; - else { - pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n", - desc_to_gpio(desc), np); - return ERR_PTR(-EINVAL); - } - - if (name && of_property_read_string(np, "line-name", name)) - *name = np->name; - - return desc; -} - -/** - * of_gpiochip_add_hog - Add all hogs in a hog device node - * @chip: gpio chip to act on - * @hog: device node describing the hogs - * - * Returns: - * 0 on success, or negative errno on failure. - */ -static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog) -{ - enum gpiod_flags dflags; - struct gpio_desc *desc; - unsigned long lflags; - const char *name; - unsigned int i; - int ret; - - for (i = 0;; i++) { - desc = of_parse_own_gpio(hog, chip, i, &name, &lflags, &dflags); - if (IS_ERR(desc)) - break; - - ret = gpiod_hog(desc, name, lflags, dflags); - if (ret < 0) - return ret; - -#ifdef CONFIG_OF_DYNAMIC - WRITE_ONCE(desc->hog, hog); -#endif - } - - return 0; -} - -/** - * of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions - * @chip: gpio chip to act on - * - * This is only used by of_gpiochip_add to request/set GPIO initial - * configuration. - * - * Returns: - * 0 on success, or negative errno on failure. - */ -static int of_gpiochip_scan_gpios(struct gpio_chip *chip) -{ - int ret; - - for_each_available_child_of_node_scoped(dev_of_node(&chip->gpiodev->dev), np) { - if (!of_property_read_bool(np, "gpio-hog")) - continue; - - ret = of_gpiochip_add_hog(chip, np); - if (ret < 0) - return ret; - - of_node_set_flag(np, OF_POPULATED); - } - return 0; } @@ -922,7 +787,7 @@ static int of_gpio_notify(struct notifier_block *nb, unsigned long action, if (!gdev) return NOTIFY_DONE; /* not for us */ - ret = of_gpiochip_add_hog(gpio_device_get_chip(gdev), rd->dn); + ret = gpiochip_add_hog(gpio_device_get_chip(gdev), of_fwnode_handle(rd->dn)); if (ret < 0) { pr_err("%s: failed to add hogs for %pOF\n", __func__, rd->dn); @@ -1201,16 +1066,24 @@ int of_gpiochip_add(struct gpio_chip *chip) of_node_get(np); - ret = of_gpiochip_scan_gpios(chip); - if (ret) - of_node_put(np); + for_each_available_child_of_node_scoped(np, child) { + if (of_property_read_bool(child, "gpio-hog")) + of_node_set_flag(child, OF_POPULATED); + } return ret; } void of_gpiochip_remove(struct gpio_chip *chip) { - of_node_put(dev_of_node(&chip->gpiodev->dev)); + struct device_node *np = dev_of_node(&chip->gpiodev->dev); + + for_each_child_of_node_scoped(np, child) { + if (of_property_present(child, "gpio-hog")) + of_node_clear_flag(child, OF_POPULATED); + } + + of_node_put(np); } bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index) diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h index 2257f7a498a1..218cfe5bc4ac 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -10,6 +10,7 @@ struct device_node; struct fwnode_handle; +struct fwnode_reference_args; struct gpio_chip; struct gpio_desc; @@ -24,6 +25,9 @@ int of_gpiochip_add(struct gpio_chip *gc); void of_gpiochip_remove(struct gpio_chip *gc); bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index); int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id); +int of_gpiochip_get_lflags(struct gpio_chip *chip, + struct fwnode_reference_args *gpiospec, + unsigned long *lflags); #else static inline struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id, @@ -44,6 +48,12 @@ static inline int of_gpio_count(const struct fwnode_handle *fwnode, { return 0; } +static inline int of_gpiochip_get_lflags(struct gpio_chip *chip, + struct fwnode_reference_args *gpiospec, + unsigned long *lflags) +{ + return -ENOENT; +} #endif /* CONFIG_OF_GPIO */ extern struct notifier_block gpio_of_notifier; diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index 0d7f3f09a0b4..4374067f621e 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -93,6 +93,14 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode, ret = swnode_gpio_get_reference(fwnode, propname, idx, &args); if (ret == 0) break; + if (ret == -ENOTCONN) + /* + * -ENOTCONN for a software node reference lookup means + * that a remote struct software_node exists but has + * not yet been registered as a firmware node. Defer + * until this happens. + */ + return ERR_PTR(-EPROBE_DEFER); } if (ret) { pr_debug("%s: can't parse '%s' property of node '%pfwP[%d]'\n", diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 270e8060e761..fc06b0c2881b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -983,10 +983,10 @@ void gpiod_unexport(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_unexport); -int gpiochip_sysfs_register(struct gpio_device *gdev) +int gpiochip_sysfs_register(struct gpio_chip *gc) { + struct gpio_device *gdev = gc->gpiodev; struct gpiodev_data *data; - struct gpio_chip *chip; struct device *parent; int err; @@ -999,18 +999,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) if (!class_is_registered(&gpio_class)) return 0; - guard(srcu)(&gdev->srcu); - - chip = srcu_dereference(gdev->chip, &gdev->srcu); - if (!chip) - return -ENODEV; - /* * For sysfs backward compatibility we need to preserve this * preferred parenting to the gpio_chip parent field, if set. */ - if (chip->parent) - parent = chip->parent; + if (gc->parent) + parent = gc->parent; else parent = &gdev->dev; @@ -1029,7 +1023,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) MKDEV(0, 0), data, gpiochip_groups, GPIOCHIP_NAME "%d", - chip->base); + gc->base); if (IS_ERR(data->cdev_base)) { err = PTR_ERR(data->cdev_base); kfree(data); @@ -1053,11 +1047,11 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) return 0; } -void gpiochip_sysfs_unregister(struct gpio_device *gdev) +void gpiochip_sysfs_unregister(struct gpio_chip *gc) { + struct gpio_device *gdev = gc->gpiodev; struct gpiodev_data *data; struct gpio_desc *desc; - struct gpio_chip *chip; guard(mutex)(&sysfs_lock); @@ -1065,13 +1059,8 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) if (!data) return; - guard(srcu)(&gdev->srcu); - chip = srcu_dereference(gdev->chip, &gdev->srcu); - if (!chip) - return; - /* unregister gpiod class devices owned by sysfs */ - for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) { + for_each_gpio_desc_with_flag(gc, desc, GPIOD_FLAG_SYSFS) { gpiod_unexport_unlocked(desc); gpiod_free(desc); } @@ -1090,10 +1079,9 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) */ static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data) { - struct gpio_device *gdev = gc->gpiodev; int ret; - ret = gpiochip_sysfs_register(gdev); + ret = gpiochip_sysfs_register(gc); if (ret) gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret); diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h index b794b396d6a5..d0998de043a2 100644 --- a/drivers/gpio/gpiolib-sysfs.h +++ b/drivers/gpio/gpiolib-sysfs.h @@ -7,17 +7,17 @@ struct gpio_device; #ifdef CONFIG_GPIO_SYSFS -int gpiochip_sysfs_register(struct gpio_device *gdev); -void gpiochip_sysfs_unregister(struct gpio_device *gdev); +int gpiochip_sysfs_register(struct gpio_chip *gc); +void gpiochip_sysfs_unregister(struct gpio_chip *gc); #else -static inline int gpiochip_sysfs_register(struct gpio_device *gdev) +static inline int gpiochip_sysfs_register(struct gpio_chip *gc) { return 0; } -static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) +static inline void gpiochip_sysfs_unregister(struct gpio_chip *gc) { } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 300de30fd920..1e6dce430dca 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -102,9 +103,6 @@ static DEFINE_MUTEX(gpio_devices_lock); /* Ensures coherence during read-only accesses to the list of GPIO devices. */ DEFINE_STATIC_SRCU(gpio_devices_srcu); -static DEFINE_MUTEX(gpio_machine_hogs_mutex); -static LIST_HEAD(gpio_machine_hogs); - const char *const gpio_suffixes[] = { "gpios", "gpio", NULL }; static void gpiochip_free_hogs(struct gpio_chip *gc); @@ -340,7 +338,15 @@ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev) } EXPORT_SYMBOL_GPL(gpio_device_get_chip); -/* dynamic allocation of GPIOs, e.g. on a hotplugged device */ +/** + * gpiochip_find_base_unlocked() - Find a global GPIO number base + * @ngpio: Number of consecutive GPIOs to number + * + * Finds and allocates a consecutive range of unsigned integers representing + * the GPIOs on the system. Using this numberspace outside of gpiolibs + * internals is STRONGLY DISCOURAGED, drivers and consumers should NOT concern + * themselves with this numberspace. + */ static int gpiochip_find_base_unlocked(u16 ngpio) { unsigned int base = GPIO_DYNAMIC_BASE; @@ -881,14 +887,14 @@ static const struct device_type gpio_dev_type = { }; #ifdef CONFIG_GPIO_CDEV -#define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) +#define gcdev_register(gc, devt) gpiolib_cdev_register((gc), (devt)) #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) #else /* * gpiolib_cdev_register() indirectly calls device_add(), which is still * required even when cdev is not selected. */ -#define gcdev_register(gdev, devt) device_add(&(gdev)->dev) +#define gcdev_register(gc, devt) device_add(&(gc)->gpiodev->dev) #define gcdev_unregister(gdev) device_del(&(gdev)->dev) #endif @@ -896,8 +902,9 @@ static const struct device_type gpio_dev_type = { * An initial reference count has been held in gpiochip_add_data_with_key(). * The caller should drop the reference via gpio_device_put() on errors. */ -static int gpiochip_setup_dev(struct gpio_device *gdev) +static int gpiochip_setup_dev(struct gpio_chip *gc) { + struct gpio_device *gdev = gc->gpiodev; struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev); int ret; @@ -908,11 +915,11 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) if (fwnode && !fwnode->dev) fwnode_dev_initialized(fwnode, false); - ret = gcdev_register(gdev, gpio_devt); + ret = gcdev_register(gc, gpio_devt); if (ret) return ret; - ret = gpiochip_sysfs_register(gdev); + ret = gpiochip_sysfs_register(gc); if (ret) goto err_remove_device; @@ -926,46 +933,131 @@ err_remove_device: return ret; } -static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) +int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode) { + struct fwnode_handle *gc_node = dev_fwnode(&gc->gpiodev->dev); + struct fwnode_reference_args gpiospec; + enum gpiod_flags dflags; + const char *name = NULL; struct gpio_desc *desc; - int rv; + unsigned int num_hogs; + unsigned long lflags; + int ret, argc; + /* + * For devicetree-based systems, this needs to be defined in bindings + * and there's no real default value. For other firmware descriptions + * it makes the most sense to use 2 cells for the GPIO offset and + * request flags. + */ + u32 cells = 2; - desc = gpiochip_get_desc(gc, hog->chip_hwnum); - if (IS_ERR(desc)) { - gpiochip_err(gc, "%s: unable to get GPIO desc: %ld\n", - __func__, PTR_ERR(desc)); - return; + lflags = GPIO_LOOKUP_FLAGS_DEFAULT; + dflags = GPIOD_ASIS; + name = NULL; + + argc = fwnode_property_count_u32(fwnode, "gpios"); + if (argc < 0) + return argc; + + ret = fwnode_property_read_u32(gc_node, "#gpio-cells", &cells); + if (ret && is_of_node(fwnode)) + return ret; + if (argc % cells) + return -EINVAL; + + num_hogs = argc / cells; + + u32 *gpios __free(kfree) = kzalloc_objs(*gpios, argc); + if (!gpios) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, argc); + if (ret < 0) + return ret; + + if (fwnode_property_present(fwnode, "input")) + dflags |= GPIOD_IN; + else if (fwnode_property_present(fwnode, "output-low")) + dflags |= GPIOD_OUT_LOW; + else if (fwnode_property_present(fwnode, "output-high")) + dflags |= GPIOD_OUT_HIGH; + else + return -EINVAL; + + fwnode_property_read_string(fwnode, "line-name", &name); + + for (unsigned int i = 0; i < num_hogs; i++) { + if (is_of_node(fwnode)) { + /* + * OF-nodes need some additional special handling for + * translating of devicetree flags. + */ + memset(&gpiospec, 0, sizeof(gpiospec)); + gpiospec.fwnode = fwnode; + gpiospec.nargs = cells; + + for (unsigned int j = 0; j < cells; j++) + gpiospec.args[j] = gpios[i * cells + j]; + + ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags); + if (ret) + return ret; + } else { + /* + * GPIO_ACTIVE_LOW is currently the only lookup flag + * supported for non-OF firmware nodes. + */ + if (gpios[i * cells + 1]) + lflags |= GPIO_ACTIVE_LOW; + } + + desc = gpiochip_get_desc(gc, gpios[i * cells]); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + ret = gpiod_hog(desc, name, lflags, dflags); + if (ret) + return ret; } - rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags); - if (rv) - gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n", - __func__, gc->label, hog->chip_hwnum, rv); + return 0; } -static void machine_gpiochip_add(struct gpio_chip *gc) +static int gpiochip_hog_lines(struct gpio_chip *gc) { - struct gpiod_hog *hog; + int ret; - guard(mutex)(&gpio_machine_hogs_mutex); + device_for_each_child_node_scoped(&gc->gpiodev->dev, fwnode) { + if (!fwnode_property_present(fwnode, "gpio-hog")) + continue; - list_for_each_entry(hog, &gpio_machine_hogs, list) { - if (!strcmp(gc->label, hog->chip_label)) - gpiochip_machine_hog(gc, hog); + ret = gpiochip_add_hog(gc, fwnode); + if (ret) + return ret; } + + return 0; } static void gpiochip_setup_devs(void) { struct gpio_device *gdev; + struct gpio_chip *gc; int ret; guard(srcu)(&gpio_devices_srcu); list_for_each_entry_srcu(gdev, &gpio_devices, list, srcu_read_lock_held(&gpio_devices_srcu)) { - ret = gpiochip_setup_dev(gdev); + guard(srcu)(&gdev->srcu); + + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) { + dev_err(&gdev->dev, "Underlying GPIO chip is gone\n"); + continue; + } + + ret = gpiochip_setup_dev(gc); if (ret) { gpio_device_put(gdev); dev_err(&gdev->dev, @@ -1197,7 +1289,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, acpi_gpiochip_add(gc); - machine_gpiochip_add(gc); + ret = gpiochip_hog_lines(gc); + if (ret) + goto err_remove_of_chip; ret = gpiochip_irqchip_init_valid_mask(gc); if (ret) @@ -1224,7 +1318,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, * Otherwise, defer until later. */ if (gpiolib_initialized) { - ret = gpiochip_setup_dev(gdev); + ret = gpiochip_setup_dev(gc); if (ret) goto err_teardown_shared; } @@ -1284,7 +1378,7 @@ void gpiochip_remove(struct gpio_chip *gc) struct gpio_device *gdev = gc->gpiodev; /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ - gpiochip_sysfs_unregister(gdev); + gpiochip_sysfs_unregister(gc); gpiochip_free_hogs(gc); gpiochip_free_remaining_irqs(gc); @@ -1389,7 +1483,16 @@ EXPORT_SYMBOL_GPL(gpio_device_find_by_label); static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, const void *fwnode) { - return device_match_fwnode(&gc->gpiodev->dev, fwnode); + struct device *dev = &gc->gpiodev->dev; + struct fwnode_handle *node = dev_fwnode(dev); + + if (IS_ERR(fwnode)) + return 0; + + if (device_match_fwnode(dev, fwnode)) + return 1; + + return node && node->secondary == fwnode; } /** @@ -4469,42 +4572,6 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) } EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); -/** - * gpiod_add_hogs() - register a set of GPIO hogs from machine code - * @hogs: table of gpio hog entries with a zeroed sentinel at the end - */ -void gpiod_add_hogs(struct gpiod_hog *hogs) -{ - struct gpiod_hog *hog; - - guard(mutex)(&gpio_machine_hogs_mutex); - - for (hog = &hogs[0]; hog->chip_label; hog++) { - list_add_tail(&hog->list, &gpio_machine_hogs); - - /* - * The chip may have been registered earlier, so check if it - * exists and, if so, try to hog the line now. - */ - struct gpio_device *gdev __free(gpio_device_put) = - gpio_device_find_by_label(hog->chip_label); - if (gdev) - gpiochip_machine_hog(gpio_device_get_chip(gdev), hog); - } -} -EXPORT_SYMBOL_GPL(gpiod_add_hogs); - -void gpiod_remove_hogs(struct gpiod_hog *hogs) -{ - struct gpiod_hog *hog; - - guard(mutex)(&gpio_machine_hogs_mutex); - - for (hog = &hogs[0]; hog->chip_label; hog++) - list_del(&hog->list); -} -EXPORT_SYMBOL_GPL(gpiod_remove_hogs); - static bool gpiod_match_lookup_table(struct device *dev, const struct gpiod_lookup_table *table) { @@ -4557,8 +4624,8 @@ static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *c return desc; } - dev_warn(dev, "cannot find GPIO line %s, deferring\n", - p->key); + dev_dbg(dev, "cannot find GPIO line %s, deferring\n", + p->key); return ERR_PTR(-EPROBE_DEFER); } @@ -4572,8 +4639,8 @@ static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *c * consumer be probed again or let the Deferred * Probe infrastructure handle the error. */ - dev_warn(dev, "cannot find GPIO chip %s, deferring\n", - p->key); + dev_dbg(dev, "cannot find GPIO chip %s, deferring\n", + p->key); return ERR_PTR(-EPROBE_DEFER); } @@ -5317,23 +5384,14 @@ core_initcall(gpiolib_dev_init); #ifdef CONFIG_DEBUG_FS -static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) +static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *gc) { bool active_low, is_irq, is_out; struct gpio_desc *desc; unsigned int gpio = 0; - struct gpio_chip *gc; unsigned long flags; int value; - guard(srcu)(&gdev->srcu); - - gc = srcu_dereference(gdev->chip, &gdev->srcu); - if (!gc) { - seq_puts(s, "Underlying GPIO chip is gone\n"); - return; - } - for_each_gpio_desc(gc, desc) { guard(srcu)(&desc->gdev->desc_srcu); flags = READ_ONCE(desc->flags); @@ -5446,7 +5504,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) if (gc->dbg_show) gc->dbg_show(s, gc); else - gpiolib_dbg_show(s, gdev); + gpiolib_dbg_show(s, gc); return 0; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 3abb90385829..dc4cb61a9318 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -17,11 +17,14 @@ #include #include #include +#include #include #include #define GPIOCHIP_NAME "gpiochip" +struct fwnode_handle; + /** * struct gpio_device - internal state container for GPIO devices * @dev: the GPIO device struct @@ -107,7 +110,7 @@ extern const char *const gpio_suffixes[]; if (con_id) \ snprintf(propname, sizeof(propname), "%s-%s", con_id, __gs); \ else \ - snprintf(propname, sizeof(propname), "%s", __gs); \ + strscpy(propname, __gs); \ 1; \ }); \ __suffixes++) @@ -273,6 +276,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce); int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags); +int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode); int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev); struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum); const char *gpiod_get_label(struct gpio_desc *desc); diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index c51d22e4579c..6efa83219117 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -6,9 +6,9 @@ */ #include -#include +#include #include -#include +#include #include #include #include @@ -112,13 +112,12 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, memcpy(&priv->config, pdata, sizeof(*pdata)); - if (gpio_is_valid(priv->config.reset_n_io)) { - rc = gpio_request_one(priv->config.reset_n_io, - GPIOF_OUT_INIT_LOW, - "nfcmrvl_reset_n"); - if (rc < 0) { - priv->config.reset_n_io = -EINVAL; - nfc_err(dev, "failed to request reset_n io\n"); + if (!priv->config.reset_gpio) { + priv->config.reset_gpio = + devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->config.reset_gpio)) { + priv->config.reset_gpio = NULL; + nfc_err(dev, "failed to get reset gpio\n"); } } @@ -144,7 +143,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, if (!priv->ndev) { nfc_err(dev, "nci_allocate_device failed\n"); rc = -ENOMEM; - goto error_free_gpio; + goto error_free; } rc = nfcmrvl_fw_dnld_init(priv); @@ -171,9 +170,7 @@ error_fw_dnld_deinit: nfcmrvl_fw_dnld_deinit(priv); error_free_dev: nci_free_device(priv->ndev); -error_free_gpio: - if (gpio_is_valid(priv->config.reset_n_io)) - gpio_free(priv->config.reset_n_io); +error_free: kfree(priv); return ERR_PTR(rc); } @@ -189,9 +186,6 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) nfcmrvl_fw_dnld_deinit(priv); - if (gpio_is_valid(priv->config.reset_n_io)) - gpio_free(priv->config.reset_n_io); - nci_free_device(ndev); kfree(priv); } @@ -233,34 +227,25 @@ void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) /* Reset possible fault of previous session */ clear_bit(NFCMRVL_PHY_ERROR, &priv->flags); - if (gpio_is_valid(priv->config.reset_n_io)) { + if (priv->config.reset_gpio) { nfc_info(priv->dev, "reset the chip\n"); - gpio_set_value(priv->config.reset_n_io, 0); + gpiod_set_value(priv->config.reset_gpio, 1); usleep_range(5000, 10000); - gpio_set_value(priv->config.reset_n_io, 1); + gpiod_set_value(priv->config.reset_gpio, 0); } else nfc_info(priv->dev, "no reset available on this interface\n"); } void nfcmrvl_chip_halt(struct nfcmrvl_private *priv) { - if (gpio_is_valid(priv->config.reset_n_io)) - gpio_set_value(priv->config.reset_n_io, 0); + if (priv->config.reset_gpio) + gpiod_set_value(priv->config.reset_gpio, 1); } int nfcmrvl_parse_dt(struct device_node *node, struct nfcmrvl_platform_data *pdata) { - int reset_n_io; - - reset_n_io = of_get_named_gpio(node, "reset-n-io", 0); - if (reset_n_io < 0) { - pr_info("no reset-n-io config\n"); - } else if (!gpio_is_valid(reset_n_io)) { - pr_err("invalid reset-n-io GPIO\n"); - return reset_n_io; - } - pdata->reset_n_io = reset_n_io; + pdata->reset_gpio = NULL; pdata->hci_muxed = of_property_read_bool(node, "hci-muxed"); return 0; diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index f61a99e553db..62fa77d587b2 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -10,6 +10,8 @@ #include "fw_dnld.h" +struct gpio_desc; + /* Define private flags: */ #define NFCMRVL_NCI_RUNNING 1 #define NFCMRVL_PHY_ERROR 2 @@ -54,7 +56,7 @@ struct nfcmrvl_platform_data { */ /* GPIO that is wired to RESET_N signal */ - int reset_n_io; + struct gpio_desc *reset_gpio; /* Tell if transport is muxed in HCI one */ bool hci_muxed; diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c index 2037cd6d4f4f..9aedd168759c 100644 --- a/drivers/nfc/nfcmrvl/uart.c +++ b/drivers/nfc/nfcmrvl/uart.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,6 @@ static unsigned int hci_muxed; static unsigned int flow_control; static unsigned int break_control; -static int reset_n_io = -EINVAL; /* * NFCMRVL NCI OPS @@ -62,9 +62,11 @@ static const struct nfcmrvl_if_ops uart_ops = { }; static int nfcmrvl_uart_parse_dt(struct device_node *node, - struct nfcmrvl_platform_data *pdata) + struct nfcmrvl_platform_data *pdata, + struct device *dev) { struct device_node *matched_node; + struct gpio_desc *reset_gpio; int ret; matched_node = of_get_compatible_child(node, "marvell,nfc-uart"); @@ -84,6 +86,16 @@ static int nfcmrvl_uart_parse_dt(struct device_node *node, pdata->flow_control = of_property_read_bool(matched_node, "flow-control"); pdata->break_control = of_property_read_bool(matched_node, "break-control"); + reset_gpio = devm_fwnode_gpiod_get_optional(dev, + of_fwnode_handle(matched_node), + "reset", GPIOD_OUT_HIGH, + "nfcmrvl_reset_n"); + if (IS_ERR(reset_gpio)) { + of_node_put(matched_node); + return PTR_ERR(reset_gpio); + } + pdata->reset_gpio = reset_gpio; + of_node_put(matched_node); return 0; @@ -107,13 +119,13 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu) */ if (dev && dev->parent && dev->parent->of_node) - if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config) == 0) + if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config, dev) == 0) pdata = &config; if (!pdata) { pr_info("No platform data / DT -> fallback to module params\n"); config.hci_muxed = hci_muxed; - config.reset_n_io = reset_n_io; + config.reset_gpio = NULL; config.flow_control = flow_control; config.break_control = break_control; pdata = &config; @@ -201,6 +213,3 @@ MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal."); module_param(hci_muxed, uint, 0); MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one."); - -module_param(reset_n_io, int, 0); -MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal."); diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index ea7309453096..ac62358445bf 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -294,7 +294,7 @@ static int nfcmrvl_probe(struct usb_interface *intf, /* No configuration for USB */ memset(&config, 0, sizeof(config)); - config.reset_n_io = -EINVAL; + config.reset_gpio = NULL; nfc_info(&udev->dev, "intf %p id %p\n", intf, id); diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index 110d086cfe5b..91b8d1445efd 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -8,10 +8,8 @@ #include #include -#include +#include #include -#include -#include #include #include @@ -146,37 +144,6 @@ out: return IRQ_HANDLED; } -static int s3fwrn5_i2c_parse_dt(struct i2c_client *client) -{ - struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client); - struct device_node *np = client->dev.of_node; - - if (!np) - return -ENODEV; - - phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0); - if (!gpio_is_valid(phy->common.gpio_en)) { - /* Support also deprecated property */ - phy->common.gpio_en = of_get_named_gpio(np, - "s3fwrn5,en-gpios", - 0); - if (!gpio_is_valid(phy->common.gpio_en)) - return -ENODEV; - } - - phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0); - if (!gpio_is_valid(phy->common.gpio_fw_wake)) { - /* Support also deprecated property */ - phy->common.gpio_fw_wake = of_get_named_gpio(np, - "s3fwrn5,fw-gpios", - 0); - if (!gpio_is_valid(phy->common.gpio_fw_wake)) - return -ENODEV; - } - - return 0; -} - static int s3fwrn5_i2c_probe(struct i2c_client *client) { struct s3fwrn5_i2c_phy *phy; @@ -193,20 +160,13 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client) phy->i2c_dev = client; i2c_set_clientdata(client, phy); - ret = s3fwrn5_i2c_parse_dt(client); - if (ret < 0) - return ret; + phy->common.gpio_en = devm_gpiod_get(&client->dev, "en", GPIOD_OUT_HIGH); + if (IS_ERR(phy->common.gpio_en)) + return PTR_ERR(phy->common.gpio_en); - ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->common.gpio_en, - GPIOF_OUT_INIT_HIGH, "s3fwrn5_en"); - if (ret < 0) - return ret; - - ret = devm_gpio_request_one(&phy->i2c_dev->dev, - phy->common.gpio_fw_wake, - GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake"); - if (ret < 0) - return ret; + phy->common.gpio_fw_wake = devm_gpiod_get(&client->dev, "wake", GPIOD_OUT_LOW); + if (IS_ERR(phy->common.gpio_fw_wake)) + return PTR_ERR(phy->common.gpio_fw_wake); /* * S3FWRN5 depends on a clock input ("XI" pin) to function properly. diff --git a/drivers/nfc/s3fwrn5/phy_common.c b/drivers/nfc/s3fwrn5/phy_common.c index deb2c039f0fd..39a60e34136c 100644 --- a/drivers/nfc/s3fwrn5/phy_common.c +++ b/drivers/nfc/s3fwrn5/phy_common.c @@ -8,7 +8,6 @@ * Bongsu Jeon */ -#include #include #include @@ -19,7 +18,7 @@ void s3fwrn5_phy_set_wake(void *phy_id, bool wake) struct phy_common *phy = phy_id; mutex_lock(&phy->mutex); - gpio_set_value(phy->gpio_fw_wake, wake); + gpiod_set_value(phy->gpio_fw_wake, wake); if (wake) msleep(S3FWRN5_EN_WAIT_TIME); mutex_unlock(&phy->mutex); @@ -33,14 +32,14 @@ bool s3fwrn5_phy_power_ctrl(struct phy_common *phy, enum s3fwrn5_mode mode) phy->mode = mode; - gpio_set_value(phy->gpio_en, 1); - gpio_set_value(phy->gpio_fw_wake, 0); + gpiod_set_value(phy->gpio_en, 1); + gpiod_set_value(phy->gpio_fw_wake, 0); if (mode == S3FWRN5_MODE_FW) - gpio_set_value(phy->gpio_fw_wake, 1); + gpiod_set_value(phy->gpio_fw_wake, 1); if (mode != S3FWRN5_MODE_COLD) { msleep(S3FWRN5_EN_WAIT_TIME); - gpio_set_value(phy->gpio_en, 0); + gpiod_set_value(phy->gpio_en, 0); msleep(S3FWRN5_EN_WAIT_TIME); } diff --git a/drivers/nfc/s3fwrn5/phy_common.h b/drivers/nfc/s3fwrn5/phy_common.h index 9cef25436bf9..871bec53dd0a 100644 --- a/drivers/nfc/s3fwrn5/phy_common.h +++ b/drivers/nfc/s3fwrn5/phy_common.h @@ -12,6 +12,7 @@ #define __NFC_S3FWRN5_PHY_COMMON_H #include +#include #include #include "s3fwrn5.h" @@ -21,8 +22,8 @@ struct phy_common { struct nci_dev *ndev; - int gpio_en; - int gpio_fw_wake; + struct gpio_desc *gpio_en; + struct gpio_desc *gpio_fw_wake; struct mutex mutex; diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c index 4ee481bd7e96..540a4ddb0b05 100644 --- a/drivers/nfc/s3fwrn5/uart.c +++ b/drivers/nfc/s3fwrn5/uart.c @@ -10,13 +10,12 @@ #include #include +#include #include #include #include -#include #include -#include -#include +#include #include "phy_common.h" @@ -92,25 +91,6 @@ static const struct of_device_id s3fwrn82_uart_of_match[] = { }; MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match); -static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev) -{ - struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); - struct device_node *np = serdev->dev.of_node; - - if (!np) - return -ENODEV; - - phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0); - if (!gpio_is_valid(phy->common.gpio_en)) - return -ENODEV; - - phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0); - if (!gpio_is_valid(phy->common.gpio_fw_wake)) - return -ENODEV; - - return 0; -} - static int s3fwrn82_uart_probe(struct serdev_device *serdev) { struct s3fwrn82_uart_phy *phy; @@ -144,20 +124,17 @@ static int s3fwrn82_uart_probe(struct serdev_device *serdev) serdev_device_set_flow_control(serdev, false); - ret = s3fwrn82_uart_parse_dt(serdev); - if (ret < 0) + phy->common.gpio_en = devm_gpiod_get(&serdev->dev, "en", GPIOD_OUT_HIGH); + if (IS_ERR(phy->common.gpio_en)) { + ret = PTR_ERR(phy->common.gpio_en); goto err_serdev; + } - ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->common.gpio_en, - GPIOF_OUT_INIT_HIGH, "s3fwrn82_en"); - if (ret < 0) - goto err_serdev; - - ret = devm_gpio_request_one(&phy->ser_dev->dev, - phy->common.gpio_fw_wake, - GPIOF_OUT_INIT_LOW, "s3fwrn82_fw_wake"); - if (ret < 0) + phy->common.gpio_fw_wake = devm_gpiod_get(&serdev->dev, "wake", GPIOD_OUT_LOW); + if (IS_ERR(phy->common.gpio_fw_wake)) { + ret = PTR_ERR(phy->common.gpio_fw_wake); goto err_serdev; + } ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev, &uart_phy_ops); diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index b5e97689589f..da0a07742460 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "core.h" @@ -938,6 +939,36 @@ int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset, } EXPORT_SYMBOL_GPL(pinctrl_gpio_set_config); +/** + * pinctrl_gpio_get_config() - Get the config for a given GPIO pin + * @gc: GPIO chip structure from the GPIO subsystem + * @offset: hardware offset of the GPIO relative to the controller + * @config: the configuration to query. On success it holds the result + * Return: 0 on success, negative errno otherwise + */ +int pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, unsigned long *config) +{ + struct pinctrl_gpio_range *range; + struct pinctrl_dev *pctldev; + int ret, pin; + + ret = pinctrl_get_device_gpio_range(gc, offset, &pctldev, &range); + if (ret) + return ret; + + mutex_lock(&pctldev->mutex); + pin = gpio_to_pin(range, gc, offset); + ret = pin_config_get_for_pin(pctldev, pin, config); + mutex_unlock(&pctldev->mutex); + + if (ret) + return ret; + + *config = pinconf_to_config_argument(*config); + return 0; +} +EXPORT_SYMBOL_GPL(pinctrl_gpio_get_config); + static struct pinctrl_state *find_state(struct pinctrl *p, const char *name) { diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 2880adef476e..659a781e2091 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -74,6 +74,12 @@ static inline int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned int p return -ENOTSUPP; } +static inline int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + return -ENOTSUPP; +} + #endif #if defined(CONFIG_PINCONF) && defined(CONFIG_DEBUG_FS) diff --git a/drivers/pinctrl/pinctrl-scmi.c b/drivers/pinctrl/pinctrl-scmi.c index f4f296e07be5..f22be6b7b82a 100644 --- a/drivers/pinctrl/pinctrl-scmi.c +++ b/drivers/pinctrl/pinctrl-scmi.c @@ -251,15 +251,9 @@ static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param, case PIN_CONFIG_MODE_LOW_POWER: *type = SCMI_PIN_LOW_POWER_MODE; break; - case PIN_CONFIG_LEVEL: - *type = SCMI_PIN_OUTPUT_VALUE; - break; case PIN_CONFIG_OUTPUT_ENABLE: *type = SCMI_PIN_OUTPUT_MODE; break; - case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: - *type = SCMI_PIN_OUTPUT_VALUE; - break; case PIN_CONFIG_POWER_SOURCE: *type = SCMI_PIN_POWER_SOURCE; break; @@ -276,6 +270,28 @@ static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param, return 0; } +static int pinctrl_scmi_map_pinconf_type_get(enum pin_config_param param, + enum scmi_pinctrl_conf_type *type) +{ + if (param == PIN_CONFIG_LEVEL) { + *type = SCMI_PIN_INPUT_VALUE; + return 0; + } + + return pinctrl_scmi_map_pinconf_type(param, type); +} + +static int pinctrl_scmi_map_pinconf_type_set(enum pin_config_param param, + enum scmi_pinctrl_conf_type *type) +{ + if (param == PIN_CONFIG_LEVEL) { + *type = SCMI_PIN_OUTPUT_VALUE; + return 0; + } + + return pinctrl_scmi_map_pinconf_type(param, type); +} + static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) { @@ -290,7 +306,7 @@ static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev, config_type = pinconf_to_config_param(*config); - ret = pinctrl_scmi_map_pinconf_type(config_type, &type); + ret = pinctrl_scmi_map_pinconf_type_get(config_type, &type); if (ret) return ret; @@ -345,7 +361,7 @@ static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev, unsigned long *configs, unsigned int num_configs) { - int i, ret; + int i, cnt, ret; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS]; u32 config_value[SCMI_NUM_CONFIGS]; @@ -361,17 +377,21 @@ static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev, if (ret) return ret; + cnt = 0; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); - ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); + if (param == PIN_CONFIG_PERSIST_STATE) + continue; + ret = pinctrl_scmi_map_pinconf_type_set(param, &p_config_type[cnt]); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); goto free_config; } - p_config_value[i] = pinconf_to_config_argument(configs[i]); + p_config_value[cnt] = pinconf_to_config_argument(configs[i]); + cnt++; } - ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, num_configs, + ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, cnt, p_config_type, p_config_value); if (ret) dev_err(pmx->dev, "Error parsing config %d\n", ret); @@ -405,7 +425,7 @@ static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev, for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); - ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); + ret = pinctrl_scmi_map_pinconf_type_set(param, &p_config_type[i]); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); goto free_config; @@ -440,7 +460,7 @@ static int pinctrl_scmi_pinconf_group_get(struct pinctrl_dev *pctldev, return -EINVAL; config_type = pinconf_to_config_param(*config); - ret = pinctrl_scmi_map_pinconf_type(config_type, &type); + ret = pinctrl_scmi_map_pinconf_type_get(config_type, &type); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); return ret; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index d10b6f9243d5..10e64e3ffb1f 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1232,7 +1232,6 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY depends on ARM || ARM64 || COMPILE_TEST depends on BACKLIGHT_CLASS_DEVICE depends on I2C - depends on OF_GPIO select REGMAP_I2C help This driver supports ATTINY regulator on the Raspberry Pi 7-inch @@ -1332,7 +1331,6 @@ config REGULATOR_RT5133 depends on I2C && GPIOLIB && OF select REGMAP select CRC8 - select OF_GPIO help This driver adds support for RT5133 PMIC regulators. RT5133 is an integrated chip. It includes 8 LDOs and 3 GPOs that diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 0d8408582918..3efb5cb1e1d1 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -6,6 +6,8 @@ #include #include +#include "defs.h" + struct acpi_device; struct device; struct fwnode_handle; diff --git a/include/linux/gpio/defs.h b/include/linux/gpio/defs.h new file mode 100644 index 000000000000..b69fd7c041b2 --- /dev/null +++ b/include/linux/gpio/defs.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LINUX_GPIO_DEFS_H +#define __LINUX_GPIO_DEFS_H + +#define GPIO_LINE_DIRECTION_IN 1 +#define GPIO_LINE_DIRECTION_OUT 0 + +#endif /* __LINUX_GPIO_DEFS_H */ diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index fabe2baf7b50..17511434ed07 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -20,6 +20,8 @@ #include #endif +#include "defs.h" + struct device; struct irq_chip; struct irq_data; @@ -42,9 +44,6 @@ union gpio_irq_fwspec { #endif }; -#define GPIO_LINE_DIRECTION_IN 1 -#define GPIO_LINE_DIRECTION_OUT 0 - /** * struct gpio_irq_chip - GPIO interrupt controller */ @@ -344,11 +343,17 @@ struct gpio_irq_chip { * @direction_output: configures signal "offset" as output, returns 0 on * success or a negative error number. This can be omitted on input-only * or output-only gpio chips. - * @get: returns value for signal "offset", 0=low, 1=high, or negative error + * @get: returns value for signal "offset", 0=low, 1=high, or negative error. + * the low and high values are defined as physical low on the line + * in/out to the connector such as a physical pad, pin or rail. The GPIO + * library has internal logic to handle lines that are active low, such + * as indicated by overstrike or #name in a schematic, and the driver + * should not try to second-guess the logic value of a line. * @get_multiple: reads values for multiple signals defined by "mask" and * stores them in "bits", returns 0 on success or negative error * @set: assigns output value for signal "offset", returns 0 on success or - * negative error value + * negative error value. The output value follows the same semantic + * rules as for @get. * @set_multiple: assigns output values for multiple signals defined by * "mask", returns 0 on success or negative error value * @set_config: optional hook for all kinds of settings. Uses the same diff --git a/include/linux/gpio/generic.h b/include/linux/gpio/generic.h index ff566dc9c3cb..de43c06c83ef 100644 --- a/include/linux/gpio/generic.h +++ b/include/linux/gpio/generic.h @@ -3,9 +3,15 @@ #ifndef __LINUX_GPIO_GENERIC_H #define __LINUX_GPIO_GENERIC_H +#include +#include #include -#include +#include +#include #include +#include + +#include struct device; diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 44e5f162973e..5eb88f5d0630 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -46,23 +46,6 @@ struct gpiod_lookup_table { struct gpiod_lookup table[]; }; -/** - * struct gpiod_hog - GPIO line hog table - * @chip_label: name of the chip the GPIO belongs to - * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO - * @line_name: consumer name for the hogged line - * @lflags: bitmask of gpio_lookup_flags GPIO_* values - * @dflags: GPIO flags used to specify the direction and value - */ -struct gpiod_hog { - struct list_head list; - const char *chip_label; - u16 chip_hwnum; - const char *line_name; - unsigned long lflags; - int dflags; -}; - /* * Helper for lookup tables with just one single lookup for a device. */ @@ -95,24 +78,10 @@ static struct gpiod_lookup_table _name = { \ .flags = _flags, \ } -/* - * Simple definition of a single GPIO hog in an array. - */ -#define GPIO_HOG(_chip_label, _chip_hwnum, _line_name, _lflags, _dflags) \ -(struct gpiod_hog) { \ - .chip_label = _chip_label, \ - .chip_hwnum = _chip_hwnum, \ - .line_name = _line_name, \ - .lflags = _lflags, \ - .dflags = _dflags, \ -} - #ifdef CONFIG_GPIOLIB void gpiod_add_lookup_table(struct gpiod_lookup_table *table); void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n); void gpiod_remove_lookup_table(struct gpiod_lookup_table *table); -void gpiod_add_hogs(struct gpiod_hog *hogs); -void gpiod_remove_hogs(struct gpiod_hog *hogs); #else /* ! CONFIG_GPIOLIB */ static inline void gpiod_add_lookup_table(struct gpiod_lookup_table *table) {} @@ -120,8 +89,6 @@ static inline void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) {} static inline void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) {} -static inline void gpiod_add_hogs(struct gpiod_hog *hogs) {} -static inline void gpiod_remove_hogs(struct gpiod_hog *hogs) {} #endif /* CONFIG_GPIOLIB */ #endif /* __LINUX_GPIO_MACHINE_H */ diff --git a/include/linux/mfd/kempld.h b/include/linux/mfd/kempld.h index 643c096b93ac..2dbd80abfd1d 100644 --- a/include/linux/mfd/kempld.h +++ b/include/linux/mfd/kempld.h @@ -37,6 +37,7 @@ #define KEMPLD_SPEC_GET_MINOR(x) (x & 0x0f) #define KEMPLD_SPEC_GET_MAJOR(x) ((x >> 4) & 0x0f) #define KEMPLD_IRQ_GPIO 0x35 +#define KEMPLD_IRQ_GPIO_MASK 0x0f #define KEMPLD_IRQ_I2C 0x36 #define KEMPLD_CFG 0x37 #define KEMPLD_CFG_GPIO_I2C_MUX (1 << 0) diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h deleted file mode 100644 index d0f66a5e1b2a..000000000000 --- a/include/linux/of_gpio.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * OF helpers for the GPIO API - * - * Copyright (c) 2007-2008 MontaVista Software, Inc. - * - * Author: Anton Vorontsov - */ - -#ifndef __LINUX_OF_GPIO_H -#define __LINUX_OF_GPIO_H - -#include -#include -#include /* FIXME: Shouldn't be here */ -#include - -struct device_node; - -#ifdef CONFIG_OF_GPIO - -extern int of_get_named_gpio(const struct device_node *np, - const char *list_name, int index); - -#else /* CONFIG_OF_GPIO */ - -#include - -/* Drivers may not strictly depend on the GPIO support, so let them link. */ -static inline int of_get_named_gpio(const struct device_node *np, - const char *propname, int index) -{ - return -ENOSYS; -} - -#endif /* CONFIG_OF_GPIO */ - -#endif /* __LINUX_OF_GPIO_H */ diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 63ce16191eb9..11b8f0b8da0c 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -35,6 +35,8 @@ int pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset); int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config); +int pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, + unsigned long *config); struct pinctrl * __must_check pinctrl_get(struct device *dev); void pinctrl_put(struct pinctrl *p); @@ -101,6 +103,13 @@ pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset) return 0; } +static inline int +pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, + unsigned long *config) +{ + return 0; +} + static inline int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index 7bfe315f7001..741ab21e1260 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_PROGS := gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh +TEST_PROGS := gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh gpio-cdev-uaf.sh TEST_FILES := gpio-mockup-sysfs.sh -TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name +TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name \ + gpio-cdev-uaf CFLAGS += -O2 -g -Wall $(KHDR_INCLUDES) include ../lib.mk diff --git a/tools/testing/selftests/gpio/gpio-cdev-uaf.c b/tools/testing/selftests/gpio/gpio-cdev-uaf.c new file mode 100644 index 000000000000..765d3cc4f0ef --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-cdev-uaf.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GPIO character device helper for UAF tests. + * + * Copyright 2026 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONFIGFS_DIR "/sys/kernel/config/gpio-sim" +#define PROCFS_DIR "/proc" + +static void print_usage(void) +{ + printf("usage:\n"); + printf(" gpio-cdev-uaf [chip|handle|event|req] [poll|read|ioctl]\n"); +} + +static int _create_chip(const char *name, int create) +{ + char path[64]; + + snprintf(path, sizeof(path), CONFIGFS_DIR "/%s", name); + + if (create) + return mkdir(path, 0755); + else + return rmdir(path); +} + +static int create_chip(const char *name) +{ + return _create_chip(name, 1); +} + +static void remove_chip(const char *name) +{ + _create_chip(name, 0); +} + +static int _create_bank(const char *chip_name, const char *name, int create) +{ + char path[64]; + + snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s", chip_name, name); + + if (create) + return mkdir(path, 0755); + else + return rmdir(path); +} + +static int create_bank(const char *chip_name, const char *name) +{ + return _create_bank(chip_name, name, 1); +} + +static void remove_bank(const char *chip_name, const char *name) +{ + _create_bank(chip_name, name, 0); +} + +static int _enable_chip(const char *name, int enable) +{ + char path[64]; + int fd, ret; + + snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/live", name); + + fd = open(path, O_WRONLY); + if (fd == -1) + return fd; + + if (enable) + ret = write(fd, "1", 1); + else + ret = write(fd, "0", 1); + + close(fd); + return ret == 1 ? 0 : -1; +} + +static int enable_chip(const char *name) +{ + return _enable_chip(name, 1); +} + +static void disable_chip(const char *name) +{ + _enable_chip(name, 0); +} + +static int open_chip(const char *chip_name, const char *bank_name) +{ + char path[64], dev_name[32]; + int ret, fd; + + ret = create_chip(chip_name); + if (ret) { + fprintf(stderr, "failed to create chip\n"); + return ret; + } + + ret = create_bank(chip_name, bank_name); + if (ret) { + fprintf(stderr, "failed to create bank\n"); + goto err_remove_chip; + } + + ret = enable_chip(chip_name); + if (ret) { + fprintf(stderr, "failed to enable chip\n"); + goto err_remove_bank; + } + + snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s/chip_name", + chip_name, bank_name); + + fd = open(path, O_RDONLY); + if (fd == -1) { + ret = fd; + fprintf(stderr, "failed to open %s\n", path); + goto err_disable_chip; + } + + ret = read(fd, dev_name, sizeof(dev_name) - 1); + close(fd); + if (ret == -1) { + fprintf(stderr, "failed to read %s\n", path); + goto err_disable_chip; + } + dev_name[ret] = '\0'; + if (ret && dev_name[ret - 1] == '\n') + dev_name[ret - 1] = '\0'; + + snprintf(path, sizeof(path), "/dev/%s", dev_name); + + fd = open(path, O_RDWR); + if (fd == -1) { + ret = fd; + fprintf(stderr, "failed to open %s\n", path); + goto err_disable_chip; + } + + return fd; +err_disable_chip: + disable_chip(chip_name); +err_remove_bank: + remove_bank(chip_name, bank_name); +err_remove_chip: + remove_chip(chip_name); + return ret; +} + +static void close_chip(const char *chip_name, const char *bank_name) +{ + disable_chip(chip_name); + remove_bank(chip_name, bank_name); + remove_chip(chip_name); +} + +static int test_poll(int fd) +{ + struct pollfd pfds; + + pfds.fd = fd; + pfds.events = POLLIN; + pfds.revents = 0; + + if (poll(&pfds, 1, 0) == -1) + return -1; + + return (pfds.revents & ~(POLLHUP | POLLERR)) ? -1 : 0; +} + +static int test_read(int fd) +{ + char data; + + if (read(fd, &data, 1) == -1 && errno == ENODEV) + return 0; + return -1; +} + +static int test_ioctl(int fd) +{ + if (ioctl(fd, 0, NULL) == -1 && errno == ENODEV) + return 0; + return -1; +} + +int main(int argc, char **argv) +{ + int cfd, fd, ret; + int (*test_func)(int); + + if (argc != 3) { + print_usage(); + return EXIT_FAILURE; + } + + if (strcmp(argv[1], "chip") == 0 || + strcmp(argv[1], "event") == 0 || + strcmp(argv[1], "req") == 0) { + if (strcmp(argv[2], "poll") && + strcmp(argv[2], "read") && + strcmp(argv[2], "ioctl")) { + fprintf(stderr, "unknown command: %s\n", argv[2]); + return EXIT_FAILURE; + } + } else if (strcmp(argv[1], "handle") == 0) { + if (strcmp(argv[2], "ioctl")) { + fprintf(stderr, "unknown command: %s\n", argv[2]); + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "unknown command: %s\n", argv[1]); + return EXIT_FAILURE; + } + + if (strcmp(argv[2], "poll") == 0) + test_func = test_poll; + else if (strcmp(argv[2], "read") == 0) + test_func = test_read; + else /* strcmp(argv[2], "ioctl") == 0 */ + test_func = test_ioctl; + + cfd = open_chip("chip", "bank"); + if (cfd == -1) { + fprintf(stderr, "failed to open chip\n"); + return EXIT_FAILURE; + } + + /* Step 1: Hold a FD to the test target. */ + if (strcmp(argv[1], "chip") == 0) { + fd = cfd; + } else if (strcmp(argv[1], "handle") == 0) { + struct gpiohandle_request req = {0}; + + req.lines = 1; + if (ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req) == -1) { + fprintf(stderr, "failed to get handle FD\n"); + goto err_close_chip; + } + + close(cfd); + fd = req.fd; + } else if (strcmp(argv[1], "event") == 0) { + struct gpioevent_request req = {0}; + + if (ioctl(cfd, GPIO_GET_LINEEVENT_IOCTL, &req) == -1) { + fprintf(stderr, "failed to get event FD\n"); + goto err_close_chip; + } + + close(cfd); + fd = req.fd; + } else { /* strcmp(argv[1], "req") == 0 */ + struct gpio_v2_line_request req = {0}; + + req.num_lines = 1; + if (ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req) == -1) { + fprintf(stderr, "failed to get req FD\n"); + goto err_close_chip; + } + + close(cfd); + fd = req.fd; + } + + /* Step 2: Free the chip. */ + close_chip("chip", "bank"); + + /* Step 3: Access the dangling FD to trigger UAF. */ + ret = test_func(fd); + close(fd); + return ret ? EXIT_FAILURE : EXIT_SUCCESS; +err_close_chip: + close(cfd); + close_chip("chip", "bank"); + return EXIT_FAILURE; +} diff --git a/tools/testing/selftests/gpio/gpio-cdev-uaf.sh b/tools/testing/selftests/gpio/gpio-cdev-uaf.sh new file mode 100755 index 000000000000..6e47533019cf --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-cdev-uaf.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2026 Google LLC + +BASE_DIR=`dirname $0` +MODULE="gpio-cdev-uaf" + +fail() { + echo "$*" >&2 + echo "GPIO $MODULE test FAIL" + exit 1 +} + +skip() { + echo "$*" >&2 + echo "GPIO $MODULE test SKIP" + exit 4 +} + +# Load the gpio-sim module. This will pull in configfs if needed too. +modprobe gpio-sim || skip "unable to load the gpio-sim module" +# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed. +for _ in `seq 5`; do + mountpoint -q /sys/kernel/config && break + mount -t configfs none /sys/kernel/config + sleep 0.1 +done +mountpoint -q /sys/kernel/config || \ + skip "configfs not mounted at /sys/kernel/config" + +echo "1. GPIO" + +echo "1.1. poll" +$BASE_DIR/gpio-cdev-uaf chip poll || fail "failed to test chip poll" +echo "1.2. read" +$BASE_DIR/gpio-cdev-uaf chip read || fail "failed to test chip read" +echo "1.3. ioctl" +$BASE_DIR/gpio-cdev-uaf chip ioctl || fail "failed to test chip ioctl" + +echo "2. linehandle" + +echo "2.1. ioctl" +$BASE_DIR/gpio-cdev-uaf handle ioctl || fail "failed to test handle ioctl" + +echo "3. lineevent" + +echo "3.1. read" +$BASE_DIR/gpio-cdev-uaf event read || fail "failed to test event read" +echo "3.2. poll" +$BASE_DIR/gpio-cdev-uaf event poll || fail "failed to test event poll" +echo "3.3. ioctl" +$BASE_DIR/gpio-cdev-uaf event ioctl || fail "failed to test event ioctl" + +echo "4. linereq" + +echo "4.1. read" +$BASE_DIR/gpio-cdev-uaf req read || fail "failed to test req read" +echo "4.2. poll" +$BASE_DIR/gpio-cdev-uaf req poll || fail "failed to test req poll" +echo "4.3. ioctl" +$BASE_DIR/gpio-cdev-uaf req ioctl || fail "failed to test req ioctl" + +echo "GPIO $MODULE test PASS"