From: Linus Walleij Date: Tue, 17 Mar 2026 08:45:20 +0000 (+0100) Subject: wifi: ath9k: Obtain system GPIOS from descriptors X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=34a5329beee86a22a446e27eb37f06caa63479ca;p=thirdparty%2Fkernel%2Flinux.git wifi: ath9k: Obtain system GPIOS from descriptors The ath9k has an odd use of system-wide GPIOs: if the chip does not have internal GPIO capability, it will try to obtain a GPIO line from the system GPIO controller: if (BIT(gpio) & ah->caps.gpio_mask) ath9k_hw_gpio_cfg_wmac(...); else if (AR_SREV_SOC(ah)) ath9k_hw_gpio_cfg_soc(ah, gpio, out, label); Where ath9k_hw_gpio_cfg_soc() will attempt to issue gpio_request_one() passing the local GPIO number of the controller (0..31) to gpio_request_one(). This is somewhat peculiar and possibly even dangerous: there is nowadays no guarantee of the numbering of these system-wide GPIOs, and assuming that GPIO 0..31 as used by ath9k would correspond to GPIOs 0..31 on the system as a whole seems a bit wild. Register all 32 GPIOs at index 0..31 directly in the ATH79K GPIO driver and associate with the NULL device (making them widely available) if and only if we are probing ATH79K wifi from the AHB bus (used for SoCs). We obtain these offsets from the NULL device if necessary. These GPIOs should ideally be defined in the device tree instead, but we have no control over that for the legacy code path. Testcompiled with the ath79 defconfig. Reported-by: Michał Kępień Acked-by: Toke Høiland-Jørgensen Reviewed-by: Andy Shevchenko Acked-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Tested-by: Michał Kępień Link: https://patch.msgid.link/20260317-descriptors-wireless-v6-1-b19ecff9cd2b@kernel.org Signed-off-by: Jeff Johnson --- diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index 2ad9f6ac66362..85bd994d15d48 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -11,6 +11,7 @@ #include #include #include +#include /* For WLAN GPIOs */ #include #include #include @@ -214,6 +215,56 @@ static const struct of_device_id ath79_gpio_of_match[] = { }; MODULE_DEVICE_TABLE(of, ath79_gpio_of_match); +#if IS_ENABLED(CONFIG_ATH9K_AHB) +/* + * This registers all of the ath79k GPIOs as descriptors to be picked + * directly from the ATH79K wifi driver if the two are jitted together + * in the same SoC. + */ +#define ATH79K_WIFI_DESCS 32 +static int ath79_gpio_register_wifi_descriptors(struct device *dev, + const char *label) +{ + struct gpiod_lookup_table *lookup; + int i; + + /* Create a gpiod lookup using gpiochip-local offsets + 1 for NULL */ + lookup = devm_kzalloc(dev, + struct_size(lookup, table, ATH79K_WIFI_DESCS + 1), + GFP_KERNEL); + if (!lookup) + return -ENOMEM; + + /* + * Ugly system-wide lookup for the NULL device: we know this + * is already NULL but explicitly assign it here for people to + * know what is going on. (Yes this is an ugly legacy hack, live + * with it.) + */ + lookup->dev_id = NULL; + + for (i = 0; i < ATH79K_WIFI_DESCS; i++) { + lookup->table[i] = + /* + * Set the HW offset on the chip and the lookup + * index to the same value, so looking up index 0 + * will get HW offset 0, index 1 HW offset 1 etc. + */ + GPIO_LOOKUP_IDX(label, i, "ath9k", i, GPIO_ACTIVE_HIGH); + } + + gpiod_add_lookup_table(lookup); + + return 0; +} +#else +static int ath79_gpio_register_wifi_descriptors(struct device *dev, + const char *label) +{ + return 0; +} +#endif + static int ath79_gpio_probe(struct platform_device *pdev) { struct gpio_generic_chip_config config; @@ -276,7 +327,11 @@ static int ath79_gpio_probe(struct platform_device *pdev) girq->handler = handle_simple_irq; } - return devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl); + err = devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl); + if (err) + return err; + + return ath79_gpio_register_wifi_descriptors(dev, ctrl->chip.gc.label); } static struct platform_driver ath79_gpio_driver = { diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index a45351afcf6ee..05c95e67a853b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include "hw.h" @@ -2719,19 +2719,28 @@ static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah, u32 gpio, u32 type) static void ath9k_hw_gpio_cfg_soc(struct ath_hw *ah, u32 gpio, bool out, const char *label) { + enum gpiod_flags flags = out ? GPIOD_OUT_LOW : GPIOD_IN; + struct gpio_desc *gpiod; int err; - if (ah->caps.gpio_requested & BIT(gpio)) + if (ah->gpiods[gpio]) return; - err = devm_gpio_request_one(ah->dev, gpio, out ? GPIOF_OUT_INIT_LOW : GPIOF_IN, label); + /* + * Obtains a system specific GPIO descriptor from another GPIO controller. + * Ideally this should come from the device tree, this is a legacy code + * path. + */ + gpiod = gpiod_get_index(NULL, "ath9k", gpio, flags); + err = PTR_ERR_OR_ZERO(gpiod); if (err) { ath_err(ath9k_hw_common(ah), "request GPIO%d failed:%d\n", gpio, err); return; } - ah->caps.gpio_requested |= BIT(gpio); + gpiod_set_consumer_name(gpiod, label); + ah->gpiods[gpio] = gpiod; } static void ath9k_hw_gpio_cfg_wmac(struct ath_hw *ah, u32 gpio, bool out, @@ -2791,10 +2800,12 @@ void ath9k_hw_gpio_free(struct ath_hw *ah, u32 gpio) if (!AR_SREV_SOC(ah)) return; - WARN_ON(gpio >= ah->caps.num_gpio_pins); + if (ah->gpiods[gpio]) { + gpiod_put(ah->gpiods[gpio]); + ah->gpiods[gpio] = NULL; + } - if (ah->caps.gpio_requested & BIT(gpio)) - ah->caps.gpio_requested &= ~BIT(gpio); + WARN_ON(gpio >= ah->caps.num_gpio_pins); } EXPORT_SYMBOL(ath9k_hw_gpio_free); @@ -2822,8 +2833,8 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) val = REG_READ(ah, AR_GPIO_IN(ah)) & BIT(gpio); else val = MS_REG_READ(AR, gpio); - } else if (BIT(gpio) & ah->caps.gpio_requested) { - val = gpio_get_value(gpio) & BIT(gpio); + } else if (ah->gpiods[gpio]) { + val = gpiod_get_value(ah->gpiods[gpio]); } else { WARN_ON(1); } @@ -2846,8 +2857,8 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) AR7010_GPIO_OUT : AR_GPIO_IN_OUT(ah); REG_RMW(ah, out_addr, val << gpio, BIT(gpio)); - } else if (BIT(gpio) & ah->caps.gpio_requested) { - gpio_set_value(gpio, val); + } else if (ah->gpiods[gpio]) { + gpiod_set_value(ah->gpiods[gpio], val); } else { WARN_ON(1); } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index eaa07d6dbde00..d9d2f64c55700 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -302,7 +303,6 @@ struct ath9k_hw_capabilities { u8 max_rxchains; u8 num_gpio_pins; u32 gpio_mask; - u32 gpio_requested; u8 rx_hp_qdepth; u8 rx_lp_qdepth; u8 rx_status_len; @@ -783,6 +783,7 @@ struct ath_hw { struct ath9k_hw_capabilities caps; struct ath9k_channel channels[ATH9K_NUM_CHANNELS]; struct ath9k_channel *curchan; + struct gpio_desc *gpiods[32]; union { struct ar5416_eeprom_def def;