From 5ccb19ae1cb3620a76254db5c60121d092d9a0f6 Mon Sep 17 00:00:00 2001 From: Arun T Date: Fri, 10 Apr 2026 19:38:58 +0530 Subject: [PATCH 01/62] gpio: usbio: Add ACPI device-id for NVL platforms Add device IDs of Nova Lake into gpio-usbio support list. Signed-off-by: Arun T Reviewed-by: Vadillo Miguel Reviewed-by: Sakari Ailus Link: https://patch.msgid.link/20260410140858.585609-2-arun.t@intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-usbio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-usbio.c b/drivers/gpio/gpio-usbio.c index 34d42c743d5b..489c8ac6299e 100644 --- a/drivers/gpio/gpio-usbio.c +++ b/drivers/gpio/gpio-usbio.c @@ -31,6 +31,7 @@ static const struct acpi_device_id usbio_gpio_acpi_hids[] = { { "INTC10B5" }, /* LNL */ { "INTC10D1" }, /* MTL-CVF */ { "INTC10E2" }, /* PTL */ + { "INTC1116" }, /* NVL */ { } }; From a56604e397575647bfc425a8df176948577a364e Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 17 Apr 2026 13:59:53 -0400 Subject: [PATCH 02/62] gpio: pca953x: drop bitmap_complement() where feasible The driver reproduces the following pattern: bitmap_complement(tmp, data1, nbits); bitmap_and(dst, data2, tmp, nbits); This can be done in a single pass: bitmap_andnot(dst, data2, data1, nbits); Reviewed-by: Andy Shevchenko Signed-off-by: Yury Norov Link: https://patch.msgid.link/20260417175955.375275-2-ynorov@nvidia.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 52e96cc5f67b..1fef733fe1f0 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -877,11 +877,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio); bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio); - bitmap_complement(reg_direction, reg_direction, gc->ngpio); - bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); /* Look for any newly setup interrupt */ - for_each_set_bit(level, irq_mask, gc->ngpio) + for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio) pca953x_gpio_direction_input(&chip->gpio_chip, level); mutex_unlock(&chip->irq_lock); @@ -1005,8 +1003,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); bitmap_or(pending, pending, cur_stat, gc->ngpio); - bitmap_complement(cur_stat, new_stat, gc->ngpio); - bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio); + bitmap_andnot(cur_stat, reg_direction, new_stat, gc->ngpio); bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio); bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio); bitmap_or(pending, pending, old_stat, gc->ngpio); From 2757a5b1bca76a1b6378496b669a2baf1faddec5 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 17 Apr 2026 13:59:54 -0400 Subject: [PATCH 03/62] gpio: xilinx: drop bitmap_complement() where feasible The driver reproduces the following pattern: bitmap_complement(tmp, data1, nbits); bitmap_and(dst, data2, tmp, nbits); This can be done in a single pass: bitmap_andnot(dst, data2, data1, nbits); Reviewed-by: Andy Shevchenko Signed-off-by: Yury Norov Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260417175955.375275-3-ynorov@nvidia.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xilinx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index be4b4d730547..532205175827 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -495,13 +495,11 @@ static void xgpio_irqhandler(struct irq_desc *desc) xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, hw); - bitmap_complement(rising, chip->last_irq_read, 64); - bitmap_and(rising, rising, hw, 64); + bitmap_andnot(rising, hw, chip->last_irq_read, 64); bitmap_and(rising, rising, chip->enable, 64); bitmap_and(rising, rising, chip->rising_edge, 64); - bitmap_complement(falling, hw, 64); - bitmap_and(falling, falling, chip->last_irq_read, 64); + bitmap_andnot(falling, chip->last_irq_read, hw, 64); bitmap_and(falling, falling, chip->enable, 64); bitmap_and(falling, falling, chip->falling_edge, 64); From 6d22fcf85e3f089e5096812e89b742dd726aa7e6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 Apr 2026 19:35:54 +0200 Subject: [PATCH 04/62] gpio: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260423173553.92364-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c5ede0e4a32a..7a52b2b91ed6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -805,7 +805,7 @@ config GPIO_VISCONTI Say yes here to support GPIO on Tohisba Visconti. config GPIO_WCD934X - tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" + tristate "Qualcomm WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X help This driver is to support GPIO block found on the Qualcomm Technologies From 42509588db15100732f236b6a007f384dde3833f Mon Sep 17 00:00:00 2001 From: Mohamed Ayman Date: Fri, 24 Apr 2026 14:59:20 +0300 Subject: [PATCH 05/62] gpio: ep93xx: use handle_bad_irq() as default IRQ handler Replace the temporary fallback handle_simple_irq with handle_bad_irq now that the driver operates with a proper hierarchical IRQ setup. This ensures unexpected or unmapped interrupts are clearly flagged instead of being silently handled. Signed-off-by: Mohamed Ayman Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260424115920.54707-1-mohamedaymanworkspace@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ep93xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 1f56e44ffc9a..8784e433e1ff 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -323,8 +323,7 @@ static int ep93xx_setup_irqs(struct platform_device *pdev, } girq->default_type = IRQ_TYPE_NONE; - /* TODO: replace with handle_bad_irq() once we are fully hierarchical */ - girq->handler = handle_simple_irq; + girq->handler = handle_bad_irq; return 0; } From bfa336cee3324f991e93e9e570e8b827273df97e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 27 Apr 2026 10:43:21 +0200 Subject: [PATCH 06/62] ASoC: wsa881x: Move custom workaround to gpiolib-of The WSA881x codec driver has a local workaround for old device trees that have the "powerdown" GPIO flagged as active high, despite it is active low. This quirk can be replaced by a single quirk entry in gpiolib-of.c Drop all polarity inversion code and drop the surplus gpiod_direction_output() call in probe() since we now set up the line correctly when getting the GPIO. Also drop the inclusion of the unused . Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260427-asoc-wsa881x-v2-1-9ef965f94624@kernel.org Signed-off-by: Mark Brown --- drivers/gpio/gpiolib-of.c | 8 ++++++++ sound/soc/codecs/wsa881x.c | 35 ++++------------------------------- 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2c923d17541f..90f6295ab338 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -240,6 +240,14 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np, * treats it as "active low". */ { "ti,tsc2005", "reset-gpios", false }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_WSA881X) + /* + * WSA881 powerdown is always active low, but some device trees + * missed this when first contributed. It also has a very strange + * compatible. + */ + { "sdw10217201000", "powerdown-gpios", false }, #endif }; unsigned int i; diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 2fc234adca5f..d15fda648dad 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -3,7 +3,6 @@ // Copyright (c) 2019, Linaro Limited #include -#include #include #include #include @@ -672,11 +671,6 @@ struct wsa881x_priv { struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS]; struct gpio_desc *sd_n; - /* - * Logical state for SD_N GPIO: high for shutdown, low for enable. - * For backwards compatibility. - */ - unsigned int sd_n_val; int active_ports; bool hw_init; bool port_prepared[WSA881X_MAX_SWR_PORTS]; @@ -1121,31 +1115,11 @@ static int wsa881x_probe(struct sdw_slave *pdev, if (!wsa881x) return -ENOMEM; - wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", 0); + wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(wsa881x->sd_n)) return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n), "Shutdown Control GPIO not found\n"); - /* - * Backwards compatibility work-around. - * - * The SD_N GPIO is active low, however upstream DTS used always active - * high. Changing the flag in driver and DTS will break backwards - * compatibility, so add a simple value inversion to work with both old - * and new DTS. - * - * This won't work properly with DTS using the flags properly in cases: - * 1. Old DTS with proper ACTIVE_LOW, however such case was broken - * before as the driver required the active high. - * 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case - * (not existing upstream) but possible. This is the price of - * backwards compatibility, therefore this hack should be removed at - * some point. - */ - wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n); - if (!wsa881x->sd_n_val) - dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO."); - dev_set_drvdata(dev, wsa881x); wsa881x->slave = pdev; wsa881x->dev = dev; @@ -1158,7 +1132,6 @@ static int wsa881x_probe(struct sdw_slave *pdev, pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; pdev->prop.clk_stop_mode1 = true; - gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); if (IS_ERR(wsa881x->regmap)) @@ -1181,7 +1154,7 @@ static int wsa881x_runtime_suspend(struct device *dev) struct regmap *regmap = dev_get_regmap(dev, NULL); struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); - gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); + gpiod_direction_output(wsa881x->sd_n, 1); regcache_cache_only(regmap, true); regcache_mark_dirty(regmap); @@ -1196,13 +1169,13 @@ static int wsa881x_runtime_resume(struct device *dev) struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); unsigned long time; - gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); + gpiod_direction_output(wsa881x->sd_n, 0); time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(WSA881X_PROBE_TIMEOUT)); if (!time) { dev_err(dev, "Initialization not complete, timed out\n"); - gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); + gpiod_direction_output(wsa881x->sd_n, 1); return -ETIMEDOUT; } From 1005d6e0257a5623ef79bfbd8f588b498c1cab0d Mon Sep 17 00:00:00 2001 From: Mohamed Ayman Date: Tue, 28 Apr 2026 00:43:10 +0300 Subject: [PATCH 07/62] gpio: ixp4xx: switch to dynamic GPIO base Most IXP4xx platforms are Device Tree-based, and GPIO consumers use phandle-based descriptors rather than legacy integer GPIO numbers. Audit of the IXP4xx platform shows: - No gpio_request(), gpio_get_value(), or gpio_set_value() users in arch/arm/mach-ixp4xx/ - No platform data using fixed GPIO numbers This switches the gpiochip to dynamic base allocation, aligning with modern gpiolib expectations where GPIO numbers are not globally fixed and may be assigned dynamically. Set gpiochip.base = -1 to allow gpiolib to assign the GPIO base dynamically, avoiding global GPIO number space conflicts. Signed-off-by: Mohamed Ayman Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260427214311.331996-1-mohamedaymanworkspace@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ixp4xx.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index f34d87869c8b..669b139cd499 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -311,12 +311,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) } g->chip.gc.ngpio = 16; g->chip.gc.label = "IXP4XX_GPIO_CHIP"; - /* - * TODO: when we have migrated to device tree and all GPIOs - * are fetched using phandles, set this to -1 to get rid of - * the fixed gpiochip base. - */ - g->chip.gc.base = 0; + g->chip.gc.base = -1; g->chip.gc.parent = &pdev->dev; g->chip.gc.owner = THIS_MODULE; From be22c0f7f2d573addcdf3a92f8aaef7a45a8c133 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Tue, 28 Apr 2026 06:34:39 -0500 Subject: [PATCH 08/62] gpio: sim: Replace sprintf() with sysfs_emit() Replace sprintf() function calls with sysfs_emit() in the configfs show callbacks. This will help harden the driver and will bring the driver up-to-date with more modern functions. Suggested-by: Bartosz Golaszewski Signed-off-by: Maxwell Doose Link: https://patch.msgid.link/20260428113439.9783-1-m32285159@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sim.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index e19701c2ed67..b1f31861f40d 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -695,9 +695,9 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, pdev = dev->pdev; if (pdev) - return sprintf(page, "%s\n", dev_name(&pdev->dev)); + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); - return sprintf(page, "gpio-sim.%d\n", dev->id); + return sysfs_emit(page, "gpio-sim.%d\n", dev->id); } CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name); @@ -711,7 +711,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page) scoped_guard(mutex, &dev->lock) live = gpio_sim_device_is_live(dev); - return sprintf(page, "%c\n", live ? '1' : '0'); + return sysfs_emit(page, "%c\n", live ? '1' : '0'); } static unsigned int gpio_sim_get_line_names_size(struct gpio_sim_bank *bank) @@ -1056,7 +1056,7 @@ static int gpio_sim_emit_chip_name(struct device *dev, void *data) return 0; if (device_match_fwnode(dev, ctx->swnode)) - return sprintf(ctx->page, "%s\n", dev_name(dev)); + return sysfs_emit(ctx->page, "%s\n", dev_name(dev)); return 0; } @@ -1074,7 +1074,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, return device_for_each_child(&dev->pdev->dev, &ctx, gpio_sim_emit_chip_name); - return sprintf(page, "none\n"); + return sysfs_emit(page, "none\n"); } CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name); @@ -1087,7 +1087,7 @@ gpio_sim_bank_config_label_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", bank->label ?: ""); + return sysfs_emit(page, "%s\n", bank->label ?: ""); } static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, @@ -1122,7 +1122,7 @@ gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%u\n", bank->num_lines); + return sysfs_emit(page, "%u\n", bank->num_lines); } static ssize_t @@ -1168,7 +1168,7 @@ gpio_sim_line_config_name_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", line->name ?: ""); + return sysfs_emit(page, "%s\n", line->name ?: ""); } static ssize_t gpio_sim_line_config_name_store(struct config_item *item, @@ -1203,7 +1203,7 @@ gpio_sim_line_config_valid_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%c\n", line->valid ? '1' : '0'); + return sysfs_emit(page, "%c\n", line->valid ? '1' : '0'); } static ssize_t gpio_sim_line_config_valid_store(struct config_item *item, @@ -1241,7 +1241,7 @@ static ssize_t gpio_sim_hog_config_name_show(struct config_item *item, guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", hog->name ?: ""); + return sysfs_emit(page, "%s\n", hog->name ?: ""); } static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, @@ -1295,7 +1295,7 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item, return -EINVAL; } - return sprintf(page, "%s\n", repr); + return sysfs_emit(page, "%s\n", repr); } static ssize_t @@ -1335,7 +1335,7 @@ static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item, guard(mutex)(&dev->lock); - return sprintf(page, "%c\n", hog->active_low ? '1' : '0'); + return sysfs_emit(page, "%c\n", hog->active_low ? '1' : '0'); } static ssize_t From 575d985581e9ef7a175b61d63c824e5dd1d47987 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 27 Apr 2026 10:47:57 +0200 Subject: [PATCH 09/62] gpio: devres: Use devres parent if undefined If the user did not pass a parent in the struct gpio_chip then use the device used for devres as parent. This is quite intuitive and can help avoiding having to assign parent explicitly in every driver using devres to add the gpiochip. Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260427-gpio-mmio-more-v3-1-fe1882351424@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-devres.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 72422c5db364..2ec825ffab7d 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -353,6 +353,13 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo { int ret; + /* + * We are passing the devres device here so if the user did not pass + * another parent, it's this one. + */ + if (!gc->parent) + gc->parent = dev; + ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key); if (ret < 0) return ret; From 9278928e0fc75b6b7d6fb85774c1966bed2bc365 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 27 Apr 2026 10:47:58 +0200 Subject: [PATCH 10/62] gpio: altera: User gc helper variable Make the code easier to read by adding a local gpio_chip *gc variable. Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260427-gpio-mmio-more-v3-2-fe1882351424@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-altera.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 9508d764cce4..cb04700c8ccd 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -234,6 +234,7 @@ static int altera_gpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int reg, ret; struct altera_gpio_chip *altera_gc; + struct gpio_chip *gc; struct gpio_irq_chip *girq; int mapped_irq; @@ -243,29 +244,31 @@ static int altera_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&altera_gc->gpio_lock); + gc = &altera_gc->gc; + if (device_property_read_u32(dev, "altr,ngpio", ®)) /* By default assume maximum ngpio */ - altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + gc->ngpio = ALTERA_GPIO_MAX_NGPIO; else - altera_gc->gc.ngpio = reg; + gc->ngpio = reg; - if (altera_gc->gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { + if (gc->ngpio > ALTERA_GPIO_MAX_NGPIO) { dev_warn(&pdev->dev, "ngpio is greater than %d, defaulting to %d\n", ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); - altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + gc->ngpio = ALTERA_GPIO_MAX_NGPIO; } - altera_gc->gc.direction_input = altera_gpio_direction_input; - altera_gc->gc.direction_output = altera_gpio_direction_output; - altera_gc->gc.get = altera_gpio_get; - altera_gc->gc.set = altera_gpio_set; - altera_gc->gc.owner = THIS_MODULE; - altera_gc->gc.parent = &pdev->dev; - altera_gc->gc.base = -1; + gc->direction_input = altera_gpio_direction_input; + gc->direction_output = altera_gpio_direction_output; + gc->get = altera_gpio_get; + gc->set = altera_gpio_set; + gc->owner = THIS_MODULE; + gc->parent = &pdev->dev; + gc->base = -1; - altera_gc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); - if (!altera_gc->gc.label) + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!gc->label) return -ENOMEM; altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); @@ -283,7 +286,7 @@ static int altera_gpio_probe(struct platform_device *pdev) } altera_gc->interrupt_trigger = reg; - girq = &altera_gc->gc.irq; + girq = &gc->irq; gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip); if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) From 6458c5db0d1515914a98eb1e833305a0b5b75175 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 27 Apr 2026 10:47:59 +0200 Subject: [PATCH 11/62] gpio: altera: Use generic MMIO GPIO If we use the generic GPIO lib for MMIO in the Altera driver we do not only cut down on the code, we also get get/set_multiple for free. Keep the local raw spinlock instead of reusing the bgpio spinlock because it makes the gpiochip and irqchip nicely orthogonal. Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260427-gpio-mmio-more-v3-3-fe1882351424@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-altera.c | 109 +++++++++---------------------------- 2 files changed, 28 insertions(+), 82 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f86e25da964b..c56b00d77bf3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -156,6 +156,7 @@ config GPIO_74XX_MMIO config GPIO_ALTERA tristate "Altera GPIO" + select GPIO_GENERIC select GPIOLIB_IRQCHIP help Say Y or M here to build support for the Altera PIO device. diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index cb04700c8ccd..fe144360a88d 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -17,6 +17,7 @@ #include #include +#include #define ALTERA_GPIO_MAX_NGPIO 32 #define ALTERA_GPIO_DATA 0x0 @@ -26,7 +27,7 @@ /** * struct altera_gpio_chip -* @gc : GPIO chip structure. +* @chip : Generic GPIO chip structure. * @regs : memory mapped IO address for the controller registers. * @gpio_lock : synchronization lock so that new irq/set/get requests * will be blocked until the current one completes. @@ -34,7 +35,7 @@ * (rising, falling, both, high) */ struct altera_gpio_chip { - struct gpio_chip gc; + struct gpio_generic_chip chip; void __iomem *regs; raw_spinlock_t gpio_lock; int interrupt_trigger; @@ -106,72 +107,6 @@ static unsigned int altera_gpio_irq_startup(struct irq_data *d) return 0; } -static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - - return !!(readl(altera_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); -} - -static int altera_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int data_reg; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); - if (value) - data_reg |= BIT(offset); - else - data_reg &= ~BIT(offset); - writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - -static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int gpio_ddr; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - /* Set pin as input, assumes software controlled IP */ - gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr &= ~BIT(offset); - writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - -static int altera_gpio_direction_output(struct gpio_chip *gc, - unsigned offset, int value) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int data_reg, gpio_ddr; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - /* Sets the GPIO value */ - data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); - if (value) - data_reg |= BIT(offset); - else - data_reg &= ~BIT(offset); - writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); - - /* Set pin as output, assumes software controlled IP */ - gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr |= BIT(offset); - writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - static void altera_gpio_irq_edge_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -231,9 +166,11 @@ static const struct irq_chip altera_gpio_irq_chip = { static int altera_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config; struct device *dev = &pdev->dev; int reg, ret; struct altera_gpio_chip *altera_gc; + struct gpio_generic_chip *chip; struct gpio_chip *gc; struct gpio_irq_chip *girq; int mapped_irq; @@ -244,7 +181,26 @@ static int altera_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&altera_gc->gpio_lock); - gc = &altera_gc->gc; + altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(altera_gc->regs)) + return dev_err_probe(dev, PTR_ERR(altera_gc->regs), + "failed to ioremap memory resource\n"); + + chip = &altera_gc->chip; + + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = altera_gc->regs + ALTERA_GPIO_DATA, + .set = altera_gc->regs + ALTERA_GPIO_DATA, + .dirout = altera_gc->regs + ALTERA_GPIO_DIR, + }; + + ret = gpio_generic_chip_init(chip, &config); + if (ret) + return dev_err_probe(dev, ret, "unable to init generic GPIO\n"); + + gc = &chip->gc; if (device_property_read_u32(dev, "altr,ngpio", ®)) /* By default assume maximum ngpio */ @@ -259,22 +215,11 @@ static int altera_gpio_probe(struct platform_device *pdev) gc->ngpio = ALTERA_GPIO_MAX_NGPIO; } - gc->direction_input = altera_gpio_direction_input; - gc->direction_output = altera_gpio_direction_output; - gc->get = altera_gpio_get; - gc->set = altera_gpio_set; - gc->owner = THIS_MODULE; - gc->parent = &pdev->dev; - gc->base = -1; - + gc->base = -1; gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); if (!gc->label) return -ENOMEM; - altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(altera_gc->regs)) - return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n"); - mapped_irq = platform_get_irq_optional(pdev, 0); if (mapped_irq < 0) goto skip_irq; @@ -303,7 +248,7 @@ static int altera_gpio_probe(struct platform_device *pdev) girq->parents[0] = mapped_irq; skip_irq: - ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc); + ret = devm_gpiochip_add_data(dev, gc, altera_gc); if (ret) { dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); return ret; From bfdc854ba63bc815cf710701f889544a9d27df83 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 28 Apr 2026 17:45:07 +0200 Subject: [PATCH 12/62] gpiolib: move legacy interface into linux/gpio/legacy.h Split the old contents from gpio.h for clarity. Ideally any driver that still includes linux/gpio.h can now be ported over to use either linux/gpio/legacy.h or linux/gpio/consumer.h, with the original file getting removed once that is complete. No functional changes intended for now. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260428154522.2861492-1-arnd@kernel.org Signed-off-by: Bartosz Golaszewski --- include/linux/gpio.h | 162 +-------------------------------- include/linux/gpio/legacy.h | 173 ++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 158 deletions(-) create mode 100644 include/linux/gpio/legacy.h diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 8f85ddb26429..b0d4942a65de 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -2,13 +2,11 @@ /* * NOTE: This header *must not* be included. * - * This is the LEGACY GPIO bulk include file, including legacy APIs. It is - * used for GPIO drivers still referencing the global GPIO numberspace, - * and should not be included in new code. - * * If you're implementing a GPIO driver, only include * If you're implementing a GPIO consumer, only include + * If you're using the legacy interfaces, include */ + #ifndef __LINUX_GPIO_H #define __LINUX_GPIO_H @@ -18,159 +16,7 @@ #endif #ifdef CONFIG_GPIOLIB_LEGACY - -struct device; - -/* make these flag values available regardless of GPIO kconfig options */ -#define GPIOF_IN ((1 << 0)) -#define GPIOF_OUT_INIT_LOW ((0 << 0) | (0 << 1)) -#define GPIOF_OUT_INIT_HIGH ((0 << 0) | (1 << 1)) - -#ifdef CONFIG_GPIOLIB -/* - * "valid" GPIO numbers are nonnegative and may be passed to - * setup routines like gpio_request(). Only some valid numbers - * can successfully be requested and used. - * - * Invalid GPIO numbers are useful for indicating no-such-GPIO in - * platform data and other tables. - */ -static inline bool gpio_is_valid(int number) -{ - /* only non-negative numbers are valid */ - return number >= 0; -} - -/* - * Platforms may implement their GPIO interface with library code, - * at a small performance cost for non-inlined operations and some - * extra memory (for code and for per-GPIO table entries). - */ - -/* Always use the library code for GPIO management calls, - * or when sleeping may be involved. - */ -int gpio_request(unsigned gpio, const char *label); -void gpio_free(unsigned gpio); - -static inline int gpio_direction_input(unsigned gpio) -{ - return gpiod_direction_input(gpio_to_desc(gpio)); -} -static inline int gpio_direction_output(unsigned gpio, int value) -{ - return gpiod_direction_output_raw(gpio_to_desc(gpio), value); -} - -static inline int gpio_get_value_cansleep(unsigned gpio) -{ - return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio)); -} -static inline void gpio_set_value_cansleep(unsigned gpio, int value) -{ - gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value); -} - -static inline int gpio_get_value(unsigned gpio) -{ - return gpiod_get_raw_value(gpio_to_desc(gpio)); -} -static inline void gpio_set_value(unsigned gpio, int value) -{ - gpiod_set_raw_value(gpio_to_desc(gpio), value); -} - -static inline int gpio_to_irq(unsigned gpio) -{ - return gpiod_to_irq(gpio_to_desc(gpio)); -} - -int gpio_request_one(unsigned gpio, unsigned long flags, const char *label); - -int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label); - -#else /* ! CONFIG_GPIOLIB */ - -#include - -#include -#include - -static inline bool gpio_is_valid(int number) -{ - return false; -} - -static inline int gpio_request(unsigned gpio, const char *label) -{ - return -ENOSYS; -} - -static inline int gpio_request_one(unsigned gpio, - unsigned long flags, const char *label) -{ - return -ENOSYS; -} - -static inline void gpio_free(unsigned gpio) -{ - might_sleep(); - - /* GPIO can never have been requested */ - WARN_ON(1); -} - -static inline int gpio_direction_input(unsigned gpio) -{ - return -ENOSYS; -} - -static inline int gpio_direction_output(unsigned gpio, int value) -{ - return -ENOSYS; -} - -static inline int gpio_get_value(unsigned gpio) -{ - /* GPIO can never have been requested or set as {in,out}put */ - WARN_ON(1); - return 0; -} - -static inline void gpio_set_value(unsigned gpio, int value) -{ - /* GPIO can never have been requested or set as output */ - WARN_ON(1); -} - -static inline int gpio_get_value_cansleep(unsigned gpio) -{ - /* GPIO can never have been requested or set as {in,out}put */ - WARN_ON(1); - return 0; -} - -static inline void gpio_set_value_cansleep(unsigned gpio, int value) -{ - /* GPIO can never have been requested or set as output */ - WARN_ON(1); -} - -static inline int gpio_to_irq(unsigned gpio) -{ - /* GPIO can never have been requested or set as input */ - WARN_ON(1); - return -EINVAL; -} - -static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label) -{ - WARN_ON(1); - return -EINVAL; -} - -#endif /* ! CONFIG_GPIOLIB */ +#include #endif /* CONFIG_GPIOLIB_LEGACY */ + #endif /* __LINUX_GPIO_H */ diff --git a/include/linux/gpio/legacy.h b/include/linux/gpio/legacy.h new file mode 100644 index 000000000000..557ef635935e --- /dev/null +++ b/include/linux/gpio/legacy.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This is the LEGACY GPIO include file, used only for legacy APIs. + * + * No new code should use this, but instead use the linux/gpio/consumer.h + * interfaces directly. + */ + +#ifndef __LINUX_GPIO_LEGACY_H +#define __LINUX_GPIO_LEGACY_H + +#include + +#ifdef CONFIG_GPIOLIB_LEGACY + +struct device; + +/* make these flag values available regardless of GPIO kconfig options */ +#define GPIOF_IN ((1 << 0)) +#define GPIOF_OUT_INIT_LOW ((0 << 0) | (0 << 1)) +#define GPIOF_OUT_INIT_HIGH ((0 << 0) | (1 << 1)) + +#ifdef CONFIG_GPIOLIB + +#include + +/* + * "valid" GPIO numbers are nonnegative and may be passed to + * setup routines like gpio_request(). Only some valid numbers + * can successfully be requested and used. + * + * Invalid GPIO numbers are useful for indicating no-such-GPIO in + * platform data and other tables. + */ +static inline bool gpio_is_valid(int number) +{ + /* only non-negative numbers are valid */ + return number >= 0; +} + +/* + * Platforms may implement their GPIO interface with library code, + * at a small performance cost for non-inlined operations and some + * extra memory (for code and for per-GPIO table entries). + */ + +/* Always use the library code for GPIO management calls, + * or when sleeping may be involved. + */ +int gpio_request(unsigned gpio, const char *label); +void gpio_free(unsigned gpio); + +static inline int gpio_direction_input(unsigned gpio) +{ + return gpiod_direction_input(gpio_to_desc(gpio)); +} +static inline int gpio_direction_output(unsigned gpio, int value) +{ + return gpiod_direction_output_raw(gpio_to_desc(gpio), value); +} + +static inline int gpio_get_value_cansleep(unsigned gpio) +{ + return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio)); +} +static inline void gpio_set_value_cansleep(unsigned gpio, int value) +{ + gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value); +} + +static inline int gpio_get_value(unsigned gpio) +{ + return gpiod_get_raw_value(gpio_to_desc(gpio)); +} +static inline void gpio_set_value(unsigned gpio, int value) +{ + gpiod_set_raw_value(gpio_to_desc(gpio), value); +} + +static inline int gpio_to_irq(unsigned gpio) +{ + return gpiod_to_irq(gpio_to_desc(gpio)); +} + +int gpio_request_one(unsigned gpio, unsigned long flags, const char *label); + +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label); + +#else /* ! CONFIG_GPIOLIB */ + +#include + +#include +#include + +static inline bool gpio_is_valid(int number) +{ + return false; +} + +static inline int gpio_request(unsigned gpio, const char *label) +{ + return -ENOSYS; +} + +static inline int gpio_request_one(unsigned gpio, + unsigned long flags, const char *label) +{ + return -ENOSYS; +} + +static inline void gpio_free(unsigned gpio) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + +static inline int gpio_direction_input(unsigned gpio) +{ + return -ENOSYS; +} + +static inline int gpio_direction_output(unsigned gpio, int value) +{ + return -ENOSYS; +} + +static inline int gpio_get_value(unsigned gpio) +{ + /* GPIO can never have been requested or set as {in,out}put */ + WARN_ON(1); + return 0; +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ + /* GPIO can never have been requested or set as output */ + WARN_ON(1); +} + +static inline int gpio_get_value_cansleep(unsigned gpio) +{ + /* GPIO can never have been requested or set as {in,out}put */ + WARN_ON(1); + return 0; +} + +static inline void gpio_set_value_cansleep(unsigned gpio, int value) +{ + /* GPIO can never have been requested or set as output */ + WARN_ON(1); +} + +static inline int gpio_to_irq(unsigned gpio) +{ + /* GPIO can never have been requested or set as input */ + WARN_ON(1); + return -EINVAL; +} + +static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label) +{ + WARN_ON(1); + return -EINVAL; +} + +#endif /* ! CONFIG_GPIOLIB */ +#endif /* CONFIG_GPIOLIB_LEGACY */ +#endif /* __LINUX_GPIO_LEGAGY_H */ From 3a33394e8a5bc10ae4cbe9a35177fef714513e2e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Apr 2026 10:03:12 +0200 Subject: [PATCH 13/62] gpio: sim: add a Kconfig dependency on SYSFS gpio-sim is unusable without sysfs. Add a Kconfig dependency to its entry. Closes: https://sashiko.dev/#/patchset/20260428113439.9783-1-m32285159%40gmail.com Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260429080312.15561-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c56b00d77bf3..ce95a25298a8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -2061,6 +2061,7 @@ config GPIO_VIRTIO config GPIO_SIM tristate "GPIO Simulator Module" + depends on SYSFS select IRQ_SIM select CONFIGFS_FS help From d6e1a94888f5a4306c9998944a0f29f7bcd49411 Mon Sep 17 00:00:00 2001 From: Chanhong Jung Date: Wed, 29 Apr 2026 12:51:33 +0900 Subject: [PATCH 14/62] dt-bindings: gpio: fairchild,74hc595: add lines-initial-states property The 74HC595 and 74LVC594 shift registers latch their outputs until the first serial write, so boards that depend on a specific power-on pattern (for example active-low indicators, reset lines, or other signals that must come up non-zero) have no way to express that today: the Linux driver always writes zeros from its zero-initialised buffer during probe. Document support for the existing lines-initial-states bitmask, already defined for nxp,pcf8575, so the same convention covers this output-only device. Bit N corresponds to GPIO line N. Because the 74HC595/74LVC594 family is push-pull output only (no input mode, no high-impedance state under software control), bit=0 drives the line low and bit=1 drives it high; this differs from nxp,pcf8575, where the 0/1 polarity reflects the quasi-bidirectional nature of that part. The bitmask covers up to 32 lines, which fits the typical 1-4 chip cascades that appear in tree. Should longer chains require seeding in the future, the property can be extended to a uint32-array without breaking the bit-N-equals-line-N convention. Suggested-by: Linus Walleij Signed-off-by: Chanhong Jung Reviewed-by: Rob Herring (Arm) Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260429035134.1023330-2-happycpu@gmail.com Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/fairchild,74hc595.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml index 23410aeca300..451538df63f7 100644 --- a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml +++ b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml @@ -45,6 +45,18 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 description: Number of daisy-chained shift registers + lines-initial-states: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Bitmask that specifies the initial state of each output line, written + by the driver before the gpiochip is registered. Bit N corresponds to + GPIO line N, following the convention already documented for + nxp,pcf8575. Because the 74HC595/74LVC594 family is push-pull output + only, a bit set to zero drives the line low and a bit set to one + drives it high. The bitmask covers up to 32 lines (four cascaded + registers); outputs beyond that come up zeroed. When the property is + absent all outputs come up low, preserving the previous behaviour. + enable-gpios: description: GPIO connected to the OE (Output Enable) pin. maxItems: 1 @@ -79,6 +91,7 @@ examples: gpio-controller; #gpio-cells = <2>; registers-number = <4>; + lines-initial-states = <0xffff0000>; spi-max-frequency = <100000>; }; }; From cb77f8933467d08c8896674cd39ca98550a70fd6 Mon Sep 17 00:00:00 2001 From: Chanhong Jung Date: Wed, 29 Apr 2026 12:51:34 +0900 Subject: [PATCH 15/62] gpio: 74x164: support lines-initial-states for boot-time output state 74HC595 and 74LVC594 chains retain their output state from the first serial write onwards. Today the driver always kicks that first write from a zero-initialised buffer, so every output comes up low until user space issues a write. Boards that rely on the chain to drive signals whose power-on state matters (active-low indicators, reset lines, etc.) have no way to express the desired initial pattern via DT. Read the optional lines-initial-states bitmask, recently documented for this binding, into chip->buffer before the first __gen_74x164_write_config() so the chain comes up in a known state on the very first SPI transaction. Bit N maps to GPIO line N (matching the nxp,pcf8575 convention); on this output-only device, bit=0 drives the line low and bit=1 drives it high. Property absence keeps the existing zeroing behaviour intact. Suggested-by: Linus Walleij Signed-off-by: Chanhong Jung Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260429035134.1023330-3-happycpu@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index c226524efeba..5ca61cf5206a 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -112,7 +112,7 @@ static int gen_74x164_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct gen_74x164_chip *chip; - u32 nregs; + u32 nregs, init_state; int ret; /* @@ -134,6 +134,21 @@ static int gen_74x164_probe(struct spi_device *spi) chip->registers = nregs; + /* + * Optionally seed the chain with a board-specified pattern so the + * outputs come up in a known state on the first SPI write. The + * property follows the nxp,pcf8575 convention where bit N maps to + * GPIO line N. On this output-only device, bit=0 drives the line + * low and bit=1 drives it high. The bitmask covers up to 32 lines; + * any further outputs come up zeroed by devm_kzalloc(). + */ + if (!device_property_read_u32(dev, "lines-initial-states", &init_state)) { + unsigned int i; + + for (i = 0; i < min(nregs, 4U); i++) + chip->buffer[nregs - 1 - i] = (init_state >> (i * 8)) & 0xff; + } + chip->gpiod_oe = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(chip->gpiod_oe)) return PTR_ERR(chip->gpiod_oe); From 87e4643ab19cdfa152b7d10b3418cfff19724109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 6 May 2026 16:49:18 +0200 Subject: [PATCH 16/62] gpio: amd8111: Drop useless zeros in array initialisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiler fills in zeros as needed, so there is no technical reason to add explicit zeros at the end of a list initializer. Drop them. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260506144918.2445358-2-u.kleine-koenig@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-amd8111.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 15fd5e210d74..8078b5d7b80c 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -59,8 +59,8 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 }, - { 0, }, /* terminate list */ + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS) }, + { }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); From 73ae5f8c231e79c25dc85cba00f420776ab78bb3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 27 Mar 2026 11:49:07 +0100 Subject: [PATCH 17/62] mfd: timberdale: Move GPIO_NR_PINS into the driver This symbol is only used inside the Timberdale MFD driver. Move into the .c file as there's no need for it to be exposed in a header. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260327-gpio-timberdale-swnode-v3-1-9a1bc1b2b124@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/mfd/timberdale.c | 2 ++ drivers/mfd/timberdale.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index a4d9c070d481..d79419215cc2 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -37,6 +37,8 @@ #define DRIVER_NAME "timberdale" +#define GPIO_NR_PINS 16 + struct timberdale_device { resource_size_t ctl_mapbase; unsigned char __iomem *ctl_membase; diff --git a/drivers/mfd/timberdale.h b/drivers/mfd/timberdale.h index b01d2388e1af..db7b434f766d 100644 --- a/drivers/mfd/timberdale.h +++ b/drivers/mfd/timberdale.h @@ -113,7 +113,6 @@ #define GPIO_PIN_ASCB 8 #define GPIO_PIN_INIC_RST 14 #define GPIO_PIN_BT_RST 15 -#define GPIO_NR_PINS 16 /* DMA Channels */ #define DMA_UART_RX 0 From 0e951de77048427210994d140e516297251befaf Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 27 Mar 2026 11:49:08 +0100 Subject: [PATCH 18/62] mfd: timberdale: Set up a software node for the GPIO cell Using generic device properties instead of custom platform data structures is preferred due to the resulting unification of the way properties are accessed in consumer drivers. There's no DT node for the GPIO cell in this driver but we can create a software node with device properties and attach it to all the GPIO cells. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260327-gpio-timberdale-swnode-v3-2-9a1bc1b2b124@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/mfd/timberdale.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index d79419215cc2..0ab3da3d6818 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -38,6 +38,8 @@ #define DRIVER_NAME "timberdale" #define GPIO_NR_PINS 16 +#define GPIO_BASE 0 +#define IRQ_BASE 200 struct timberdale_device { resource_size_t ctl_mapbase; @@ -183,6 +185,18 @@ static struct timbgpio_platform_data .irq_base = 200, }; +static const struct property_entry timberdale_gpio_properties[] = { + PROPERTY_ENTRY_U32("ngpios", GPIO_NR_PINS), + PROPERTY_ENTRY_U32("gpio-base", GPIO_BASE), + PROPERTY_ENTRY_U32("irq-base", IRQ_BASE), + { } +}; + +static const struct software_node timberdale_gpio_swnode = { + .name = "timb-gpio", + .properties = timberdale_gpio_properties, +}; + static const struct resource timberdale_gpio_resources[] = { { .start = GPIOOFFSET, @@ -394,6 +408,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg0[] = { .resources = timberdale_gpio_resources, .platform_data = &timberdale_gpio_platform_data, .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-video", @@ -456,6 +471,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg1[] = { .resources = timberdale_gpio_resources, .platform_data = &timberdale_gpio_platform_data, .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-mlogicore", @@ -518,6 +534,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg2[] = { .resources = timberdale_gpio_resources, .platform_data = &timberdale_gpio_platform_data, .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-video", @@ -568,6 +585,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg3[] = { .resources = timberdale_gpio_resources, .platform_data = &timberdale_gpio_platform_data, .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-video", From a0aa7d4037ac161f0f273a4daa382da82466b967 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 27 Mar 2026 11:49:09 +0100 Subject: [PATCH 19/62] gpio: timberdale: Use device properties The top-level MFD driver now passes the device properties to the GPIO cell via the software node. Use generic device property accessors and stop using platform data. We can ignore the "ngpios" property here now as it will be retrieved internally by GPIO core. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260327-gpio-timberdale-swnode-v3-3-9a1bc1b2b124@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/gpio/gpio-timberdale.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index f488939dd00a..78fe133f5d32 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -225,19 +224,21 @@ static int timbgpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct gpio_chip *gc; struct timbgpio *tgpio; - struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev); int irq = platform_get_irq(pdev, 0); - if (!pdata || pdata->nr_pins > 32) { - dev_err(dev, "Invalid platform data\n"); - return -EINVAL; - } - tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL); if (!tgpio) return -EINVAL; - tgpio->irq_base = pdata->irq_base; + gc = &tgpio->gpio; + + err = device_property_read_u32(dev, "irq-base", &tgpio->irq_base); + if (err) + return err; + + err = device_property_read_u32(dev, "gpio-base", &gc->base); + if (err) + return err; spin_lock_init(&tgpio->lock); @@ -245,8 +246,6 @@ static int timbgpio_probe(struct platform_device *pdev) if (IS_ERR(tgpio->membase)) return PTR_ERR(tgpio->membase); - gc = &tgpio->gpio; - gc->label = dev_name(&pdev->dev); gc->owner = THIS_MODULE; gc->parent = &pdev->dev; @@ -256,21 +255,22 @@ static int timbgpio_probe(struct platform_device *pdev) gc->set = timbgpio_gpio_set; gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; gc->dbg_show = NULL; - gc->base = pdata->gpio_base; - gc->ngpio = pdata->nr_pins; gc->can_sleep = false; err = devm_gpiochip_add_data(&pdev->dev, gc, tgpio); if (err) return err; + if (gc->ngpio > 32) + return dev_err_probe(dev, -EINVAL, "Invalid number of pins\n"); + /* make sure to disable interrupts */ iowrite32(0x0, tgpio->membase + TGPIO_IER); if (irq < 0 || tgpio->irq_base <= 0) return 0; - for (i = 0; i < pdata->nr_pins; i++) { + for (i = 0; i < gc->ngpio; i++) { irq_set_chip_and_handler(tgpio->irq_base + i, &timbgpio_irqchip, handle_simple_irq); irq_set_chip_data(tgpio->irq_base + i, tgpio); From 061bc966cfe97314b9f4dcd849fc7042fb12122f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 27 Mar 2026 11:49:10 +0100 Subject: [PATCH 20/62] gpio: timberdale: Remove platform data header With no more users, we can remove timb_gpio.h. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260327-gpio-timberdale-swnode-v3-4-9a1bc1b2b124@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/mfd/timberdale.c | 17 ----------------- include/linux/timb_gpio.h | 25 ------------------------- 2 files changed, 42 deletions(-) delete mode 100644 include/linux/timb_gpio.h diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 0ab3da3d6818..e75e1d6851ab 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -15,8 +15,6 @@ #include #include -#include - #include #include #include @@ -178,13 +176,6 @@ static const struct resource timberdale_eth_resources[] = { }, }; -static struct timbgpio_platform_data - timberdale_gpio_platform_data = { - .gpio_base = 0, - .nr_pins = GPIO_NR_PINS, - .irq_base = 200, -}; - static const struct property_entry timberdale_gpio_properties[] = { PROPERTY_ENTRY_U32("ngpios", GPIO_NR_PINS), PROPERTY_ENTRY_U32("gpio-base", GPIO_BASE), @@ -406,8 +397,6 @@ static const struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), .swnode = &timberdale_gpio_swnode, }, { @@ -469,8 +458,6 @@ static const struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), .swnode = &timberdale_gpio_swnode, }, { @@ -532,8 +519,6 @@ static const struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), .swnode = &timberdale_gpio_swnode, }, { @@ -583,8 +568,6 @@ static const struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), .swnode = &timberdale_gpio_swnode, }, { diff --git a/include/linux/timb_gpio.h b/include/linux/timb_gpio.h deleted file mode 100644 index 74f5e73bf6db..000000000000 --- a/include/linux/timb_gpio.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * timb_gpio.h timberdale FPGA GPIO driver, platform data definition - * Copyright (c) 2009 Intel Corporation - */ - -#ifndef _LINUX_TIMB_GPIO_H -#define _LINUX_TIMB_GPIO_H - -/** - * struct timbgpio_platform_data - Platform data of the Timberdale GPIO driver - * @gpio_base: The number of the first GPIO pin, set to -1 for - * dynamic number allocation. - * @nr_pins: Number of pins that is supported by the hardware (1-32) - * @irq_base: If IRQ is supported by the hardware, this is the base - * number of IRQ:s. One IRQ per pin will be used. Set to - * -1 if IRQ:s is not supported. - */ -struct timbgpio_platform_data { - int gpio_base; - int nr_pins; - int irq_base; -}; - -#endif From 8cd19f5560218bf263b8ffc622ceb4bc329b3bf5 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 7 May 2026 12:01:32 +0300 Subject: [PATCH 21/62] dt-bindings: gpio: describe Waveshare GPIO controller The Waveshare DSI TOUCH family of panels has separate on-board GPIO controller, which controls power supplies to the panel and the touch screen and provides reset pins for both the panel and the touchscreen. Also it provides a simple PWM controller for panel backlight. Add bindings for these GPIO controllers. As overall integration might be not very obvious (and it differs significantly from the bindings used by the original drivers), provide complete example with the on-board regulators and the DSI panel. Acked-by: Conor Dooley Signed-off-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260507-waveshare-dsi-touch-v5-1-d2ac7ccc22d4@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- .../gpio/waveshare,dsi-touch-gpio.yaml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/waveshare,dsi-touch-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/waveshare,dsi-touch-gpio.yaml b/Documentation/devicetree/bindings/gpio/waveshare,dsi-touch-gpio.yaml new file mode 100644 index 000000000000..091e1fffcd47 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/waveshare,dsi-touch-gpio.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/waveshare,dsi-touch-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Waveshare GPIO controller on DSI TOUCH panels + +maintainers: + - Dmitry Baryshkov + +description: + Waveshare DSI TOUCH panel kits contain separate GPIO controller for toggling + power supplies and panel / touchscreen resets. + +properties: + compatible: + const: waveshare,dsi-touch-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + wsgpio: gpio@45 { + compatible = "waveshare,dsi-touch-gpio"; + reg = <0x45>; + gpio-controller; + #gpio-cells = <2>; + }; + }; + + panel_avdd: regulator-panel-avdd { + compatible = "regulator-fixed"; + regulator-name = "panel-avdd"; + gpios = <&wsgpio 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + panel_iovcc: regulator-panel-iovcc { + compatible = "regulator-fixed"; + regulator-name = "panel-iovcc"; + gpios = <&wsgpio 4 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + panel_vcc: regulator-panel-vcc { + compatible = "regulator-fixed"; + regulator-name = "panel-vcc"; + gpios = <&wsgpio 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; +... From 79f44c8acfe66ff8cbed5626e2535245cfcf43cf Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 7 May 2026 12:01:33 +0300 Subject: [PATCH 22/62] gpio: add GPIO controller found on Waveshare DSI TOUCH panels The Waveshare DSI TOUCH family of panels has separate on-board GPIO controller, which controls power supplies to the panel and the touch screen and provides reset pins for both the panel and the touchscreen. Also it provides a simple PWM controller for panel backlight. Add support for this GPIO controller. Tested-by: Riccardo Mereu Reviewed-by: Linus Walleij Signed-off-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260507-waveshare-dsi-touch-v5-2-d2ac7ccc22d4@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 10 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-waveshare-dsi.c | 208 ++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 drivers/gpio/gpio-waveshare-dsi.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ce95a25298a8..8ae6a423da6d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -806,6 +806,16 @@ config GPIO_VISCONTI help Say yes here to support GPIO on Tohisba Visconti. +config GPIO_WAVESHARE_DSI_TOUCH + tristate "Waveshare GPIO controller for DSI panels" + depends on BACKLIGHT_CLASS_DEVICE + depends on I2C + select REGMAP_I2C + help + Enable support for the GPIO and PWM controller found on Waveshare DSI + TOUCH panel kits. It provides GPIOs (used for regulator control and + resets) and backlight support. + config GPIO_WCD934X tristate "Qualcomm WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b267598b517d..2ea47d9d3dca 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -205,6 +205,7 @@ obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_WAVESHARE_DSI_TOUCH) += gpio-waveshare-dsi.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o diff --git a/drivers/gpio/gpio-waveshare-dsi.c b/drivers/gpio/gpio-waveshare-dsi.c new file mode 100644 index 000000000000..38f52351bb58 --- /dev/null +++ b/drivers/gpio/gpio-waveshare-dsi.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Waveshare International Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* I2C registers of the microcontroller. */ +#define REG_TP 0x94 +#define REG_LCD 0x95 +#define REG_PWM 0x96 +#define REG_SIZE 0x97 +#define REG_ID 0x98 +#define REG_VERSION 0x99 + +enum { + GPIO_AVDD = 0, + GPIO_PANEL_RESET = 1, + GPIO_BL_ENABLE = 2, + GPIO_IOVCC = 4, + GPIO_VCC = 8, + GPIO_TS_RESET = 9, +}; + +#define NUM_GPIO 16 + +struct waveshare_gpio { + struct mutex dir_lock; + struct mutex pwr_lock; + struct regmap *regmap; + u16 poweron_state; + + struct gpio_chip gc; +}; + +static const struct regmap_config waveshare_gpio_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_VERSION, +}; + +static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset) +{ + u16 pwr_state; + + guard(mutex)(&state->pwr_lock); + pwr_state = state->poweron_state & BIT(offset); + + return !!pwr_state; +} + +static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value) +{ + u16 last_val; + int err; + + guard(mutex)(&state->pwr_lock); + + last_val = state->poweron_state; + if (value) + last_val |= BIT(offset); + else + last_val &= ~BIT(offset); + + state->poweron_state = last_val; + + err = regmap_write(state->regmap, REG_TP, last_val >> 8); + if (!err) + err = regmap_write(state->regmap, REG_LCD, last_val & 0xff); + + return err; +} + +static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct waveshare_gpio *state = gpiochip_get_data(gc); + + return waveshare_gpio_get(state, offset); +} + +static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct waveshare_gpio *state = gpiochip_get_data(gc); + + return waveshare_gpio_set(state, offset, value); +} + +static int waveshare_gpio_update_status(struct backlight_device *bl) +{ + struct waveshare_gpio *state = bl_get_data(bl); + int brightness = backlight_get_brightness(bl); + + waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness); + + return regmap_write(state->regmap, REG_PWM, brightness); +} + +static const struct backlight_ops waveshare_gpio_bl = { + .update_status = waveshare_gpio_update_status, +}; + +static int waveshare_gpio_probe(struct i2c_client *i2c) +{ + struct backlight_properties props = {}; + struct waveshare_gpio *state; + struct device *dev = &i2c->dev; + struct backlight_device *bl; + struct regmap *regmap; + unsigned int data; + int ret; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + ret = devm_mutex_init(dev, &state->dir_lock); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &state->pwr_lock); + if (ret) + return ret; + + regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n"); + + state->regmap = regmap; + i2c_set_clientdata(i2c, state); + + ret = regmap_read(regmap, REG_ID, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data); + + ret = regmap_read(regmap, REG_SIZE, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel size = %d\n", data); + + ret = regmap_read(regmap, REG_VERSION, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data); + + ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1); + if (ret) + return dev_err_probe(dev, ret, "Failed to program GPIOs\n"); + + msleep(20); + + state->gc.parent = dev; + state->gc.label = i2c->name; + state->gc.owner = THIS_MODULE; + state->gc.base = -1; + state->gc.ngpio = NUM_GPIO; + + /* it is output only */ + state->gc.get = waveshare_gpio_gpio_get; + state->gc.set = waveshare_gpio_gpio_set; + state->gc.get_direction = waveshare_gpio_gpio_get_direction; + state->gc.can_sleep = true; + + ret = devm_gpiochip_add_data(dev, &state->gc, state); + if (ret) + return dev_err_probe(dev, ret, "Failed to create gpiochip\n"); + + props.type = BACKLIGHT_RAW; + props.max_brightness = 255; + props.brightness = 255; + bl = devm_backlight_device_register(dev, dev_name(dev), dev, state, + &waveshare_gpio_bl, &props); + return PTR_ERR_OR_ZERO(bl); +} + +static const struct of_device_id waveshare_gpio_dt_ids[] = { + { .compatible = "waveshare,dsi-touch-gpio" }, + {}, +}; +MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids); + +static struct i2c_driver waveshare_gpio_regulator_driver = { + .driver = { + .name = "waveshare-regulator", + .of_match_table = of_match_ptr(waveshare_gpio_dt_ids), + }, + .probe = waveshare_gpio_probe, +}; + +module_i2c_driver(waveshare_gpio_regulator_driver); + +MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels"); +MODULE_LICENSE("GPL"); From 5d6f7ce387c09fbd30360f268168606803ee9ebf Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 8 May 2026 17:34:38 -0700 Subject: [PATCH 23/62] gpio: zevio: allow COMPILE_TEST builds The ZEVIO GPIO driver uses generic platform, MMIO, and gpiolib interfaces. Allow it to build with COMPILE_TEST so it gets coverage on non-ARM platforms. Drop the ARM-specific IOMEM() casts around the register pointer. The pointer is already __iomem, so readl() and writel() can use it directly. Tested with: make LLVM=1 ARCH=loongarch drivers/gpio/gpio-zevio.o Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260509003438.956051-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-zevio.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8ae6a423da6d..cf2e34c4bf56 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -869,7 +869,7 @@ config GPIO_XTENSA config GPIO_ZEVIO bool "LSI ZEVIO SoC memory mapped GPIOs" - depends on ARM + depends on ARM || COMPILE_TEST help Say yes here to support the GPIO controller in LSI ZEVIO SoCs. diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index 29375bea2289..af0158522ac5 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -64,14 +64,14 @@ static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, unsigned port_offset) { unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; - return readl(IOMEM(c->regs + section_offset + port_offset)); + return readl(c->regs + section_offset + port_offset); } static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, unsigned port_offset, u32 val) { unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; - writel(val, IOMEM(c->regs + section_offset + port_offset)); + writel(val, c->regs + section_offset + port_offset); } /* Functions for struct gpio_chip */ From f76c8be440e53465a306c95a7d50ca8675252f82 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 16:17:05 +0800 Subject: [PATCH 24/62] dt-bindings: gpio: dwapb: allow GPIO hogs GPIO hogs are described in the gpio.txt binding as automatic default GPIO configuration items. Allow them for GPIO ports in DesignWare APB GPIO controller nodes. Cc: Hoan Tran Cc: Linus Walleij Cc: Bartosz Golaszewski Cc: Serge Semin Signed-off-by: Icenowy Zheng Acked-by: Conor Dooley Link: https://patch.msgid.link/20260507081710.4090814-8-zhengxingda@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/snps,dw-apb-gpio.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml index bba6f5b6606f..55069533f6d9 100644 --- a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml @@ -95,6 +95,12 @@ patternProperties: '#interrupt-cells': const: 2 + patternProperties: + "^.+-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + required: - compatible - reg From 8a46bd2638f1ad6d1ed73dc3ab10919e67274738 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Mon, 11 May 2026 12:55:37 +0530 Subject: [PATCH 25/62] gpio: Add fwnode_gpiod_get() helper Add fwnode_gpiod_get() as a convenience wrapper around fwnode_gpiod_get_index() for the common case where only the first GPIO is required. This mirrors existing gpiod_get() and devm_gpiod_get() helpers and avoids open-coding index 0 at call sites. Suggested-by: Manivannan Sadhasivam Acked-by: Manivannan Sadhasivam Reviewed-by: Linus Walleij Acked-by: Bartosz Golaszewski Signed-off-by: Krishna Chaitanya Chundru Link: https://patch.msgid.link/20260511-wakeirq_support-v10-1-c10af9c9eb8c@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/consumer.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 3efb5cb1e1d1..e2601217a71d 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -598,6 +598,15 @@ static inline int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, } #endif /* CONFIG_GPIOLIB && CONFIG_HTE */ +static inline +struct gpio_desc *fwnode_gpiod_get(struct fwnode_handle *fwnode, + const char *con_id, + enum gpiod_flags flags, + const char *label) +{ + return fwnode_gpiod_get_index(fwnode, con_id, 0, flags, label); +} + static inline struct gpio_desc *devm_fwnode_gpiod_get(struct device *dev, struct fwnode_handle *fwnode, From b5fafa01bdaade5253bd39317f5455d13e6efc7d Mon Sep 17 00:00:00 2001 From: Jie Li Date: Mon, 11 May 2026 13:37:25 +0200 Subject: [PATCH 26/62] gpiolib: add gpiod_is_single_ended() helper The direction of a single-ended (open-drain or open-source) GPIO line cannot always be reliably determined by reading hardware registers. In true open-drain implementations, the "high" state is achieved by entering a high-impedance mode, which many hardware controllers report as "input" even if the software intends to use it as an output. This creates issues for consumer drivers (like I2C) that rely on gpiod_get_direction() to decide if a line can be driven. Introduce gpiod_is_single_ended() to allow consumers to check the software configuration (GPIO_FLAG_OPEN_DRAIN/GPIO_FLAG_OPEN_SOURCE) of a descriptor. This provides a robust way to identify lines that are capable of being driven, regardless of their instantaneous hardware state. Signed-off-by: Jie Li Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260511113726.49041-2-jie.i.li@nokia.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 22 ++++++++++++++++++++++ include/linux/gpio/consumer.h | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1e6dce430dca..69743d6deeaf 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -491,6 +491,28 @@ int gpiod_get_direction(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_get_direction); +/** + * gpiod_is_single_ended - check if the GPIO is configured as single-ended + * @desc: the GPIO descriptor to check + * + * Returns true if the GPIO is configured as either Open Drain or Open Source. + * In these modes, the direction of the line cannot always be reliably + * determined by reading hardware registers, as the "off" state (High-Z) + * is physically indistinguishable from an input state. + */ +bool gpiod_is_single_ended(struct gpio_desc *desc) +{ + if (!desc) + return false; + + if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags) || + test_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(gpiod_is_single_ended); + /* * Add a new chip to the global chips list, keeping the list of chips sorted * by range(means [base, base + ngpio - 1]) order. diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 3efb5cb1e1d1..8fb27f9aa67f 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -111,6 +111,7 @@ void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc); void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs); int gpiod_get_direction(struct gpio_desc *desc); +bool gpiod_is_single_ended(struct gpio_desc *desc); int gpiod_direction_input(struct gpio_desc *desc); int gpiod_direction_output(struct gpio_desc *desc, int value); int gpiod_direction_output_raw(struct gpio_desc *desc, int value); @@ -337,6 +338,10 @@ static inline int gpiod_get_direction(const struct gpio_desc *desc) WARN_ON(desc); return -ENOSYS; } +static inline bool gpiod_is_single_ended(struct gpio_desc *desc) +{ + return false; +} static inline int gpiod_direction_input(struct gpio_desc *desc) { /* GPIO can never have been requested */ From 63677aa485245cfc0e107f9d625d4f846223415a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 10 May 2026 12:55:31 -0700 Subject: [PATCH 27/62] gpio: spear-spics: Add COMPILE_TEST support The SPEAr SPI chip-select GPIO driver only depends on generic platform, OF, and MMIO interfaces, so it can be built outside SPEAr platform configurations. Enable compile-test coverage to catch build regressions on other architectures. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260510195531.10561-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index cf2e34c4bf56..00fcab5d09a4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -699,7 +699,7 @@ config GPIO_SPACEMIT_K1 config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" - depends on PLAT_SPEAR + depends on PLAT_SPEAR || COMPILE_TEST select GENERIC_IRQ_CHIP help Say yes here to support ST SPEAr SPI Chip Select as GPIO device. From 68d801eabda5219dcc25c9de98d3bbdb5b51b0a5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 11 May 2026 21:43:43 +0200 Subject: [PATCH 28/62] gpio: regmap: Support sparsed fixed direction On some regmapped GPIOs apparently only a sparser selection of the lines (not all) are actually fixed direction. Support this situation by adding an optional bitmap indicating which GPIOs are actually fixed direction and which are not. Link: https://lore.kernel.org/linux-gpio/20260501155421.3329862-10-elder@riscstar.com/ Tested-by: Alex Elder Signed-off-by: Linus Walleij Reviewed-by: Michael Walle Reviewed-by: Alex Elder Link: https://patch.msgid.link/20260511-regmap-gpio-sparse-fixed-dir-v3-1-1429ec453be7@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 37 +++++++++++++++++++++++++++++++++---- include/linux/gpio/regmap.h | 7 +++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 9ae4a41a2427..b3b4e77ec147 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -31,6 +31,7 @@ struct gpio_regmap { unsigned int reg_clr_base; unsigned int reg_dir_in_base; unsigned int reg_dir_out_base; + unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output; #ifdef CONFIG_REGMAP_IRQ @@ -138,6 +139,20 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip, return regmap_write(gpio->regmap, reg, mask); } +static bool gpio_regmap_fixed_direction(struct gpio_regmap *gpio, + unsigned int offset) +{ + if (!gpio->fixed_direction_output) + return false; + + /* In this case only some GPIOs are fixed as input/output */ + if (gpio->fixed_direction_mask && + !test_bit(offset, gpio->fixed_direction_mask)) + return false; + + return true; +} + static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -145,7 +160,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; - if (gpio->fixed_direction_output) { + if (gpio_regmap_fixed_direction(gpio, offset)) { if (test_bit(offset, gpio->fixed_direction_output)) return GPIO_LINE_DIRECTION_OUT; else @@ -302,12 +317,23 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config goto err_free_gpio; } + if (config->fixed_direction_mask) { + gpio->fixed_direction_mask = bitmap_alloc(chip->ngpio, + GFP_KERNEL); + if (!gpio->fixed_direction_mask) { + ret = -ENOMEM; + goto err_free_gpio; + } + bitmap_copy(gpio->fixed_direction_mask, + config->fixed_direction_mask, chip->ngpio); + } + if (config->fixed_direction_output) { gpio->fixed_direction_output = bitmap_alloc(chip->ngpio, GFP_KERNEL); if (!gpio->fixed_direction_output) { ret = -ENOMEM; - goto err_free_gpio; + goto err_free_bitmap_dirmask; } bitmap_copy(gpio->fixed_direction_output, config->fixed_direction_output, chip->ngpio); @@ -329,7 +355,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config ret = gpiochip_add_data(chip, gpio); if (ret < 0) - goto err_free_bitmap; + goto err_free_bitmap_output; #ifdef CONFIG_REGMAP_IRQ if (config->regmap_irq_chip) { @@ -355,8 +381,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config err_remove_gpiochip: gpiochip_remove(chip); -err_free_bitmap: +err_free_bitmap_output: bitmap_free(gpio->fixed_direction_output); +err_free_bitmap_dirmask: + bitmap_free(gpio->fixed_direction_mask); err_free_gpio: kfree(gpio); return ERR_PTR(ret); @@ -376,6 +404,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio) gpiochip_remove(&gpio->gpio_chip); bitmap_free(gpio->fixed_direction_output); + bitmap_free(gpio->fixed_direction_mask); kfree(gpio); } EXPORT_SYMBOL_GPL(gpio_regmap_unregister); diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index 12d154732ca9..06255756710d 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -38,6 +38,12 @@ struct regmap; * offset to a register/bitmask pair. If not * given the default gpio_regmap_simple_xlate() * is used. + * @fixed_direction_mask: + * (Optional) Bitmap representing the GPIO lines that + * make use of the @fixed_direction_output list to + * enforce direction of the GPIO. If this is NULL + * and @fixed_direction_output is defined, ALL GPIOs + * are assumed to be fixed direction (out or in). * @fixed_direction_output: * (Optional) Bitmap representing the fixed direction of * the GPIO lines. Useful when there are GPIO lines with a @@ -89,6 +95,7 @@ struct gpio_regmap_config { int reg_stride; int ngpio_per_reg; struct irq_domain *irq_domain; + unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output; #ifdef CONFIG_REGMAP_IRQ From 806e7acf7f331008637b4f8ecf211eb0a082e6eb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 11 May 2026 21:43:44 +0200 Subject: [PATCH 29/62] gpio: regmap: Don't set a fixed direction line If a GPIO line has a fixed direction, report an error if a consumer anyway tries to set the direction to something other than what it is hardcoded to. This didn't happen much before because what we supported was all lines input or output and then the implementer would probably not specify the direction registers, but with sparse fixed direction we can have a mixture so let's take this into account. As a consequence, since gpio_regmap_set_direction() can now fail, alter the semantics in gpio_regmap_direction_output() such that we first check if we can set the direction to output before we set the value and the direction. Suggested-by: Sashiko Link: https://sashiko.dev/#/patchset/20260507-regmap-gpio-sparse-fixed-dir-v1-1-a2e5855e2701%40kernel.org Signed-off-by: Linus Walleij Reviewed-by: Michael Walle Reviewed-by: Alex Elder Tested-by: Alex Elder Link: https://patch.msgid.link/20260511-regmap-gpio-sparse-fixed-dir-v3-2-1429ec453be7@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index b3b4e77ec147..51b4d69b8740 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -196,6 +196,22 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_IN; } +static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio, + unsigned int offset, bool output) +{ + if (test_bit(offset, gpio->fixed_direction_output)) { + if (output) + return 0; + else + return -EINVAL; + } else { + if (output) + return -EINVAL; + else + return 0; + } +} + static int gpio_regmap_set_direction(struct gpio_chip *chip, unsigned int offset, bool output) { @@ -203,6 +219,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; + /* + * If the direction is fixed, only accept the fixed + * direction in this call. + */ + if (gpio_regmap_fixed_direction(gpio, offset)) + return gpio_regmap_try_direction_fixed(gpio, offset, output); + if (gpio->reg_dir_out_base) { base = gpio_regmap_addr(gpio->reg_dir_out_base); invert = 0; @@ -234,6 +257,20 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip, static int gpio_regmap_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { + struct gpio_regmap *gpio = gpiochip_get_data(chip); + int ret; + + /* + * First check if this is gonna work on a fixed direction line, + * if it doesn't (i.e. this is a fixed input line), then do not + * attempt to set the output value either and just bail out. + */ + if (gpio_regmap_fixed_direction(gpio, offset)) { + ret = gpio_regmap_try_direction_fixed(gpio, offset, true); + if (ret) + return ret; + } + gpio_regmap_set(chip, offset, value); return gpio_regmap_set_direction(chip, offset, true); From 3eb639ef8da2d418ae69f3c8840c4e815036adc6 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Tue, 12 May 2026 11:38:47 +0530 Subject: [PATCH 30/62] dt-bindings: gpio: zynq: Sort compatible strings alphabetically Sort the compatible string alphabetically. Acked-by: Conor Dooley Signed-off-by: Shubhrajyoti Datta Link: https://patch.msgid.link/20260512060917.2096456-2-shubhrajyoti.datta@amd.com Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-zynq.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml index 5e2496379a3c..30a7f836c341 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml @@ -12,10 +12,10 @@ maintainers: properties: compatible: enum: + - xlnx,pmc-gpio-1.0 + - xlnx,versal-gpio-1.0 - xlnx,zynq-gpio-1.0 - xlnx,zynqmp-gpio-1.0 - - xlnx,versal-gpio-1.0 - - xlnx,pmc-gpio-1.0 reg: maxItems: 1 From 18409d06b4a002cb8550ad7c20273bedc77851df Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Tue, 12 May 2026 11:38:48 +0530 Subject: [PATCH 31/62] dt-bindings: gpio: Add EIO GPIO compatible to gpio-zynq EIO (Extended IO) GPIO is a Xilinx IP block that exposes multiplexed I/O pins through an EIO interface. The EIO GPIO block has 2 banks with 26 GPIOs each (52 total). The GPIO width cannot be determined from the hardware registers, the driver relies on the compatible string to select the correct bank/pin configuration. A new compatible is therefore required. The block is currently present on xa2ve3288 silicon. The compatible string uses version 1.0 matching the IP core version. Acked-by: Rob Herring (Arm) Signed-off-by: Shubhrajyoti Datta Link: https://patch.msgid.link/20260512060917.2096456-3-shubhrajyoti.datta@amd.com Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-zynq.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml index 30a7f836c341..de24bb361e9f 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml @@ -12,6 +12,7 @@ maintainers: properties: compatible: enum: + - xlnx,eio-gpio-1.0 - xlnx,pmc-gpio-1.0 - xlnx,versal-gpio-1.0 - xlnx,zynq-gpio-1.0 @@ -30,7 +31,7 @@ properties: gpio-line-names: description: strings describing the names of each gpio line - minItems: 58 + minItems: 52 maxItems: 174 interrupt-controller: true @@ -89,6 +90,16 @@ allOf: minItems: 116 maxItems: 116 + - if: + properties: + compatible: + enum: + - xlnx,eio-gpio-1.0 + then: + properties: + gpio-line-names: + maxItems: 52 + required: - compatible - reg From eeb1d6dfd89344b17afe845d4839b79e37fdd547 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Tue, 12 May 2026 11:38:49 +0530 Subject: [PATCH 32/62] gpio: zynq: Add eio gpio support Add support for the EIO GPIO controller found on xa2ve3288 silicon. The EIO GPIO block provides access to multiplexed I/O pins exposed through the EIO interface. Only bank 0 and bank 1 are connected to external MIO pins, with 26 GPIOs per bank (52 GPIOs total). This change extends the Zynq GPIO driver to support the EIO GPIO variant. Signed-off-by: Shubhrajyoti Datta Link: https://patch.msgid.link/20260512060917.2096456-4-shubhrajyoti.datta@amd.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 571e366624d2..8118ae3412c2 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -25,6 +25,7 @@ #define VERSAL_GPIO_MAX_BANK 4 #define PMC_GPIO_MAX_BANK 5 #define VERSAL_UNUSED_BANKS 2 +#define EIO_GPIO_MAX_BANK 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 @@ -818,6 +819,16 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL) }; +static const struct zynq_platform_data eio_gpio_def = { + .label = "eio_gpio", + .ngpio = 52, + .max_bank = EIO_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[1] = 26, + .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */ +}; + static const struct zynq_platform_data versal_gpio_def = { .label = "versal_gpio", .quirks = GPIO_QUIRK_VERSAL, @@ -882,6 +893,7 @@ static const struct of_device_id zynq_gpio_of_match[] = { { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def }, { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def }, + { .compatible = "xlnx,eio-gpio-1.0", .data = &eio_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); From 553e26a45e0e66698c1e0043b705933102ac3edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 12 May 2026 17:21:25 +0200 Subject: [PATCH 33/62] gpio: Initialize i2c_device_id arrays using member names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260512152125.924433-2-u.kleine-koenig@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max732x.c | 20 +++++----- drivers/gpio/gpio-pca953x.c | 80 ++++++++++++++++++------------------- drivers/gpio/gpio-pca9570.c | 6 +-- drivers/gpio/gpio-pcf857x.c | 26 ++++++------ 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 281ba1740a6a..24c67c912954 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -103,16 +103,16 @@ static uint64_t max732x_features[] = { }; static const struct i2c_device_id max732x_id[] = { - { "max7319", MAX7319 }, - { "max7320", MAX7320 }, - { "max7321", MAX7321 }, - { "max7322", MAX7322 }, - { "max7323", MAX7323 }, - { "max7324", MAX7324 }, - { "max7325", MAX7325 }, - { "max7326", MAX7326 }, - { "max7327", MAX7327 }, - { }, + { .name = "max7319", .driver_data = MAX7319 }, + { .name = "max7320", .driver_data = MAX7320 }, + { .name = "max7321", .driver_data = MAX7321 }, + { .name = "max7322", .driver_data = MAX7322 }, + { .name = "max7323", .driver_data = MAX7323 }, + { .name = "max7324", .driver_data = MAX7324 }, + { .name = "max7325", .driver_data = MAX7325 }, + { .name = "max7326", .driver_data = MAX7326 }, + { .name = "max7327", .driver_data = MAX7327 }, + { } }; MODULE_DEVICE_TABLE(i2c, max732x_id); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1fef733fe1f0..92fe40e71889 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -86,49 +86,49 @@ #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) static const struct i2c_device_id pca953x_id[] = { - { "pca6408", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca6416", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9505", 40 | PCA953X_TYPE | PCA_INT, }, - { "pca9506", 40 | PCA953X_TYPE | PCA_INT, }, - { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9536", 4 | PCA953X_TYPE, }, - { "pca9537", 4 | PCA953X_TYPE | PCA_INT, }, - { "pca9538", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9539", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9554", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9556", 8 | PCA953X_TYPE, }, - { "pca9557", 8 | PCA953X_TYPE, }, - { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, - { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, - { "pca9698", 40 | PCA953X_TYPE, }, + { .name = "pca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9505", .driver_data = 40 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9506", .driver_data = 40 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9534", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9535", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9536", .driver_data = 4 | PCA953X_TYPE }, + { .name = "pca9537", .driver_data = 4 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9555", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9556", .driver_data = 8 | PCA953X_TYPE }, + { .name = "pca9557", .driver_data = 8 | PCA953X_TYPE }, + { .name = "pca9574", .driver_data = 8 | PCA957X_TYPE | PCA_INT }, + { .name = "pca9575", .driver_data = 16 | PCA957X_TYPE | PCA_INT }, + { .name = "pca9698", .driver_data = 40 | PCA953X_TYPE }, - { "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6534", 34 | PCAL653X_TYPE | PCA_LATCH_INT, }, - { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal9554b", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, + { .name = "pcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6524", .driver_data = 24 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6534", .driver_data = 34 | PCAL653X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9535", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9554b", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9555a", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, - { "max7310", 8 | PCA953X_TYPE, }, - { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7315", 8 | PCA953X_TYPE | PCA_INT, }, - { "max7318", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, - { "tca6418", 18 | TCA6418_TYPE | PCA_INT, }, - { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, - { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, - { "tca9554", 8 | PCA953X_TYPE | PCA_INT, }, - { "xra1202", 8 | PCA953X_TYPE }, + { .name = "max7310", .driver_data = 8 | PCA953X_TYPE }, + { .name = "max7312", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "max7313", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "max7315", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "max7318", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca6107", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6418", .driver_data = 18 | TCA6418_TYPE | PCA_INT }, + { .name = "tca6424", .driver_data = 24 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "xra1202", .driver_data = 8 | PCA953X_TYPE }, - { "tcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "tcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, + { .name = "tcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "tcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index 4a368803fb03..7a47a9aa0414 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -163,9 +163,9 @@ static const struct pca9570_chip_data slg7xl45106_gpio = { }; static const struct i2c_device_id pca9570_id_table[] = { - { "pca9570", (kernel_ulong_t)&pca9570_gpio}, - { "pca9571", (kernel_ulong_t)&pca9571_gpio }, - { "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio }, + { .name = "pca9570", .driver_data = (kernel_ulong_t)&pca9570_gpio }, + { .name = "pca9571", .driver_data = (kernel_ulong_t)&pca9571_gpio }, + { .name = "slg7xl45106", .driver_data = (kernel_ulong_t)&slg7xl45106_gpio }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9570_id_table); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 3b9de8c3d924..c942b959571b 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -20,19 +20,19 @@ #include static const struct i2c_device_id pcf857x_id[] = { - { "pcf8574", 8 }, - { "pcf8574a", 8 }, - { "pca8574", 8 }, - { "pca9670", 8 }, - { "pca9672", 8 }, - { "pca9674", 8 }, - { "pcf8575", 16 }, - { "pca8575", 16 }, - { "pca9671", 16 }, - { "pca9673", 16 }, - { "pca9675", 16 }, - { "max7328", 8 }, - { "max7329", 8 }, + { .name = "pcf8574", .driver_data = 8 }, + { .name = "pcf8574a", .driver_data = 8 }, + { .name = "pca8574", .driver_data = 8 }, + { .name = "pca9670", .driver_data = 8 }, + { .name = "pca9672", .driver_data = 8 }, + { .name = "pca9674", .driver_data = 8 }, + { .name = "pcf8575", .driver_data = 16 }, + { .name = "pca8575", .driver_data = 16 }, + { .name = "pca9671", .driver_data = 16 }, + { .name = "pca9673", .driver_data = 16 }, + { .name = "pca9675", .driver_data = 16 }, + { .name = "max7328", .driver_data = 8 }, + { .name = "max7329", .driver_data = 8 }, { } }; MODULE_DEVICE_TABLE(i2c, pcf857x_id); From 4002ccd266b665f1097e20addbe8f3baeb2136f9 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Thu, 14 May 2026 12:48:34 +0000 Subject: [PATCH 34/62] dt-bindings: gpio: Add Tegra238 support Extend the existing Tegra186 GPIO controller device tree bindings with support for the GPIO controllers found on Tegra238. Tegra238 has two GPIO controllers: the main controller and always-on (AON) controller. The number of pins is slightly different, but the programming model remains the same. Add a new header, include/dt-bindings/gpio/nvidia,tegra238-gpio.h, that defines port IDs as well as the TEGRA238_MAIN_GPIO() helper, both of which are used in conjunction to create a unique specifier for each pin. Signed-off-by: Prathamesh Shete Acked-by: Conor Dooley Link: https://patch.msgid.link/20260514124835.108532-1-pshete@nvidia.com Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/nvidia,tegra186-gpio.yaml | 6 ++ .../dt-bindings/gpio/nvidia,tegra238-gpio.h | 58 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 include/dt-bindings/gpio/nvidia,tegra238-gpio.h diff --git a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml index 17748dd1015d..adeb3b3a2902 100644 --- a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml @@ -85,6 +85,8 @@ properties: - nvidia,tegra194-gpio-aon - nvidia,tegra234-gpio - nvidia,tegra234-gpio-aon + - nvidia,tegra238-gpio + - nvidia,tegra238-gpio-aon - nvidia,tegra256-gpio - nvidia,tegra264-gpio - nvidia,tegra264-gpio-uphy @@ -163,6 +165,7 @@ allOf: - nvidia,tegra186-gpio - nvidia,tegra194-gpio - nvidia,tegra234-gpio + - nvidia,tegra238-gpio - nvidia,tegra256-gpio - nvidia,tegra264-gpio - nvidia,tegra264-gpio-uphy @@ -180,6 +183,7 @@ allOf: - nvidia,tegra186-gpio-aon - nvidia,tegra194-gpio-aon - nvidia,tegra234-gpio-aon + - nvidia,tegra238-gpio-aon - nvidia,tegra264-gpio-aon then: properties: @@ -192,6 +196,8 @@ allOf: compatible: contains: enum: + - nvidia,tegra238-gpio + - nvidia,tegra238-gpio-aon - nvidia,tegra264-gpio - nvidia,tegra264-gpio-uphy - nvidia,tegra264-gpio-aon diff --git a/include/dt-bindings/gpio/nvidia,tegra238-gpio.h b/include/dt-bindings/gpio/nvidia,tegra238-gpio.h new file mode 100644 index 000000000000..8a616a1df54c --- /dev/null +++ b/include/dt-bindings/gpio/nvidia,tegra238-gpio.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ +/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. */ + +/* + * This header provides constants for binding nvidia,tegra238-gpio*. + * + * The first cell in Tegra's GPIO specifier is the GPIO ID. The macros below + * provide names for this. + * + * The second cell contains standard flag values specified in gpio.h. + */ + +#ifndef _DT_BINDINGS_GPIO_TEGRA238_GPIO_H +#define _DT_BINDINGS_GPIO_TEGRA238_GPIO_H + +#include + +/* GPIOs implemented by main GPIO controller */ +#define TEGRA238_MAIN_GPIO_PORT_A 0 +#define TEGRA238_MAIN_GPIO_PORT_B 1 +#define TEGRA238_MAIN_GPIO_PORT_C 2 +#define TEGRA238_MAIN_GPIO_PORT_D 3 +#define TEGRA238_MAIN_GPIO_PORT_E 4 +#define TEGRA238_MAIN_GPIO_PORT_F 5 +#define TEGRA238_MAIN_GPIO_PORT_G 6 +#define TEGRA238_MAIN_GPIO_PORT_H 7 +#define TEGRA238_MAIN_GPIO_PORT_J 8 +#define TEGRA238_MAIN_GPIO_PORT_K 9 +#define TEGRA238_MAIN_GPIO_PORT_L 10 +#define TEGRA238_MAIN_GPIO_PORT_M 11 +#define TEGRA238_MAIN_GPIO_PORT_N 12 +#define TEGRA238_MAIN_GPIO_PORT_P 13 +#define TEGRA238_MAIN_GPIO_PORT_Q 14 +#define TEGRA238_MAIN_GPIO_PORT_R 15 +#define TEGRA238_MAIN_GPIO_PORT_S 16 +#define TEGRA238_MAIN_GPIO_PORT_T 17 +#define TEGRA238_MAIN_GPIO_PORT_U 18 +#define TEGRA238_MAIN_GPIO_PORT_V 19 +#define TEGRA238_MAIN_GPIO_PORT_W 20 +#define TEGRA238_MAIN_GPIO_PORT_X 21 + +#define TEGRA238_MAIN_GPIO(port, offset) \ + ((TEGRA238_MAIN_GPIO_PORT_##port * 8) + (offset)) + +/* GPIOs implemented by AON GPIO controller */ +#define TEGRA238_AON_GPIO_PORT_AA 0 +#define TEGRA238_AON_GPIO_PORT_BB 1 +#define TEGRA238_AON_GPIO_PORT_CC 2 +#define TEGRA238_AON_GPIO_PORT_DD 3 +#define TEGRA238_AON_GPIO_PORT_EE 4 +#define TEGRA238_AON_GPIO_PORT_FF 5 +#define TEGRA238_AON_GPIO_PORT_GG 6 +#define TEGRA238_AON_GPIO_PORT_HH 7 + +#define TEGRA238_AON_GPIO(port, offset) \ + ((TEGRA238_AON_GPIO_PORT_##port * 8) + (offset)) + +#endif From 8ac12d8b7099cdebff19aed78a81f61d8042c6be Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Thu, 14 May 2026 12:48:35 +0000 Subject: [PATCH 35/62] gpio: tegra186: Add support for Tegra238 Extend the existing Tegra186 GPIO controller driver with support for the GPIO controller found on Tegra238. Signed-off-by: Prathamesh Shete Link: https://patch.msgid.link/20260514124835.108532-2-pshete@nvidia.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index aa7c3e44234f..f56617c298c0 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1239,6 +1240,67 @@ static const struct tegra_gpio_soc tegra234_aon_soc = { .has_vm_support = false, }; +#define TEGRA238_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA238_MAIN, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra238_main_ports[] = { + TEGRA238_MAIN_GPIO_PORT(A, 0, 0, 8), + TEGRA238_MAIN_GPIO_PORT(B, 0, 1, 5), + TEGRA238_MAIN_GPIO_PORT(C, 0, 2, 8), + TEGRA238_MAIN_GPIO_PORT(D, 0, 3, 8), + TEGRA238_MAIN_GPIO_PORT(E, 0, 4, 4), + TEGRA238_MAIN_GPIO_PORT(F, 0, 5, 8), + TEGRA238_MAIN_GPIO_PORT(G, 0, 6, 8), + TEGRA238_MAIN_GPIO_PORT(H, 0, 7, 6), + TEGRA238_MAIN_GPIO_PORT(J, 1, 0, 8), + TEGRA238_MAIN_GPIO_PORT(K, 1, 1, 4), + TEGRA238_MAIN_GPIO_PORT(L, 1, 2, 8), + TEGRA238_MAIN_GPIO_PORT(M, 1, 3, 8), + TEGRA238_MAIN_GPIO_PORT(N, 1, 4, 3), + TEGRA238_MAIN_GPIO_PORT(P, 1, 5, 8), + TEGRA238_MAIN_GPIO_PORT(Q, 1, 6, 3), + TEGRA238_MAIN_GPIO_PORT(R, 2, 0, 8), + TEGRA238_MAIN_GPIO_PORT(S, 2, 1, 8), + TEGRA238_MAIN_GPIO_PORT(T, 2, 2, 8), + TEGRA238_MAIN_GPIO_PORT(U, 2, 3, 6), + TEGRA238_MAIN_GPIO_PORT(V, 2, 4, 2), + TEGRA238_MAIN_GPIO_PORT(W, 3, 0, 8), + TEGRA238_MAIN_GPIO_PORT(X, 3, 1, 2) +}; + +static const struct tegra_gpio_soc tegra238_main_soc = { + .num_ports = ARRAY_SIZE(tegra238_main_ports), + .ports = tegra238_main_ports, + .name = "tegra238-gpio", + .instance = 0, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + +#define TEGRA238_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA238_AON, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra238_aon_ports[] = { + TEGRA238_AON_GPIO_PORT(AA, 0, 0, 8), + TEGRA238_AON_GPIO_PORT(BB, 0, 1, 1), + TEGRA238_AON_GPIO_PORT(CC, 0, 2, 8), + TEGRA238_AON_GPIO_PORT(DD, 0, 3, 8), + TEGRA238_AON_GPIO_PORT(EE, 0, 4, 6), + TEGRA238_AON_GPIO_PORT(FF, 0, 5, 8), + TEGRA238_AON_GPIO_PORT(GG, 0, 6, 8), + TEGRA238_AON_GPIO_PORT(HH, 0, 7, 4) +}; + +static const struct tegra_gpio_soc tegra238_aon_soc = { + .num_ports = ARRAY_SIZE(tegra238_aon_ports), + .ports = tegra238_aon_ports, + .name = "tegra238-gpio-aon", + .instance = 1, + .num_irqs_per_bank = 8, + .has_gte = true, + .has_vm_support = false, +}; + #define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins) @@ -1447,6 +1509,12 @@ static const struct of_device_id tegra186_gpio_of_match[] = { }, { .compatible = "nvidia,tegra234-gpio-aon", .data = &tegra234_aon_soc + }, { + .compatible = "nvidia,tegra238-gpio", + .data = &tegra238_main_soc + }, { + .compatible = "nvidia,tegra238-gpio-aon", + .data = &tegra238_aon_soc }, { .compatible = "nvidia,tegra256-gpio", .data = &tegra256_main_soc From c7b929fe289d6e5118954f8327c143f8ad707a63 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 18 May 2026 17:59:58 -0700 Subject: [PATCH 36/62] gpio: xgene: allow COMPILE_TEST builds The APM X-Gene GPIO driver uses generic platform, ACPI, MMIO, and gpiolib interfaces. Allow it to build with COMPILE_TEST, matching the existing coverage for the X-Gene standby GPIO driver. Tested with: make LLVM=1 ARCH=loongarch drivers/gpio/gpio-xgene.o Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260519005958.628783-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 00fcab5d09a4..4d64ad9df0e0 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -825,7 +825,7 @@ config GPIO_WCD934X config GPIO_XGENE bool "APM X-Gene GPIO controller support" - depends on ARM64 + depends on ARM64 || COMPILE_TEST 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 From 292e7cab58e2ce1f9213a6a326eb314e18756459 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 18 May 2026 17:59:12 -0700 Subject: [PATCH 37/62] gpio: en7523: allow COMPILE_TEST builds The Airoha EN7523 GPIO driver uses generic platform, MMIO, and gpiolib interfaces. Allow it to build with COMPILE_TEST so it gets coverage on non-Airoha platforms. Tested with: make LLVM=1 ARCH=loongarch drivers/gpio/gpio-en7523.o Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260519005912.628667-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4d64ad9df0e0..9ea5c2523fd4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -302,7 +302,7 @@ config GPIO_EM config GPIO_EN7523 tristate "Airoha GPIO support" - depends on ARCH_AIROHA + depends on ARCH_AIROHA || COMPILE_TEST default ARCH_AIROHA select GPIO_GENERIC select GPIOLIB_IRQCHIP From c010a78304a648fd13556aff63e60a83f35d23c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 20 May 2026 09:48:12 +0200 Subject: [PATCH 38/62] gpio: Initialize all i2c_device_id arrays using member names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previously applied similar commit 553e26a45e0e ("gpio: Initialize i2c_device_id arrays using member names") only handled i2c_device_id arrays that also have an assignment for .driver_data. For consistency also convert the entries without such an assignment. Again this is a modification that has no influence on the generated code, it's only more robust against changes to struct i2c_device_id and easier to understand for a human. While touching adnp_i2c_id[] drop the comma after the list terminator. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260520074812.1632512-2-u.kleine-koenig@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adnp.c | 4 ++-- drivers/gpio/gpio-ds4520.c | 2 +- drivers/gpio/gpio-fxl6408.c | 2 +- drivers/gpio/gpio-gw-pld.c | 2 +- drivers/gpio/gpio-max7300.c | 2 +- drivers/gpio/gpio-tpic2810.c | 2 +- drivers/gpio/gpio-ts4900.c | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index e5ac2d211013..27a80d1143a1 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -499,8 +499,8 @@ static int adnp_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adnp_i2c_id[] = { - { "gpio-adnp" }, - { }, + { .name = "gpio-adnp" }, + { } }; MODULE_DEVICE_TABLE(i2c, adnp_i2c_id); diff --git a/drivers/gpio/gpio-ds4520.c b/drivers/gpio/gpio-ds4520.c index f52ecae382a4..5add662a7ef5 100644 --- a/drivers/gpio/gpio-ds4520.c +++ b/drivers/gpio/gpio-ds4520.c @@ -54,7 +54,7 @@ static const struct of_device_id ds4520_gpio_of_match_table[] = { MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table); static const struct i2c_device_id ds4520_gpio_id_table[] = { - { "ds4520-gpio" }, + { .name = "ds4520-gpio" }, { } }; MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table); diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c index afc1b8461dab..45b02d36e66f 100644 --- a/drivers/gpio/gpio-fxl6408.c +++ b/drivers/gpio/gpio-fxl6408.c @@ -150,7 +150,7 @@ static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = { MODULE_DEVICE_TABLE(of, fxl6408_dt_ids); static const struct i2c_device_id fxl6408_id[] = { - { "fxl6408" }, + { .name = "fxl6408" }, { } }; MODULE_DEVICE_TABLE(i2c, fxl6408_id); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 2e5d97b7363f..bf1f91c3c4a8 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -109,7 +109,7 @@ static int gw_pld_probe(struct i2c_client *client) } static const struct i2c_device_id gw_pld_id[] = { - { "gw-pld", }, + { .name = "gw-pld" }, { } }; MODULE_DEVICE_TABLE(i2c, gw_pld_id); diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c index 621d609ece90..62f2434c0d79 100644 --- a/drivers/gpio/gpio-max7300.c +++ b/drivers/gpio/gpio-max7300.c @@ -53,7 +53,7 @@ static void max7300_remove(struct i2c_client *client) } static const struct i2c_device_id max7300_id[] = { - { "max7300" }, + { .name = "max7300" }, { } }; MODULE_DEVICE_TABLE(i2c, max7300_id); diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index 866ff2d436d5..c38538653e99 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -112,7 +112,7 @@ static int tpic2810_probe(struct i2c_client *client) } static const struct i2c_device_id tpic2810_id_table[] = { - { "tpic2810", }, + { .name = "tpic2810" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, tpic2810_id_table); diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index d9ee8fc77ccd..b46b48e56c56 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -175,7 +175,7 @@ static int ts4900_gpio_probe(struct i2c_client *client) } static const struct i2c_device_id ts4900_gpio_id_table[] = { - { "ts4900-gpio", }, + { .name = "ts4900-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table); From 822878aa1737c6c9573e05c5cd501b679cf5ea1c Mon Sep 17 00:00:00 2001 From: Suneel Garapati Date: Thu, 21 May 2026 01:20:30 +0000 Subject: [PATCH 39/62] gpio: tegra186: Enable GTE for Tegra264 Set has_gte flag to enable GTE for Tegra264 AON pins. Signed-off-by: Suneel Garapati Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260521012031.2003914-1-suneelg@nvidia.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index f56617c298c0..d9a2dedf50ea 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -1395,6 +1395,7 @@ static const struct tegra_gpio_soc tegra264_aon_soc = { .name = "tegra264-gpio-aon", .instance = 1, .num_irqs_per_bank = 8, + .has_gte = true, .has_vm_support = true, }; From d01bf517cbbd5a4b0c16ba2d5107bda0e361b00c Mon Sep 17 00:00:00 2001 From: Thomas Lin Date: Thu, 21 May 2026 10:34:49 +0800 Subject: [PATCH 40/62] gpio: dwapb: Add ACPI ID LECA0001 for LECARC SoCs Add ACPI ID "LECA0001" for LECARC SoCs that use the DesignWare GPIO controller with V1 register offsets. Signed-off-by: Thomas Lin Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260521-lecarc-acpi-ids-v1-1-ae0ae90b2817@lecomputing.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-dwapb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 15cebc8b5d66..c1f3d83a67c1 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -694,6 +694,7 @@ static const struct acpi_device_id dwapb_acpi_match[] = { {"APMC0D07", GPIO_REG_OFFSET_V1}, {"APMC0D81", GPIO_REG_OFFSET_V2}, {"FUJI200A", GPIO_REG_OFFSET_V1}, + {"LECA0001", GPIO_REG_OFFSET_V1}, { } }; MODULE_DEVICE_TABLE(acpi, dwapb_acpi_match); From 820017813b818a9b6411e481fcc98f5260b6e6c1 Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Sun, 24 May 2026 23:49:53 +0800 Subject: [PATCH 41/62] dt-bindings: gpio: meson-axg: Fix whitespace issue Clean up whitespace misalignment in meson-axg-gpio.h Signed-off-by: Jun Yan Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260524154954.385778-1-jerrysteve1101@gmail.com Signed-off-by: Bartosz Golaszewski --- include/dt-bindings/gpio/meson-axg-gpio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dt-bindings/gpio/meson-axg-gpio.h b/include/dt-bindings/gpio/meson-axg-gpio.h index 25bb1fffa97a..a0d42bcd9bd3 100644 --- a/include/dt-bindings/gpio/meson-axg-gpio.h +++ b/include/dt-bindings/gpio/meson-axg-gpio.h @@ -23,7 +23,7 @@ #define GPIOAO_11 11 #define GPIOAO_12 12 #define GPIOAO_13 13 -#define GPIO_TEST_N 14 +#define GPIO_TEST_N 14 /* Second GPIO chip */ #define GPIOZ_0 0 @@ -52,7 +52,7 @@ #define BOOT_12 23 #define BOOT_13 24 #define BOOT_14 25 -#define GPIOA_0 26 +#define GPIOA_0 26 #define GPIOA_1 27 #define GPIOA_2 28 #define GPIOA_3 29 From b12e12ee4138e30d786eda02223e87044c989bb1 Mon Sep 17 00:00:00 2001 From: Len Bao Date: Sat, 16 May 2026 10:57:34 +0000 Subject: [PATCH 42/62] gpiolib: Mark gpio_devt, gpiolib_initialized and gpio_stub_drv as __ro_after_init The 'gpio_devt' and 'gpiolib_initialized' variables are initialized only during the init phase in the 'gpiolib_dev_init' function and never changed. So, mark these as __ro_after_init. The 'gpio_stub_drv' variable is initialized only in the declaration and never changed. So, this variable could be 'const', but using the 'driver_register' and 'driver_unregister' functions discards the 'const' qualifier. Therefore, as an alternative, mark it as a __ro_after_init. Signed-off-by: Len Bao Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260516105737.45174-1-len.bao@gmx.us Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 69743d6deeaf..9643af18e8ab 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -55,7 +55,7 @@ /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); -static dev_t gpio_devt; +static dev_t gpio_devt __ro_after_init; #define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */ static int gpio_bus_match(struct device *dev, const struct device_driver *drv) @@ -114,7 +114,7 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc); static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc); static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc); -static bool gpiolib_initialized; +static bool gpiolib_initialized __ro_after_init; const char *gpiod_get_label(struct gpio_desc *desc) { @@ -5362,7 +5362,7 @@ EXPORT_SYMBOL_GPL(gpiod_put_array); * gpio_device of the GPIO chip with the firmware node and then simply * bind it to this stub driver. */ -static struct device_driver gpio_stub_drv = { +static struct device_driver gpio_stub_drv __ro_after_init = { .name = "gpio_stub_drv", .bus = &gpio_bus_type, }; From 4c237ab773c93959aa2a7750f9dba772b5f1baee Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 22 May 2026 15:42:16 +0200 Subject: [PATCH 43/62] kunit: provide kunit_platform_device_register_full() Provide a kunit-managed variant of platform_device_register_full(). Reviewed-by: David Gow Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260522-gpiolib-kunit-v3-1-b15fe6987430@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- include/kunit/platform_device.h | 4 ++++ lib/kunit/platform.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/kunit/platform_device.h b/include/kunit/platform_device.h index f8236a8536f7..8cad6e1c3e7e 100644 --- a/include/kunit/platform_device.h +++ b/include/kunit/platform_device.h @@ -6,10 +6,14 @@ struct completion; struct kunit; struct platform_device; struct platform_driver; +struct platform_device_info; struct platform_device * kunit_platform_device_alloc(struct kunit *test, const char *name, int id); int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev); +struct platform_device * +kunit_platform_device_register_full(struct kunit *test, + const struct platform_device_info *pdevinfo); int kunit_platform_device_prepare_wait_for_probe(struct kunit *test, struct platform_device *pdev, diff --git a/lib/kunit/platform.c b/lib/kunit/platform.c index 0b518de26065..583b50b538c7 100644 --- a/lib/kunit/platform.c +++ b/lib/kunit/platform.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -130,6 +131,36 @@ int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev) } EXPORT_SYMBOL_GPL(kunit_platform_device_add); +/** + * kunit_platform_device_register_full() - Register a KUnit test-managed platform + * device described by platform device info + * @test: test context + * @pdevinfo: platform device information describing the new device + * + * Register a test-managed platform device. The device is unregistered when the + * test completes. + * + * Return: New platform device on success, IS_ERR() on error. + */ +struct platform_device * +kunit_platform_device_register_full(struct kunit *test, + const struct platform_device_info *pdevinfo) +{ + struct platform_device *pdev; + int ret; + + pdev = platform_device_register_full(pdevinfo); + if (IS_ERR(pdev)) + return pdev; + + ret = kunit_add_action_or_reset(test, platform_device_unregister_wrapper, pdev); + if (ret) + return ERR_PTR(ret); + + return pdev; +} +EXPORT_SYMBOL_GPL(kunit_platform_device_register_full); + struct kunit_platform_device_probe_nb { struct completion *x; struct device *dev; From 136569e6bc7b2d62e6777ef47caf0a417d70bf4a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 22 May 2026 15:42:17 +0200 Subject: [PATCH 44/62] kunit: provide kunit_platform_device_unregister() Tests may want to unregister a platform device as part of the test case logic. Using the regular platform_device_register() with kunit assertions may result in a platform device leak or otherwise requires cumbersome error handling. Provide a function that unregisters a kunit-managed platform device and drops the release action from the test's list. Reviewed-by: David Gow Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260522-gpiolib-kunit-v3-2-b15fe6987430@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- include/kunit/platform_device.h | 2 ++ lib/kunit/platform.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/kunit/platform_device.h b/include/kunit/platform_device.h index 8cad6e1c3e7e..eee565d5d1d3 100644 --- a/include/kunit/platform_device.h +++ b/include/kunit/platform_device.h @@ -14,6 +14,8 @@ int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev); struct platform_device * kunit_platform_device_register_full(struct kunit *test, const struct platform_device_info *pdevinfo); +void kunit_platform_device_unregister(struct kunit *test, + struct platform_device *pdev); int kunit_platform_device_prepare_wait_for_probe(struct kunit *test, struct platform_device *pdev, diff --git a/lib/kunit/platform.c b/lib/kunit/platform.c index 583b50b538c7..737758d710b2 100644 --- a/lib/kunit/platform.c +++ b/lib/kunit/platform.c @@ -161,6 +161,39 @@ kunit_platform_device_register_full(struct kunit *test, } EXPORT_SYMBOL_GPL(kunit_platform_device_register_full); +static bool +kunit_platform_device_add_match(struct kunit *test, struct kunit_resource *res, + void *match_data) +{ + struct platform_device *pdev = match_data; + + return res->data == pdev && res->free == kunit_platform_device_add_exit; +} + +/** + * kunit_platform_device_unregister() - Unregister a KUnit-managed platform device + * @test: test context + * @pdev: platform device to unregister + * + * Unregister a test-managed platform device and cancel its release action. + */ +void kunit_platform_device_unregister(struct kunit *test, + struct platform_device *pdev) +{ + struct kunit_resource *res; + + res = kunit_find_resource(test, kunit_platform_device_add_match, pdev); + if (res) { + res->free = NULL; + kunit_put_resource(res); + } else { + kunit_remove_action(test, platform_device_unregister_wrapper, pdev); + } + + platform_device_unregister(pdev); +} +EXPORT_SYMBOL_GPL(kunit_platform_device_unregister); + struct kunit_platform_device_probe_nb { struct completion *x; struct device *dev; From a6d2a3b403cbcbcdb3ffdb63e52ac090c1003d05 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 22 May 2026 15:42:18 +0200 Subject: [PATCH 45/62] gpio: add kunit test cases for the GPIO subsystem Add a module containing kunit test cases for GPIO core. The idea is to use it to test functionalities that can't easily be tested from user-space with kernel selftests or GPIO character device test suites provided by the libgpiod package. For now add test cases that verify software node based lookup and ensure that a GPIO provider unbinding with active consumers does not cause a crash. Reviewed-by: David Gow Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260522-gpiolib-kunit-v3-3-b15fe6987430@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpiolib-kunit.c | 358 +++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/gpio/gpiolib-kunit.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9ea5c2523fd4..d7e2bf6b7a7d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -102,6 +102,14 @@ config GPIO_CDEV_V1 This ABI version is deprecated. Please use the latest ABI for new developments. +config GPIO_KUNIT + tristate "Build GPIO Kunit test cases" + depends on KUNIT + default KUNIT_ALL_TESTS + help + Say Y here to build the module containing Kunit test cases verifying + the functionality of the GPIO subsystem. + config GPIO_GENERIC depends on HAS_IOMEM # Only for IOMEM drivers tristate diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2ea47d9d3dca..c66b6dd659b1 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o +obj-$(CONFIG_GPIO_KUNIT) += gpiolib-kunit.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c new file mode 100644 index 000000000000..380b68f879e5 --- /dev/null +++ b/drivers/gpio/gpiolib-kunit.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define GPIO_TEST_PROVIDER "gpio-test-provider" +#define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer" +#define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer" + +static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + return 0; +} + +static int gpio_test_provider_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_chip *gc; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->base = -1; + gc->ngpio = 4; + gc->label = "gpio-swnode-consumer-test-device"; + gc->parent = dev; + gc->owner = THIS_MODULE; + + gc->get_direction = gpio_test_provider_get_direction; + gc->set = gpio_test_provider_set; + + return devm_gpiochip_add_data(dev, gc, NULL); +} + +static struct platform_driver gpio_test_provider_driver = { + .probe = gpio_test_provider_probe, + .driver = { + .name = GPIO_TEST_PROVIDER, + }, +}; + +static const struct software_node gpio_test_provider_swnode = { + .name = "gpio-test-provider-primary", +}; + +struct gpio_swnode_consumer_pdata { + bool gpio_ok; +}; + +static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = { + .gpio_ok = false, +}; + +static int gpio_swnode_consumer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_swnode_consumer_pdata *pdata = dev_get_platdata(dev); + struct gpio_desc *desc; + + desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + pdata->gpio_ok = true; + + return 0; +} + +static struct platform_driver gpio_swnode_consumer_driver = { + .probe = gpio_swnode_consumer_probe, + .driver = { + .name = GPIO_SWNODE_TEST_CONSUMER, + }, +}; + +static void gpio_swnode_lookup_by_primary(struct kunit *test) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *pdev; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_SWNODE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_swnode_pdata_template, + .size_data = sizeof(gpio_swnode_pdata_template), + .properties = properties, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound = device_is_bound(&pdev->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +static void gpio_swnode_lookup_by_secondary(struct kunit *test) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct fwnode_handle *primary; + struct platform_device *pdev; + bool bound = false; + int ret; + + /* + * Can't live on the stack as it will still get referenced in cleanup + * path after this function returns. + */ + primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode_init(primary, NULL); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .fwnode = primary, + .swnode = &gpio_test_provider_swnode, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_SWNODE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_swnode_pdata_template, + .size_data = sizeof(gpio_swnode_pdata_template), + .properties = properties, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound = device_is_bound(&pdev->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +static struct kunit_case gpio_swnode_lookup_tests[] = { + KUNIT_CASE(gpio_swnode_lookup_by_primary), + KUNIT_CASE(gpio_swnode_lookup_by_secondary), + { } +}; + +static struct kunit_suite gpio_swnode_lookup_test_suite = { + .name = "gpio-swnode-lookup", + .test_cases = gpio_swnode_lookup_tests, +}; + +static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier); + +struct gpio_unbind_consumer_drvdata { + struct device *dev; + struct gpio_desc *desc; + struct notifier_block nb; + int set_retval; +}; + +static int gpio_unbind_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct gpio_unbind_consumer_drvdata *drvdata = + container_of(nb, struct gpio_unbind_consumer_drvdata, nb); + struct device *dev = data; + + if (dev != drvdata->dev) + return NOTIFY_DONE; + + drvdata->set_retval = gpiod_set_value_cansleep(drvdata->desc, 0); + + return NOTIFY_OK; +} + +static void gpio_unbind_unregister_notifier(void *data) +{ + struct notifier_block *nb = data; + + blocking_notifier_chain_unregister(&gpio_unbind_notifier, nb); +} + +static int gpio_unbind_consumer_probe(struct platform_device *pdev) +{ + struct gpio_unbind_consumer_drvdata *data; + struct device *dev = &pdev->dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + + data->desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(data->desc)) + return PTR_ERR(data->desc); + + data->nb.notifier_call = gpio_unbind_notify; + ret = blocking_notifier_chain_register(&gpio_unbind_notifier, &data->nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, gpio_unbind_unregister_notifier, &data->nb); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + return 0; +} + +static struct platform_driver gpio_unbind_consumer_driver = { + .probe = gpio_unbind_consumer_probe, + .driver = { + .name = GPIO_UNBIND_TEST_CONSUMER, + }, +}; + +static void gpio_unbind_with_consumers(struct kunit *test) +{ + struct gpio_unbind_consumer_drvdata *cons_data; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *prvd, *cons; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_unbind_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + prvd = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_UNBIND_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .properties = properties, + }; + + cons = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + wait_for_device_probe(); + scoped_guard(device, &cons->dev) + bound = device_is_bound(&cons->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + kunit_platform_device_unregister(test, prvd); + + ret = blocking_notifier_call_chain(&gpio_unbind_notifier, 0, &cons->dev); + KUNIT_ASSERT_EQ(test, ret, NOTIFY_OK); + + scoped_guard(device, &cons->dev) { + cons_data = platform_get_drvdata(cons); + ret = cons_data->set_retval; + } + + KUNIT_ASSERT_EQ(test, ret, -ENODEV); +} + +static struct kunit_case gpio_unbind_with_consumers_tests[] = { + KUNIT_CASE(gpio_unbind_with_consumers), + { } +}; + +static struct kunit_suite gpio_unbind_with_consumers_test_suite = { + .name = "gpio-unbind-with-consumers", + .test_cases = gpio_unbind_with_consumers_tests, +}; + +kunit_test_suites( + &gpio_swnode_lookup_test_suite, + &gpio_unbind_with_consumers_test_suite, +); + +MODULE_DESCRIPTION("Test module for the GPIO subsystem"); +MODULE_AUTHOR("Bartosz Golaszewski "); +MODULE_LICENSE("GPL"); From cacb104ed9d4c8dae46029b21f481fd132b3f11e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:33:54 +0200 Subject: [PATCH 46/62] gpio: ts5500: remove obsolete driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ts5500 platform is no longer functional because it is based on the removed AMD Élan i486 SoC. Remove the now obsolete driver. Signed-off-by: Arnd Bergmann Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260526103424.3246915-1-arnd@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 9 - drivers/gpio/Makefile | 1 - drivers/gpio/gpio-ts5500.c | 446 ------------------------------------- 3 files changed, 456 deletions(-) delete mode 100644 drivers/gpio/gpio-ts5500.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d7e2bf6b7a7d..bbd0ebe2f131 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1112,15 +1112,6 @@ config GPIO_SCH311X To compile this driver as a module, choose M here: the module will be called gpio-sch311x. -config GPIO_TS5500 - tristate "TS-5500 DIO blocks and compatibles" - depends on TS5500 || COMPILE_TEST - help - This driver supports Digital I/O exposed by pin blocks found on some - Technologic Systems platforms. It includes, but is not limited to, 3 - blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 - LCD port. - config GPIO_WINBOND tristate "Winbond Super I/O GPIO support" select ISA_BUS_API diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c66b6dd659b1..8ec03c9aec20 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -195,7 +195,6 @@ obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o -obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c deleted file mode 100644 index 3c7f2efe10fd..000000000000 --- a/drivers/gpio/gpio-ts5500.c +++ /dev/null @@ -1,446 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Digital I/O driver for Technologic Systems TS-5500 - * - * Copyright (c) 2012 Savoir-faire Linux Inc. - * Vivien Didelot - * - * Technologic Systems platforms have pin blocks, exposing several Digital - * Input/Output lines (DIO). This driver aims to support single pin blocks. - * In that sense, the support is not limited to the TS-5500 blocks. - * Actually, the following platforms have DIO support: - * - * TS-5500: - * Documentation: https://docs.embeddedts.com/TS-5500 - * Blocks: DIO1, DIO2 and LCD port. - * - * TS-5600: - * Documentation: https://docs.embeddedts.com/TS-5600 - * Blocks: LCD port (identical to TS-5500 LCD). - */ - -#include -#include -#include -#include -#include -#include - -/* List of supported Technologic Systems platforms DIO blocks */ -enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD }; - -struct ts5500_priv { - const struct ts5500_dio *pinout; - struct gpio_chip gpio_chip; - spinlock_t lock; - bool strap; - u8 hwirq; -}; - -/* - * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port). - * This flag ensures that the region has been requested by this driver. - */ -static bool hex7d_reserved; - -/* - * This structure is used to describe capabilities of DIO lines, - * such as available directions and connected interrupt (if any). - */ -struct ts5500_dio { - const u8 value_addr; - const u8 value_mask; - const u8 control_addr; - const u8 control_mask; - const bool no_input; - const bool no_output; - const u8 irq; -}; - -#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \ - { \ - .value_addr = vaddr, \ - .value_mask = BIT(vbit), \ - .control_addr = caddr, \ - .control_mask = BIT(cbit), \ - } - -#define TS5500_DIO_IN(addr, bit) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_output = true, \ - } - -#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_output = true, \ - .irq = _irq, \ - } - -#define TS5500_DIO_OUT(addr, bit) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_input = true, \ - } - -/* - * Input/Output DIO lines are programmed in groups of 4. Their values are - * available through 4 consecutive bits in a value port, whereas the direction - * of these 4 lines is driven by only 1 bit in a control port. - */ -#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit) - -/* - * TS-5500 DIO1 block - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x7b 0 0x7a 0 x x DIO1_0 1 0 - * 0x7b 1 0x7a 0 x x DIO1_1 3 1 - * 0x7b 2 0x7a 0 x x DIO1_2 5 2 - * 0x7b 3 0x7a 0 x x DIO1_3 7 3 - * 0x7b 4 0x7a 1 x x DIO1_4 9 4 - * 0x7b 5 0x7a 1 x x DIO1_5 11 5 - * 0x7b 6 0x7a 1 x x DIO1_6 13 6 - * 0x7b 7 0x7a 1 x x DIO1_7 15 7 - * 0x7c 0 0x7a 5 x x DIO1_8 4 8 - * 0x7c 1 0x7a 5 x x DIO1_9 6 9 - * 0x7c 2 0x7a 5 x x DIO1_10 8 10 - * 0x7c 3 0x7a 5 x x DIO1_11 10 11 - * 0x7c 4 x DIO1_12 12 12 - * 0x7c 5 x 7 DIO1_13 14 13 - */ -static const struct ts5500_dio ts5500_dio1[] = { - TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0), - TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1), - TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5), - TS5500_DIO_IN(0x7c, 4), - TS5500_DIO_IN_IRQ(0x7c, 5, 7), -}; - -/* - * TS-5500 DIO2 block - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x7e 0 0x7d 0 x x DIO2_0 1 0 - * 0x7e 1 0x7d 0 x x DIO2_1 3 1 - * 0x7e 2 0x7d 0 x x DIO2_2 5 2 - * 0x7e 3 0x7d 0 x x DIO2_3 7 3 - * 0x7e 4 0x7d 1 x x DIO2_4 9 4 - * 0x7e 5 0x7d 1 x x DIO2_5 11 5 - * 0x7e 6 0x7d 1 x x DIO2_6 13 6 - * 0x7e 7 0x7d 1 x x DIO2_7 15 7 - * 0x7f 0 0x7d 5 x x DIO2_8 4 8 - * 0x7f 1 0x7d 5 x x DIO2_9 6 9 - * 0x7f 2 0x7d 5 x x DIO2_10 8 10 - * 0x7f 3 0x7d 5 x x DIO2_11 10 11 - * 0x7f 4 x 6 DIO2_13 14 12 - */ -static const struct ts5500_dio ts5500_dio2[] = { - TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0), - TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1), - TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5), - TS5500_DIO_IN_IRQ(0x7f, 4, 6), -}; - -/* - * TS-5500 LCD port used as DIO block - * TS-5600 LCD port is identical - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x72 0 0x7d 2 x x LCD_0 8 0 - * 0x72 1 0x7d 2 x x LCD_1 7 1 - * 0x72 2 0x7d 2 x x LCD_2 10 2 - * 0x72 3 0x7d 2 x x LCD_3 9 3 - * 0x72 4 0x7d 3 x x LCD_4 12 4 - * 0x72 5 0x7d 3 x x LCD_5 11 5 - * 0x72 6 0x7d 3 x x LCD_6 14 6 - * 0x72 7 0x7d 3 x x LCD_7 13 7 - * 0x73 0 x LCD_EN 5 8 - * 0x73 6 x LCD_WR 6 9 - * 0x73 7 x 1 LCD_RS 3 10 - */ -static const struct ts5500_dio ts5500_lcd[] = { - TS5500_DIO_GROUP(0x72, 0, 0x7d, 2), - TS5500_DIO_GROUP(0x72, 4, 0x7d, 3), - TS5500_DIO_OUT(0x73, 0), - TS5500_DIO_IN(0x73, 6), - TS5500_DIO_IN_IRQ(0x73, 7, 1), -}; - -static inline void ts5500_set_mask(u8 mask, u8 addr) -{ - u8 val = inb(addr); - val |= mask; - outb(val, addr); -} - -static inline void ts5500_clear_mask(u8 mask, u8 addr) -{ - u8 val = inb(addr); - val &= ~mask; - outb(val, addr); -} - -static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - if (line.no_input) - return -ENXIO; - - if (line.no_output) - return 0; - - spin_lock_irqsave(&priv->lock, flags); - ts5500_clear_mask(line.control_mask, line.control_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - - return !!(inb(line.value_addr) & line.value_mask); -} - -static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - if (line.no_output) - return -ENXIO; - - spin_lock_irqsave(&priv->lock, flags); - if (!line.no_input) - ts5500_set_mask(line.control_mask, line.control_addr); - - if (val) - ts5500_set_mask(line.value_mask, line.value_addr); - else - ts5500_clear_mask(line.value_mask, line.value_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (val) - ts5500_set_mask(line.value_mask, line.value_addr); - else - ts5500_clear_mask(line.value_mask, line.value_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio *block = priv->pinout; - const struct ts5500_dio line = block[offset]; - - /* Only one pin is connected to an interrupt */ - if (line.irq) - return line.irq; - - /* As this pin is input-only, we may strap it to another in/out pin */ - if (priv->strap) - return priv->hwirq; - - return -ENXIO; -} - -static int ts5500_enable_irq(struct ts5500_priv *priv) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->hwirq == 7) - ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */ - else if (priv->hwirq == 6) - ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */ - else if (priv->hwirq == 1) - ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */ - else - ret = -EINVAL; - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -static void ts5500_disable_irq(struct ts5500_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->hwirq == 7) - ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */ - else if (priv->hwirq == 6) - ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */ - else if (priv->hwirq == 1) - ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */ - else - dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n", - priv->hwirq); - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int ts5500_dio_probe(struct platform_device *pdev) -{ - enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data; - struct device *dev = &pdev->dev; - const char *name = dev_name(dev); - struct ts5500_priv *priv; - unsigned long flags; - int ret; - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - - priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_set_drvdata(pdev, priv); - priv->hwirq = ret; - spin_lock_init(&priv->lock); - - priv->gpio_chip.owner = THIS_MODULE; - priv->gpio_chip.label = name; - priv->gpio_chip.parent = dev; - priv->gpio_chip.direction_input = ts5500_gpio_input; - priv->gpio_chip.direction_output = ts5500_gpio_output; - priv->gpio_chip.get = ts5500_gpio_get; - priv->gpio_chip.set = ts5500_gpio_set; - priv->gpio_chip.to_irq = ts5500_gpio_to_irq; - priv->gpio_chip.base = -1; - - switch (block) { - case TS5500_DIO1: - priv->pinout = ts5500_dio1; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1); - - if (!devm_request_region(dev, 0x7a, 3, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - break; - case TS5500_DIO2: - priv->pinout = ts5500_dio2; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2); - - if (!devm_request_region(dev, 0x7e, 2, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - - if (hex7d_reserved) - break; - - if (!devm_request_region(dev, 0x7d, 1, name)) { - dev_err(dev, "failed to request %s 7D\n", name); - return -EBUSY; - } - - hex7d_reserved = true; - break; - case TS5500_LCD: - case TS5600_LCD: - priv->pinout = ts5500_lcd; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd); - - if (!devm_request_region(dev, 0x72, 2, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - - if (!hex7d_reserved) { - if (!devm_request_region(dev, 0x7d, 1, name)) { - dev_err(dev, "failed to request %s 7D\n", name); - return -EBUSY; - } - - hex7d_reserved = true; - } - - /* Ensure usage of LCD port as DIO */ - spin_lock_irqsave(&priv->lock, flags); - ts5500_clear_mask(BIT(4), 0x7d); - spin_unlock_irqrestore(&priv->lock, flags); - break; - } - - ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); - if (ret) { - dev_err(dev, "failed to register the gpio chip\n"); - return ret; - } - - ret = ts5500_enable_irq(priv); - if (ret) { - dev_err(dev, "invalid interrupt %d\n", priv->hwirq); - return ret; - } - - return 0; -} - -static void ts5500_dio_remove(struct platform_device *pdev) -{ - struct ts5500_priv *priv = platform_get_drvdata(pdev); - - ts5500_disable_irq(priv); -} - -static const struct platform_device_id ts5500_dio_ids[] = { - { "ts5500-dio1", TS5500_DIO1 }, - { "ts5500-dio2", TS5500_DIO2 }, - { "ts5500-dio-lcd", TS5500_LCD }, - { "ts5600-dio-lcd", TS5600_LCD }, - { } -}; -MODULE_DEVICE_TABLE(platform, ts5500_dio_ids); - -static struct platform_driver ts5500_dio_driver = { - .driver = { - .name = "ts5500-dio", - }, - .probe = ts5500_dio_probe, - .remove = ts5500_dio_remove, - .id_table = ts5500_dio_ids, -}; - -module_platform_driver(ts5500_dio_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Savoir-faire Linux Inc. "); -MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver"); From 20de1f993ea6ca9a9d15fe2e433f9e58841999f0 Mon Sep 17 00:00:00 2001 From: Chen Jung Ku Date: Tue, 26 May 2026 20:19:05 +0800 Subject: [PATCH 47/62] gpio: gpiolib: use seq_puts() for plain strings Replace seq_printf() with seq_puts() where the format string is a plain string literal with no format specifiers. No functional change intended. Signed-off-by: Chen Jung Ku Link: https://patch.msgid.link/20260526121905.46345-1-ku.loong@gapp.nthu.edu.tw Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9643af18e8ab..c72eec54cb19 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5520,8 +5520,8 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) if (gc->label) seq_printf(s, ", %s", gc->label); if (gc->can_sleep) - seq_printf(s, ", can sleep"); - seq_printf(s, ":\n"); + seq_puts(s, ", can sleep"); + seq_puts(s, ":\n"); if (gc->dbg_show) gc->dbg_show(s, gc); From 3288e99f9968c37a8b654d5f4251382fe11bf854 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 30 Apr 2026 09:30:55 +0200 Subject: [PATCH 48/62] ARM: omap1: drop unused variable from omap16xx_gpio_init() The pdata variable is set but not used. Remove it. Reviewed-by: Arnd Bergmann Acked-by: Aaro Koskinen Link: https://patch.msgid.link/20260430-nokia770-gpio-swnodes-v7-1-c88f74c90dd6@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- arch/arm/mach-omap1/gpio16xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/mach-omap1/gpio16xx.c b/arch/arm/mach-omap1/gpio16xx.c index 55acec22fef4..e8dbe173bd33 100644 --- a/arch/arm/mach-omap1/gpio16xx.c +++ b/arch/arm/mach-omap1/gpio16xx.c @@ -212,7 +212,6 @@ static int __init omap16xx_gpio_init(void) void __iomem *base; struct resource *res; struct platform_device *pdev; - struct omap_gpio_platform_data *pdata; if (!cpu_is_omap16xx()) return -EINVAL; @@ -226,7 +225,6 @@ static int __init omap16xx_gpio_init(void) for (i = 0; i < ARRAY_SIZE(omap16xx_gpio_dev); i++) { pdev = omap16xx_gpio_dev[i]; - pdata = pdev->dev.platform_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!res)) { From 2bdf432e15818a81575e691e4ba0e3a3259d1711 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 30 Apr 2026 09:30:56 +0200 Subject: [PATCH 49/62] ARM: omap1: use platform_device_register_full() for GPIO devices on OMAP 16xx Ahead of changes attaching GPIO controller's software nodes referenced from the Nokia 770 board files to their target devices, switch the method for registering the platform devices to the platform_device_register_full() variant. This is done to leverage the new swnode field of struct platform_device_info which automate the software node's registration and assignment. Reviewed-by: Arnd Bergmann Acked-by: Aaro Koskinen Link: https://patch.msgid.link/20260430-nokia770-gpio-swnodes-v7-2-c88f74c90dd6@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- arch/arm/mach-omap1/gpio16xx.c | 71 ++++++++++++++++------------------ 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/arch/arm/mach-omap1/gpio16xx.c b/arch/arm/mach-omap1/gpio16xx.c index e8dbe173bd33..9cca29f86054 100644 --- a/arch/arm/mach-omap1/gpio16xx.c +++ b/arch/arm/mach-omap1/gpio16xx.c @@ -55,14 +55,13 @@ static struct omap_gpio_platform_data omap16xx_mpu_gpio_config = { .regs = &omap16xx_mpuio_regs, }; -static struct platform_device omap16xx_mpu_gpio = { +static const struct platform_device_info omap16xx_mpu_gpio = { .name = "omap_gpio", .id = 0, - .dev = { - .platform_data = &omap16xx_mpu_gpio_config, - }, - .num_resources = ARRAY_SIZE(omap16xx_mpu_gpio_resources), - .resource = omap16xx_mpu_gpio_resources, + .data = &omap16xx_mpu_gpio_config, + .size_data = sizeof(omap16xx_mpu_gpio_config), + .num_res = ARRAY_SIZE(omap16xx_mpu_gpio_resources), + .res = omap16xx_mpu_gpio_resources, }; /* gpio1 */ @@ -99,14 +98,13 @@ static struct omap_gpio_platform_data omap16xx_gpio1_config = { .regs = &omap16xx_gpio_regs, }; -static struct platform_device omap16xx_gpio1 = { +static const struct platform_device_info omap16xx_gpio1 = { .name = "omap_gpio", .id = 1, - .dev = { - .platform_data = &omap16xx_gpio1_config, - }, - .num_resources = ARRAY_SIZE(omap16xx_gpio1_resources), - .resource = omap16xx_gpio1_resources, + .data = &omap16xx_gpio1_config, + .size_data = sizeof(omap16xx_gpio1_config), + .num_res = ARRAY_SIZE(omap16xx_gpio1_resources), + .res = omap16xx_gpio1_resources, }; /* gpio2 */ @@ -127,14 +125,13 @@ static struct omap_gpio_platform_data omap16xx_gpio2_config = { .regs = &omap16xx_gpio_regs, }; -static struct platform_device omap16xx_gpio2 = { +static const struct platform_device_info omap16xx_gpio2 = { .name = "omap_gpio", .id = 2, - .dev = { - .platform_data = &omap16xx_gpio2_config, - }, - .num_resources = ARRAY_SIZE(omap16xx_gpio2_resources), - .resource = omap16xx_gpio2_resources, + .data = &omap16xx_gpio2_config, + .size_data = sizeof(omap16xx_gpio2_config), + .num_res = ARRAY_SIZE(omap16xx_gpio2_resources), + .res = omap16xx_gpio2_resources, }; /* gpio3 */ @@ -155,14 +152,13 @@ static struct omap_gpio_platform_data omap16xx_gpio3_config = { .regs = &omap16xx_gpio_regs, }; -static struct platform_device omap16xx_gpio3 = { +static const struct platform_device_info omap16xx_gpio3 = { .name = "omap_gpio", .id = 3, - .dev = { - .platform_data = &omap16xx_gpio3_config, - }, - .num_resources = ARRAY_SIZE(omap16xx_gpio3_resources), - .resource = omap16xx_gpio3_resources, + .data = &omap16xx_gpio3_config, + .size_data = sizeof(omap16xx_gpio3_config), + .num_res = ARRAY_SIZE(omap16xx_gpio3_resources), + .res = omap16xx_gpio3_resources, }; /* gpio4 */ @@ -183,17 +179,16 @@ static struct omap_gpio_platform_data omap16xx_gpio4_config = { .regs = &omap16xx_gpio_regs, }; -static struct platform_device omap16xx_gpio4 = { +static const struct platform_device_info omap16xx_gpio4 = { .name = "omap_gpio", .id = 4, - .dev = { - .platform_data = &omap16xx_gpio4_config, - }, - .num_resources = ARRAY_SIZE(omap16xx_gpio4_resources), - .resource = omap16xx_gpio4_resources, + .data = &omap16xx_gpio4_config, + .size_data = sizeof(omap16xx_gpio4_config), + .num_res = ARRAY_SIZE(omap16xx_gpio4_resources), + .res = omap16xx_gpio4_resources, }; -static struct platform_device *omap16xx_gpio_dev[] __initdata = { +static const struct platform_device_info *omap16xx_gpio_dev[] __initconst = { &omap16xx_mpu_gpio, &omap16xx_gpio1, &omap16xx_gpio2, @@ -210,8 +205,8 @@ static int __init omap16xx_gpio_init(void) { int i; void __iomem *base; - struct resource *res; - struct platform_device *pdev; + const struct resource *res; + const struct platform_device_info *pdevinfo; if (!cpu_is_omap16xx()) return -EINVAL; @@ -224,24 +219,24 @@ static int __init omap16xx_gpio_init(void) ULPD_CAM_CLK_CTRL); for (i = 0; i < ARRAY_SIZE(omap16xx_gpio_dev); i++) { - pdev = omap16xx_gpio_dev[i]; + pdevinfo = omap16xx_gpio_dev[i]; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res = &pdevinfo->res[0]; if (unlikely(!res)) { - dev_err(&pdev->dev, "Invalid mem resource.\n"); + pr_err("%s.%d: Invalid mem resource.\n", pdevinfo->name, pdevinfo->id); return -ENODEV; } base = ioremap(res->start, resource_size(res)); if (unlikely(!base)) { - dev_err(&pdev->dev, "ioremap failed.\n"); + pr_err("%s.%d: ioremap failed.\n", pdevinfo->name, pdevinfo->id); return -ENOMEM; } __raw_writel(SYSCONFIG_WORD, base + OMAP1610_GPIO_SYSCONFIG); iounmap(base); - platform_device_register(omap16xx_gpio_dev[i]); + platform_device_register_full(omap16xx_gpio_dev[i]); } return 0; From 61442e46fb9de57718a36f54c9ce1ce66ff7750b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 30 Apr 2026 09:30:57 +0200 Subject: [PATCH 50/62] ARM: omap1: enable real software node lookup of GPIOs on Nokia 770 Currently the board file for Nokia 770 creates dummy software nodes not attached in any way to the actual GPIO controller devices and uses the fact that GPIOLIB matching swnode's name to the GPIO chip's label during software node lookup. This behavior is wrong and we want to remove it. To that end, we need to first convert all existing users to creating actual fwnode links. Create real software nodes for GPIO controllers on OMAP16xx and reference them from the software nodes in the nokia board file. Acked-by: Arnd Bergmann Acked-by: Janusz Krzysztofik Acked-by: Aaro Koskinen Link: https://patch.msgid.link/20260430-nokia770-gpio-swnodes-v7-3-c88f74c90dd6@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- arch/arm/mach-omap1/board-nokia770.c | 33 +++++----------------------- arch/arm/mach-omap1/common.h | 3 +++ arch/arm/mach-omap1/gpio16xx.c | 7 ++++++ 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index a5bf5554800f..72d9e92a1071 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -36,27 +36,6 @@ #include "clock.h" #include "mmc.h" -static const struct software_node nokia770_mpuio_gpiochip_node = { - .name = "mpuio", -}; - -static const struct software_node nokia770_gpiochip1_node = { - .name = "gpio-0-15", -}; - -static const struct software_node nokia770_gpiochip2_node = { - .name = "gpio-16-31", -}; - -static const struct software_node *nokia770_gpiochip_nodes[] = { - &nokia770_mpuio_gpiochip_node, - &nokia770_gpiochip1_node, - &nokia770_gpiochip2_node, - NULL -}; - -#define ADS7846_PENDOWN_GPIO 15 - static const unsigned int nokia770_keymap[] = { KEY(1, 0, GROUP_0 | KEY_UP), KEY(2, 0, GROUP_1 | KEY_F5), @@ -112,7 +91,7 @@ static const struct omap_lcd_config nokia770_lcd_config __initconst = { }; static const struct property_entry nokia770_mipid_props[] = { - PROPERTY_ENTRY_GPIO("reset-gpios", &nokia770_gpiochip1_node, + PROPERTY_ENTRY_GPIO("reset-gpios", &omap16xx_gpio1_swnode, 13, GPIO_ACTIVE_LOW), { } }; @@ -138,8 +117,7 @@ static const struct property_entry nokia770_ads7846_props[] = { PROPERTY_ENTRY_U16("ti,x-plate-ohms", 180), PROPERTY_ENTRY_U16("ti,debounce-tol", 3), PROPERTY_ENTRY_U16("ti,debounce-rep", 1), - PROPERTY_ENTRY_GPIO("pendown-gpios", &nokia770_gpiochip1_node, - ADS7846_PENDOWN_GPIO, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("pendown-gpios", &omap16xx_gpio1_swnode, 15, GPIO_ACTIVE_LOW), { } }; @@ -225,9 +203,9 @@ static inline void nokia770_mmc_init(void) #if IS_ENABLED(CONFIG_I2C_CBUS_GPIO) static const struct software_node_ref_args nokia770_cbus_gpio_refs[] = { - SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 9, 0), - SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 10, 0), - SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 11, 0), + SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 9, 0), + SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 10, 0), + SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 11, 0), }; static const struct property_entry nokia770_cbus_props[] = { @@ -318,7 +296,6 @@ static void __init omap_nokia770_init(void) /* Unmask SleepX signal */ omap_writew((omap_readw(0xfffb5004) & ~2), 0xfffb5004); - software_node_register_node_group(nokia770_gpiochip_nodes); platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices)); gpiod_add_lookup_table(&nokia770_irq_gpio_table); diff --git a/arch/arm/mach-omap1/common.h b/arch/arm/mach-omap1/common.h index 7a7c3d9eb84a..c0f6e231fdb4 100644 --- a/arch/arm/mach-omap1/common.h +++ b/arch/arm/mach-omap1/common.h @@ -35,6 +35,9 @@ #include "soc.h" #include "i2c.h" +extern const struct software_node omap16xx_mpu_gpio_swnode; +extern const struct software_node omap16xx_gpio1_swnode; + #ifdef CONFIG_OMAP_SERIAL_WAKE int omap_serial_wakeup_init(void); #else diff --git a/arch/arm/mach-omap1/gpio16xx.c b/arch/arm/mach-omap1/gpio16xx.c index 9cca29f86054..0f97972fd248 100644 --- a/arch/arm/mach-omap1/gpio16xx.c +++ b/arch/arm/mach-omap1/gpio16xx.c @@ -9,6 +9,7 @@ */ #include +#include #include #include "hardware.h" @@ -55,6 +56,8 @@ static struct omap_gpio_platform_data omap16xx_mpu_gpio_config = { .regs = &omap16xx_mpuio_regs, }; +const struct software_node omap16xx_mpu_gpio_swnode = { }; + static const struct platform_device_info omap16xx_mpu_gpio = { .name = "omap_gpio", .id = 0, @@ -62,6 +65,7 @@ static const struct platform_device_info omap16xx_mpu_gpio = { .size_data = sizeof(omap16xx_mpu_gpio_config), .num_res = ARRAY_SIZE(omap16xx_mpu_gpio_resources), .res = omap16xx_mpu_gpio_resources, + .swnode = &omap16xx_mpu_gpio_swnode, }; /* gpio1 */ @@ -98,6 +102,8 @@ static struct omap_gpio_platform_data omap16xx_gpio1_config = { .regs = &omap16xx_gpio_regs, }; +const struct software_node omap16xx_gpio1_swnode = { }; + static const struct platform_device_info omap16xx_gpio1 = { .name = "omap_gpio", .id = 1, @@ -105,6 +111,7 @@ static const struct platform_device_info omap16xx_gpio1 = { .size_data = sizeof(omap16xx_gpio1_config), .num_res = ARRAY_SIZE(omap16xx_gpio1_resources), .res = omap16xx_gpio1_resources, + .swnode = &omap16xx_gpio1_swnode, }; /* gpio2 */ From 516e4d886941568174f46985fbb7c960c516ada9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 27 May 2026 16:57:27 +0200 Subject: [PATCH 51/62] gpio: cros-ec: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly set the .driver_data member of struct platform_device_id to zero without relying on that value. Drop this unused assignments. While touching this array unify spacing and use named initializers for .name. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Tzung-Bi Shih Reviewed-by: Linus Walleij Link: https://patch.msgid.link/06dfc8d1df46467269ee6113f161edac234e51cf.1779893336.git.u.kleine-koenig@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-cros-ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c index 435483826c6e..9deda8a9d11a 100644 --- a/drivers/gpio/gpio-cros-ec.c +++ b/drivers/gpio/gpio-cros-ec.c @@ -196,8 +196,8 @@ static int cros_ec_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id cros_ec_gpio_id[] = { - { "cros-ec-gpio", 0 }, - {} + { .name = "cros-ec-gpio" }, + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id); From 2d43fb71f4ecbd10649a277e8790e7ca27acfdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 27 May 2026 16:57:28 +0200 Subject: [PATCH 52/62] gpio: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous unit. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Linus Walleij Link: https://patch.msgid.link/b8f7581e9311d5579447304ac4f2d557b29e4f9d.1779893336.git.u.kleine-koenig@baylibre.com [Bartosz: Rebased on top of current linux-next where one of the drivers no longer exists.] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adp5585.c | 4 ++-- drivers/gpio/gpio-bd72720.c | 4 ++-- drivers/gpio/gpio-bd9571mwv.c | 4 ++-- drivers/gpio/gpio-lp873x.c | 2 +- drivers/gpio/gpio-lp87565.c | 2 +- drivers/gpio/gpio-max77759.c | 2 +- drivers/gpio/gpio-pxa.c | 18 +++++++++--------- drivers/gpio/gpio-tps65086.c | 2 +- drivers/gpio/gpio-tps65218.c | 2 +- drivers/gpio/gpio-tps65219.c | 4 ++-- drivers/gpio/gpio-tps65912.c | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index 0fd3cc26d017..6f10fc646008 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -507,8 +507,8 @@ static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { }; static const struct platform_device_id adp5585_gpio_id_table[] = { - { "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info }, - { "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info }, + { .name = "adp5585-gpio", .driver_data = (kernel_ulong_t)&adp5585_gpio_chip_info }, + { .name = "adp5589-gpio", .driver_data = (kernel_ulong_t)&adp5589_gpio_chip_info }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table); diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c index d0f936ed80af..306e23411209 100644 --- a/drivers/gpio/gpio-bd72720.c +++ b/drivers/gpio/gpio-bd72720.c @@ -263,8 +263,8 @@ static int gpo_bd72720_probe(struct platform_device *pdev) } static const struct platform_device_id bd72720_gpio_id[] = { - { "bd72720-gpio" }, - { }, + { .name = "bd72720-gpio" }, + { } }; MODULE_DEVICE_TABLE(platform, bd72720_gpio_id); diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index cc5b1746f2fe..f829fc40c02b 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -110,8 +110,8 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id bd9571mwv_gpio_id_table[] = { - { "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 }, - { "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 }, + { .name = "bd9571mwv-gpio", .driver_data = ROHM_CHIP_TYPE_BD9571 }, + { .name = "bd9574mwf-gpio", .driver_data = ROHM_CHIP_TYPE_BD9574 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table); diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index f4413fa5a811..0d1bd9df265a 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -156,7 +156,7 @@ static int lp873x_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id lp873x_gpio_id_table[] = { - { "lp873x-gpio", }, + { .name = "lp873x-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp873x_gpio_id_table); diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index 0f337c1283b2..3ac78f2b0fa7 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -171,7 +171,7 @@ static int lp87565_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id lp87565_gpio_id_table[] = { - { "lp87565-q1-gpio", }, + { .name = "lp87565-q1-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table); diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c index 3bf9f23d1532..c6bdac7fb44a 100644 --- a/drivers/gpio/gpio-max77759.c +++ b/drivers/gpio/gpio-max77759.c @@ -502,7 +502,7 @@ static const struct of_device_id max77759_gpio_of_id[] = { MODULE_DEVICE_TABLE(of, max77759_gpio_of_id); static const struct platform_device_id max77759_gpio_platform_id[] = { - { "max77759-gpio", }, + { .name = "max77759-gpio" }, { } }; MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id); diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 664cf1eef494..5d61053e0596 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -708,15 +708,15 @@ static int pxa_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id gpio_id_table[] = { - { "pxa25x-gpio", (unsigned long)&pxa25x_id }, - { "pxa26x-gpio", (unsigned long)&pxa26x_id }, - { "pxa27x-gpio", (unsigned long)&pxa27x_id }, - { "pxa3xx-gpio", (unsigned long)&pxa3xx_id }, - { "pxa93x-gpio", (unsigned long)&pxa93x_id }, - { "mmp-gpio", (unsigned long)&mmp_id }, - { "mmp2-gpio", (unsigned long)&mmp2_id }, - { "pxa1928-gpio", (unsigned long)&pxa1928_id }, - { }, + { .name = "pxa25x-gpio", .driver_data = (unsigned long)&pxa25x_id }, + { .name = "pxa26x-gpio", .driver_data = (unsigned long)&pxa26x_id }, + { .name = "pxa27x-gpio", .driver_data = (unsigned long)&pxa27x_id }, + { .name = "pxa3xx-gpio", .driver_data = (unsigned long)&pxa3xx_id }, + { .name = "pxa93x-gpio", .driver_data = (unsigned long)&pxa93x_id }, + { .name = "mmp-gpio", .driver_data = (unsigned long)&mmp_id }, + { .name = "mmp2-gpio", .driver_data = (unsigned long)&mmp2_id }, + { .name = "pxa1928-gpio", .driver_data = (unsigned long)&pxa1928_id }, + { } }; static struct platform_driver pxa_gpio_driver = { diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index df770ecf28bc..f29d8485ab33 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -91,7 +91,7 @@ static int tps65086_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps65086_gpio_id_table[] = { - { "tps65086-gpio", }, + { .name = "tps65086-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 3b4c41f5ef55..bf85663349fb 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -201,7 +201,7 @@ static const struct of_device_id tps65218_dt_match[] = { MODULE_DEVICE_TABLE(of, tps65218_dt_match); static const struct platform_device_id tps65218_gpio_id_table[] = { - { "tps65218-gpio", }, + { .name = "tps65218-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 158f63bcf10c..457fd8a589e8 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -249,8 +249,8 @@ static int tps65219_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps6521x_gpio_id_table[] = { - { "tps65214-gpio", TPS65214 }, - { "tps65219-gpio", TPS65219 }, + { .name = "tps65214-gpio", .driver_data = TPS65214 }, + { .name = "tps65219-gpio", .driver_data = TPS65219 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index 7a2c5685c2fd..cf3fa49a7097 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -115,7 +115,7 @@ static int tps65912_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps65912_gpio_id_table[] = { - { "tps65912-gpio", }, + { .name = "tps65912-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65912_gpio_id_table); From a8754838f83a9905af516f38dd2633744a94f71a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 27 May 2026 16:57:29 +0200 Subject: [PATCH 53/62] gpio: max77620: Unify usage of space and comma in platform_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The most accepted style for the array terminator is to use a single space between the curly braces and no trailing comma. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Linus Walleij Link: https://patch.msgid.link/985c86e80f35a944a4712f0c2ac8dd795868cdfb.1779893336.git.u.kleine-koenig@baylibre.com [Bartosz: Fixed Uwe's S-B] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max77620.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index e6c85411c695..2bf3b55a61b5 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -367,7 +367,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) static const struct platform_device_id max77620_gpio_devtype[] = { { .name = "max77620-gpio", }, { .name = "max20024-gpio", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype); From 5974454ab26a5351abe4897f7110a68120d170fa Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 27 May 2026 21:10:31 -0700 Subject: [PATCH 54/62] gpio: realtek-otto: fix kernel-doc warnings Add the missing 'struct' keyword in the kernel-doc comment for realtek_gpio_ctrl, and document the @cpumask_base and @cpu_irq_maskable members that were added later but never described. Also fix the mismatch between documented @imr_line_pos and the actual member name line_imr_pos. Fixes W=1 warning: Warning: drivers/gpio/gpio-realtek-otto.c:66 cannot understand function prototype: 'struct realtek_gpio_ctrl' Assisted-by: Opencode:BigPickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260528041031.728557-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-realtek-otto.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index 5e3152c2e51a..37ef56f45318 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -40,16 +40,18 @@ #define REALTEK_GPIO_PORTS_PER_BANK 4 /** - * realtek_gpio_ctrl - Realtek Otto GPIO driver data + * struct realtek_gpio_ctrl - Realtek Otto GPIO driver data * * @chip: Associated gpio_generic_chip instance * @base: Base address of the register block for a GPIO bank + * @cpumask_base: Base address of the per-CPU interrupt mask registers + * @cpu_irq_maskable: CPUs that can receive GPIO interrupts * @lock: Lock for accessing the IRQ registers and values * @intr_mask: Mask for interrupts lines * @intr_type: Interrupt type selection * @bank_read: Read a bank setting as a single 32-bit value * @bank_write: Write a bank setting as a single 32-bit value - * @imr_line_pos: Bit shift of an IRQ line's IMR value. + * @line_imr_pos: Bit shift of an IRQ line's IMR value. * * The DIR, DATA, and ISR registers consist of four 8-bit port values, packed * into a single 32-bit register. Use @bank_read (@bank_write) to get (assign) From 9de94681ee48770ec7e2062451a572b557bf9298 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 26 May 2026 08:35:02 +0200 Subject: [PATCH 55/62] gpio: mxc: use BIT() macro Replace the open-code with the BIT() macro. Signed-off-by: Alexander Stein Reviewed-by: Frank Li Link: https://patch.msgid.link/20260526063504.25916-2-alexander.stein@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 647b6f4861b7..c7a5e039e926 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -330,13 +330,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) ret = enable_irq_wake(port->irq_high); else ret = enable_irq_wake(port->irq); - port->wakeup_pads |= (1 << gpio_idx); + port->wakeup_pads |= BIT(gpio_idx); } else { if (port->irq_high && (gpio_idx >= 16)) ret = disable_irq_wake(port->irq_high); else ret = disable_irq_wake(port->irq); - port->wakeup_pads &= ~(1 << gpio_idx); + port->wakeup_pads &= ~BIT(gpio_idx); } return ret; From aa7e8b7ef03151305a387654280306684687ade9 Mon Sep 17 00:00:00 2001 From: "Marco Scardovi (scardracs)" Date: Sun, 24 May 2026 18:27:07 +0200 Subject: [PATCH 56/62] gpio: core: fix const-correctness of gpio_chip_guard The DEFINE_CLASS macro for gpio_chip_guard currently expects a non-const struct gpio_desc pointer. This prevents the guard from being used cleanly in fast paths that receive a const descriptor, forcing developers to fall back to open-coding the SRCU locks. Update the macro to accept a const struct gpio_desc pointer. This is valid because the actual targeted gpio_device pointer assignment does not drop const qualifiers on the target structure. Convert the open-coded SRCU locks in gpiod_get_raw_value_commit() and gpiod_to_irq() to use the guard, removing their legacy FIXME comments. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Acked-by: Andy Shevchenko Link: https://patch.msgid.link/20260524162708.62949-2-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 28 ++++++++-------------------- drivers/gpio/gpiolib.h | 2 +- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c72eec54cb19..ac2779dbe42b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3428,20 +3428,13 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) { - struct gpio_device *gdev; - struct gpio_chip *gc; int value; - /* FIXME Unable to use gpio_chip_guard due to const desc. */ - gdev = desc->gdev; - - guard(srcu)(&gdev->srcu); - - gc = srcu_dereference(gdev->chip, &gdev->srcu); - if (!gc) + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) return -ENODEV; - value = gpio_chip_get_value(gc, desc); + value = gpio_chip_get_value(guard.gc, desc); value = value < 0 ? value : !!value; trace_gpio_value(desc_to_gpio(desc), 1, value); return value; @@ -4148,8 +4141,6 @@ EXPORT_SYMBOL_GPL(gpiod_is_shared); */ int gpiod_to_irq(const struct gpio_desc *desc) { - struct gpio_device *gdev; - struct gpio_chip *gc; int offset; int ret; @@ -4157,16 +4148,13 @@ int gpiod_to_irq(const struct gpio_desc *desc) if (ret <= 0) return -EINVAL; - gdev = desc->gdev; - /* FIXME Cannot use gpio_chip_guard due to const desc. */ - guard(srcu)(&gdev->srcu); - gc = srcu_dereference(gdev->chip, &gdev->srcu); - if (!gc) + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) return -ENODEV; offset = gpiod_hwgpio(desc); - if (gc->to_irq) { - ret = gc->to_irq(gc, offset); + if (guard.gc->to_irq) { + ret = guard.gc->to_irq(guard.gc, offset); if (ret) return ret; @@ -4174,7 +4162,7 @@ int gpiod_to_irq(const struct gpio_desc *desc) return -ENXIO; } #ifdef CONFIG_GPIOLIB_IRQCHIP - if (gc->irq.chip) { + if (guard.gc->irq.chip) { /* * Avoid race condition with other code, which tries to lookup * an IRQ before the irqchip has been properly registered, diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index dc4cb61a9318..650a702741df 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -244,7 +244,7 @@ DEFINE_CLASS(gpio_chip_guard, _guard; }), - struct gpio_desc *desc) + const struct gpio_desc *desc) int gpiod_request(struct gpio_desc *desc, const char *label); int gpiod_request_commit(struct gpio_desc *desc, const char *label); From 07c44ee9fdf196dcec14675c793e3139ec8a15b5 Mon Sep 17 00:00:00 2001 From: "Marco Scardovi (scardracs)" Date: Sun, 24 May 2026 18:27:08 +0200 Subject: [PATCH 57/62] gpio: remove obsolete UAF FIXMEs from lookup paths The ACPI and swnode GPIO lookup backends both temporarily grab a reference to the gpio_device, resolve the descriptor, and then drop the reference before returning the descriptor to the caller. They carry FIXME comments warning that the descriptor is being returned without its backing device reference. However, the gpiod_find_and_request() core functionally prevents any use-after-free window by wrapping the entire lookup operation inside the gpio_devices_srcu read lock. The lookup functions are correct to drop their references since the caller (gpiod_request) will subsequently take its own permanent module and device references safely. Remove these obsolete FIXMEs to prevent misleading future subsystem developers. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Acked-by: Andy Shevchenko Link: https://patch.msgid.link/20260524162708.62949-3-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi-core.c | 4 ---- drivers/gpio/gpiolib-swnode.c | 4 ---- 2 files changed, 8 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 09f860200a05..fbd6945726bf 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -142,10 +142,6 @@ static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin) if (!gdev) return ERR_PTR(-EPROBE_DEFER); - /* - * FIXME: keep track of the reference to the GPIO device somehow - * instead of putting it here. - */ return gpio_device_get_desc(gdev, pin); } diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index 4374067f621e..8d9591aa9304 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -114,10 +114,6 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode, if (IS_ERR(gdev)) return ERR_CAST(gdev); - /* - * FIXME: The GPIO device reference is put at return but the descriptor - * is passed on. Find a proper solution. - */ desc = gpio_device_get_desc(gdev, args.args[0]); *flags = args.args[1]; /* We expect native GPIO flags */ From 25afa211ae336569ddd0043f77e25e1b9b48c4d8 Mon Sep 17 00:00:00 2001 From: David Laight Date: Sat, 6 Jun 2026 21:25:57 +0100 Subject: [PATCH 58/62] gpiolib: Replace strcpy() with memcpy() The length of the string is calculated in order to allocate the correct sized memory block, use the same length to copy the string. Signed-off-by: David Laight Link: https://patch.msgid.link/20260606202633.5018-3-david.laight.linux@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ac2779dbe42b..7bb6f114d64d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -143,13 +143,15 @@ static void desc_free_label(struct rcu_head *rh) static int desc_set_label(struct gpio_desc *desc, const char *label) { struct gpio_desc_label *new = NULL, *old; + size_t len; if (label) { - new = kzalloc_flex(*new, str, strlen(label) + 1); + len = strlen(label); + new = kzalloc_flex(*new, str, len + 1); if (!new) return -ENOMEM; - strcpy(new->str, label); + memcpy(new->str, label, len); } old = rcu_replace_pointer(desc->label, new, 1); From b8840c4f640e07562f15e5fc477ddaf1b2efb241 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 6 May 2026 10:22:11 +0200 Subject: [PATCH 59/62] staging: media: max96712: drop unneeded dependency on OF_GPIO OF_GPIO is selected automatically on all OF systems. Any symbols it controls also provide stubs and are private to GPIOLIB anyway so there's really no reason to select it explicitly. Link: https://patch.msgid.link/20260506082211.5624-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/staging/media/max96712/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/max96712/Kconfig b/drivers/staging/media/max96712/Kconfig index 117fadf81bd0..93a2d583e90d 100644 --- a/drivers/staging/media/max96712/Kconfig +++ b/drivers/staging/media/max96712/Kconfig @@ -2,7 +2,6 @@ config VIDEO_MAX96712 tristate "Maxim MAX96712 Quad GMSL2 Deserializer support" depends on I2C - depends on OF_GPIO depends on VIDEO_DEV select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API From c698cfec79eb71588b512e790fbf9e10a39c3179 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 6 May 2026 10:19:59 +0200 Subject: [PATCH 60/62] bus: ts-nbus: drop unneeded dependency on OF_GPIO OF_GPIO is selected automatically on all OF systems. Any symbols it controls also provide stubs and are private to GPIOLIB anyway so there's really no reason to select it explicitly. Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260506081959.5221-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/bus/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 3181d8aa32a3..e4b1db809187 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -216,7 +216,7 @@ config TI_SYSC config TS_NBUS tristate "Technologic Systems NBUS Driver" depends on SOC_IMX28 - depends on OF_GPIO && PWM + depends on PWM help Driver for the Technologic Systems NBUS which is used to interface with the peripherals in the FPGA of the TS-4600 SoM. From a46f2e5720f5670feda145709d1f0d20be5c7263 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Tue, 9 Jun 2026 05:11:18 +0200 Subject: [PATCH 61/62] gpio: mt7621: fix interrupt banks mapping on gpio chips The GPIO controller's registers are organized as sets of eight 32-bit registers with each set controlling a bank of up to 32 pins. A single interrupt is shared for all of the banks handled by the controller. The driver implements this using three gpio chip instances every one with its own irq chip. Every single pin can generate interrupts having a total of 96 possible interrupts here. It looks like there is a problem with interrupts being properly mapped to the gpio bank using this solution. This problem report is in the following lore's link [0]. Device tree is using two cells for this, so only the interrupt pin and the interrupt type are described there. Changing to have three cells to setup also the bank and implement 'of_node_instance_match()' would also work but this would be an ABI breakage and also a bit incoherent since gpios itself are also using two cells and properly mapped in desired bank using through its pin number on 'of_xlate()'. That said, register a linear IRQ domain of the total of 96 interrupts shared with the three gpio chip instances so the bank and the interrupt is properly decoded and devices using gpio IRQs properly work. [0]: https://lore.kernel.org/linux-gpio/CAAMcf8C_A9dJ_v4QRKtb9eGNOpJ7BZNOGsFP4i2WFOZxOVBPnQ@mail.gmail.com/T/#u Fixes: 4ba9c3afda41 ("gpio: mt7621: Add a driver for MT7621") Co-developed-by: Vicente Bergas Signed-off-by: Vicente Bergas Tested-by: Vicente Bergas Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260609031118.2275735-1-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mt7621.c | 282 ++++++++++++++++++++++++++++--------- 1 file changed, 216 insertions(+), 66 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 91230be51587..a814885ccd5d 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -29,8 +29,8 @@ #define GPIO_REG_EDGE 0xA0 struct mtk_gc { - struct irq_chip irq_chip; struct gpio_generic_chip chip; + struct mtk *parent_priv; int bank; u32 rising; u32 falling; @@ -41,20 +41,32 @@ struct mtk_gc { /** * struct mtk - state container for * data of the platform driver. It is 3 - * separate gpio-chip each one with its - * own irq_chip. - * @dev: device instance + * separate gpio-chip having an IRQ + * linear domain shared for all of them + * @pdev: platform device instance * @base: memory base address + * @irq_domain: IRQ linear domain shared across the three gpio chips * @gpio_irq: irq number from the device tree + * @num_gpios: total number of gpio pins on the three gpio chips * @gc_map: array of the gpio chips */ struct mtk { - struct device *dev; + struct platform_device *pdev; void __iomem *base; + struct irq_domain *irq_domain; int gpio_irq; + int num_gpios; struct mtk_gc gc_map[MTK_BANK_CNT]; }; +static inline struct mtk * +mt7621_gpio_gc_to_priv(struct gpio_chip *gc) +{ + struct mtk_gc *bank = gpiochip_get_data(gc); + + return bank->parent_priv; +} + static inline struct mtk_gc * to_mediatek_gpio(struct gpio_chip *chip) { @@ -67,7 +79,7 @@ static inline void mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; gpio_generic_write_reg(&rg->chip, mtk->base + offset, val); @@ -77,41 +89,62 @@ static inline u32 mtk_gpio_r32(struct mtk_gc *rg, u32 offset) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; return gpio_generic_read_reg(&rg->chip, mtk->base + offset); } -static irqreturn_t -mediatek_gpio_irq_handler(int irq, void *data) +static void +mt7621_gpio_irq_bank_handler(struct mtk_gc *bank) { - struct gpio_chip *gc = data; - struct mtk_gc *rg = to_mediatek_gpio(gc); - irqreturn_t ret = IRQ_NONE; + struct mtk *priv = bank->parent_priv; + struct irq_domain *domain = priv->irq_domain; + int hwbase = bank->chip.gc.offset; unsigned long pending; - int bit; + unsigned int offset; - pending = mtk_gpio_r32(rg, GPIO_REG_STAT); + pending = mtk_gpio_r32(bank, GPIO_REG_STAT); + if (!pending) + return; - for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) { - generic_handle_domain_irq(gc->irq.domain, bit); - mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit)); - ret |= IRQ_HANDLED; + mtk_gpio_w32(bank, GPIO_REG_STAT, pending); + + for_each_set_bit(offset, &pending, MTK_BANK_WIDTH) + generic_handle_domain_irq(domain, hwbase + offset); +} + +static void +mt7621_gpio_irq_handler(struct irq_desc *desc) +{ + struct mtk *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int i; + + chained_irq_enter(chip, desc); + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; + + mt7621_gpio_irq_bank_handler(bank); } + chained_irq_exit(chip, desc); +} - return ret; +static int +mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank) +{ + return hwirq - bank->chip.gc.offset; } static void mediatek_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; - gpiochip_enable_irq(gc, d->hwirq); + gpiochip_enable_irq(gc, mask); guard(gpio_generic_lock_irqsave)(&rg->chip); @@ -119,18 +152,18 @@ mediatek_gpio_irq_unmask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising)); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(mask) & rg->rising)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(mask) & rg->falling)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(mask) & rg->hlevel)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(mask) & rg->llevel)); } static void mediatek_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; scoped_guard(gpio_generic_lock_irqsave, &rg->chip) { @@ -138,22 +171,21 @@ mediatek_gpio_irq_mask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(mask)); } - gpiochip_disable_irq(gc, d->hwirq); + gpiochip_disable_irq(gc, mask); } static int mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; - u32 mask = BIT(pin); + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg)); if (type == IRQ_TYPE_PROBE) { if ((rg->rising | rg->falling | @@ -190,6 +222,26 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) return 0; } +static int +mt7621_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + return gpiochip_reqres_irq(gc, irq); +} + +static void +mt7621_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + gpiochip_relres_irq(gc, irq); +} + static int mediatek_gpio_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) @@ -208,14 +260,123 @@ mediatek_gpio_xlate(struct gpio_chip *chip, static const struct irq_chip mt7621_irq_chip = { .name = "mt7621-gpio", + .irq_request_resources = mt7621_gpio_irq_reqres, + .irq_release_resources = mt7621_gpio_irq_relres, .irq_mask_ack = mediatek_gpio_irq_mask, .irq_mask = mediatek_gpio_irq_mask, .irq_unmask = mediatek_gpio_irq_unmask, .irq_set_type = mediatek_gpio_irq_type, .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, }; +static void +mt7621_gpio_remove(struct platform_device *pdev) +{ + struct mtk *priv = platform_get_drvdata(pdev); + int offset, virq; + + if (priv->gpio_irq > 0) + irq_set_chained_handler_and_data(priv->gpio_irq, NULL, NULL); + + /* Remove all IRQ mappings and delete the domain */ + if (priv->irq_domain) { + for (offset = 0; offset < priv->num_gpios; offset++) { + virq = irq_find_mapping(priv->irq_domain, offset); + irq_dispose_mapping(virq); + } + irq_domain_remove(priv->irq_domain); + } +} + +static struct mtk_gc * +mt7621_gpio_hwirq_to_bank(struct mtk *priv, irq_hw_number_t hwirq) +{ + int i; + + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; + + if (hwirq >= bank->chip.gc.offset && + hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio)) + return bank; + } + + return NULL; +} + +static int +mt7621_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mtk *priv = d->host_data; + struct mtk_gc *bank = mt7621_gpio_hwirq_to_bank(priv, hwirq); + struct platform_device *pdev = priv->pdev; + int ret; + + if (!bank) + return -EINVAL; + + dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n", + irq, (int)hwirq, bank->bank); + + ret = irq_set_chip_data(irq, &bank->chip.gc); + if (ret < 0) + return ret; + + irq_set_chip_and_handler(irq, &mt7621_irq_chip, handle_simple_irq); + irq_set_noprobe(irq); + + return 0; +} + +static void +mt7621_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops mt7621_gpio_irq_domain_ops = { + .map = mt7621_gpio_irq_map, + .unmap = mt7621_gpio_irq_unmap, + .xlate = irq_domain_xlate_twocell, +}; + +static int +mt7621_gpio_irq_setup(struct platform_device *pdev, + struct mtk *priv) +{ + struct device *dev = &pdev->dev; + + priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev), + priv->num_gpios, + &mt7621_gpio_irq_domain_ops, + priv); + if (!priv->irq_domain) { + dev_err(dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } + + irq_set_chained_handler_and_data(priv->gpio_irq, + mt7621_gpio_irq_handler, priv); + irq_set_status_flags(priv->gpio_irq, IRQ_DISABLE_UNLAZY); + + return 0; +} + +static int +mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct mtk *priv = mt7621_gpio_gc_to_priv(gc); + /* gc_offset is relative to this gpio_chip; want real offset */ + int hwirq = offset + gc->offset; + + if (hwirq >= priv->num_gpios) + return -ENXIO; + + return irq_create_mapping(priv->irq_domain, hwirq); +} + static int mediatek_gpio_bank_probe(struct device *dev, int bank) { @@ -228,6 +389,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg = &mtk->gc_map[bank]; memset(rg, 0, sizeof(*rg)); + rg->parent_priv = mtk; rg->bank = bank; dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE); @@ -253,41 +415,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg->chip.gc.of_gpio_n_cells = 2; rg->chip.gc.of_xlate = mediatek_gpio_xlate; + rg->chip.gc.ngpio = MTK_BANK_WIDTH; rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", dev_name(dev), bank); if (!rg->chip.gc.label) return -ENOMEM; rg->chip.gc.offset = bank * MTK_BANK_WIDTH; + if (mtk->gpio_irq > 0) + rg->chip.gc.to_irq = mt7621_gpio_to_irq; - if (mtk->gpio_irq) { - struct gpio_irq_chip *girq; - - /* - * Directly request the irq here instead of passing - * a flow-handler because the irq is shared. - */ - ret = devm_request_irq(dev, mtk->gpio_irq, - mediatek_gpio_irq_handler, IRQF_SHARED, - rg->chip.gc.label, &rg->chip.gc); - - if (ret) { - dev_err(dev, "Error requesting IRQ %d: %d\n", - mtk->gpio_irq, ret); - return ret; - } - - girq = &rg->chip.gc.irq; - gpio_irq_chip_set_chip(girq, &mt7621_irq_chip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_simple_irq; - } - - ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk); + ret = devm_gpiochip_add_data(dev, &rg->chip.gc, rg); if (ret < 0) { dev_err(dev, "Could not register gpio %d, ret=%d\n", rg->chip.gc.ngpio, ret); @@ -322,7 +460,8 @@ mediatek_gpio_probe(struct platform_device *pdev) if (mtk->gpio_irq < 0) return mtk->gpio_irq; - mtk->dev = dev; + mtk->pdev = pdev; + mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT; platform_set_drvdata(pdev, mtk); for (i = 0; i < MTK_BANK_CNT; i++) { @@ -331,7 +470,17 @@ mediatek_gpio_probe(struct platform_device *pdev) return ret; } + if (mtk->gpio_irq > 0) { + ret = mt7621_gpio_irq_setup(pdev, mtk); + if (ret) + goto fail; + } + return 0; + +fail: + mt7621_gpio_remove(pdev); + return ret; } static const struct of_device_id mediatek_gpio_match[] = { @@ -342,6 +491,7 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match); static struct platform_driver mediatek_gpio_driver = { .probe = mediatek_gpio_probe, + .remove = mt7621_gpio_remove, .driver = { .name = "mt7621_gpio", .of_match_table = mediatek_gpio_match, From f953585dafd71ecb0897f9def9c0a3702afc1bf8 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Wed, 10 Jun 2026 13:50:07 -0700 Subject: [PATCH 62/62] gpio: nomadik: remove dead DB8540 code from DB8540 support was removed in commit b6d09f780761 ("pinctrl: nomadik: Drop U8540/9540 support"), but a couple small pieces of related code remained in . Remove them. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260610205007.44881-1-enelsonmoore@gmail.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/gpio-nomadik.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/include/linux/gpio/gpio-nomadik.h b/include/linux/gpio/gpio-nomadik.h index 8061b9826361..44c47645649c 100644 --- a/include/linux/gpio/gpio-nomadik.h +++ b/include/linux/gpio/gpio-nomadik.h @@ -32,9 +32,6 @@ struct fwnode_handle; #define NMK_GPIO_RWIMSC 0x50 #define NMK_GPIO_FWIMSC 0x54 #define NMK_GPIO_WKS 0x58 -/* These appear in DB8540 and later ASICs */ -#define NMK_GPIO_EDGELEVEL 0x5C -#define NMK_GPIO_LEVEL 0x60 /* Pull up/down values */ enum nmk_gpio_pull { @@ -236,19 +233,6 @@ nmk_pinctrl_db8500_init(const struct nmk_pinctrl_soc_data **soc) #endif -#ifdef CONFIG_PINCTRL_DB8540 - -void nmk_pinctrl_db8540_init(const struct nmk_pinctrl_soc_data **soc); - -#else - -static inline void -nmk_pinctrl_db8540_init(const struct nmk_pinctrl_soc_data **soc) -{ -} - -#endif - struct platform_device; #ifdef CONFIG_DEBUG_FS