From b2d51bc1601c762c63f19c119589a0a0c44bc8ec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 26 Feb 2026 10:20:23 +0100 Subject: gpio: generic: Don't use 'proxy' headers Update header inclusions to follow IWYU (Include What You Use) principle. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260226092023.4096921-1-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/generic.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'include/linux') 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; -- cgit v1.2.3 From fa4a3a95139e7293c1333a33bd7b19e7261e3bd0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 23 Feb 2026 18:20:06 +0100 Subject: gpio: introduce a header for symbols shared by suppliers and consumers GPIO_LINE_DIRECTION_IN/OUT definitions are used both in supplier (GPIO controller drivers) as well as consumer code. In order to not force the consumers to include gpio/driver.h or - even worse - to redefine these values, create a new header file - gpio/defs.h - and move them over there. Include this header from both gpio/consumer.h and gpio/driver.h. Reviewed-by: Linus Walleij Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260223172006.204268-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/consumer.h | 2 ++ include/linux/gpio/defs.h | 9 +++++++++ include/linux/gpio/driver.h | 5 ++--- 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 include/linux/gpio/defs.h (limited to 'include/linux') 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..5f5ddcbfa445 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 */ -- cgit v1.2.3 From 5645f805927c9bd4443e6143e487ef3ffea34aaf Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 6 Mar 2026 14:22:00 +0100 Subject: gpio: Document line value semantics It is not clearly documented that the GPIO driver API expect the driver to get/set the physical level of the GPIO line and the consumer API will get/set the logic level. Document this in relevant places. Reported-by: David Jander Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260306-gpio-doc-levels-v1-1-19928739e400@kernel.org Signed-off-by: Bartosz Golaszewski --- Documentation/driver-api/gpio/driver.rst | 27 +++++++++++++++++++++++++++ include/linux/gpio/driver.h | 10 ++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) (limited to 'include/linux') 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/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 5f5ddcbfa445..17511434ed07 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -343,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 -- cgit v1.2.3 From b6420bd5aa0c374331bad6c0fa2eb5f0f87cf5a0 Mon Sep 17 00:00:00 2001 From: Jialu Xu Date: Sat, 7 Mar 2026 11:06:26 +0800 Subject: gpio: remove of_get_named_gpio() and All in-tree consumers have been converted to the descriptor-based API. Remove the deprecated of_get_named_gpio() helper, delete the header, and drop the corresponding entry from MAINTAINERS. Also remove the completed TODO item for this cleanup. Signed-off-by: Jialu Xu Reviewed-by: Linus Walleij Link: https://patch.msgid.link/02ABDA1F9E3FAF1F+20260307030623.3495092-6-xujialu@vimux.org Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 1 - drivers/gpio/TODO | 28 ---------------------------- drivers/gpio/gpiolib-of.c | 27 --------------------------- include/linux/of_gpio.h | 38 -------------------------------------- 4 files changed, 94 deletions(-) delete mode 100644 include/linux/of_gpio.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..24b3f8d2a64c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10959,7 +10959,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 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/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 3bdd9af67447..c512d735e85f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -446,32 +445,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) { 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 */ -- cgit v1.2.3 From dea046e7f46f2357124a465e058c92cac3e351c5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 9 Mar 2026 13:42:41 +0100 Subject: gpio: remove machine hogs With no more users, remove legacy machine hog API from the kernel. Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260309-gpio-hog-fwnode-v2-5-4e61f3dbf06a@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- Documentation/driver-api/gpio/board.rst | 16 -------- drivers/gpio/gpiolib.c | 71 --------------------------------- include/linux/gpio/machine.h | 33 --------------- 3 files changed, 120 deletions(-) (limited to 'include/linux') diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index 069b54d8591b..0993cac891fb 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -239,22 +239,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/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4a57d9882600..56fda7891d55 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -103,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); @@ -930,36 +927,6 @@ err_remove_device: return ret; } -static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) -{ - struct gpio_desc *desc; - int rv; - - 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; - } - - 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); -} - -static void gpiochip_machine_hog_lines(struct gpio_chip *gc) -{ - struct gpiod_hog *hog; - - guard(mutex)(&gpio_machine_hogs_mutex); - - list_for_each_entry(hog, &gpio_machine_hogs, list) { - if (!strcmp(gc->label, hog->chip_label)) - gpiochip_machine_hog(gc, hog); - } -} - int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode) { struct fwnode_handle *gc_node = dev_fwnode(&gc->gpiodev->dev); @@ -1047,8 +1014,6 @@ static int gpiochip_hog_lines(struct gpio_chip *gc) return ret; } - gpiochip_machine_hog_lines(gc); - return 0; } @@ -4578,42 +4543,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) { 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 */ -- cgit v1.2.3 From a25f48fd920b557e6ad02f692f690520c82f5914 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Wed, 11 Mar 2026 15:31:20 +0100 Subject: gpio: kempld: Implement the interrupt controller Add a GPIO IRQ chip implementation for the kempld GPIO controller. Of note is only how the parent IRQ is obtained. The IRQ for the GPIO controller can be configured in the BIOS, along with the IRQ for the I2C controller. These IRQ are returned by ACPI but this information is only usable if both IRQ are configured. When only one is configured, only one is returned making it impossible to know which one it is. Luckily the BIOS will set the configured IRQ in the PLD registers, so it can be read from there instead, and that also work on platforms without ACPI. The vendor driver allowed to override the IRQ using a module parameters, so there are boards in field which used this parameter instead of properly configuring the BIOS. This implementation provides this as well for compatibility. Signed-off-by: Alban Bedel Link: https://patch.msgid.link/20260311143120.2179347-5-alban.bedel@lht.dlh.de Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-kempld.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/kempld.h | 1 + 3 files changed, 194 insertions(+) (limited to 'include/linux') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b45fb799e36c..d665afe19709 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1440,6 +1440,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. diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c index 7dd94ff6f2df..5a63df3ea5fa 100644 --- a/drivers/gpio/gpio-kempld.c +++ b/drivers/gpio/gpio-kempld.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,13 +20,26 @@ #define KEMPLD_GPIO_MASK(x) (BIT((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; }; /* @@ -193,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; @@ -247,6 +435,10 @@ static int kempld_gpio_probe(struct platform_device *pdev) 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/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) -- cgit v1.2.3 From 96b76f7bc575ac6c69090f4642e424b04fb6784c Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Mon, 23 Mar 2026 22:01:10 +0300 Subject: pinctrl: introduce pinctrl_gpio_get_config() This is a counterpart of pinctrl_gpio_set_config(), which will be used to implement the ->get() interface in a GPIO driver for SCMI. This also requires that we create a stub so pin_config_get_for_pin() can build when CONFIG_PINCONF is disabled. Signed-off-by: AKASHI Takahiro Signed-off-by: Dan Carpenter Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 31 +++++++++++++++++++++++++++++++ drivers/pinctrl/pinconf.h | 6 ++++++ include/linux/pinctrl/consumer.h | 9 +++++++++ 3 files changed, 46 insertions(+) (limited to 'include/linux') 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/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) -- cgit v1.2.3