* [PATCH v3] wifi: ath9k: Obtain system GPIOS from descriptors
@ 2026-03-12 15:09 Linus Walleij
2026-03-12 16:23 ` Andy Shevchenko
0 siblings, 1 reply; 4+ messages in thread
From: Linus Walleij @ 2026-03-12 15:09 UTC (permalink / raw)
To: Kalle Valo, Andy Shevchenko, Arnd Bergmann, Alban Bedel,
Bartosz Golaszewski, Toke Høiland-Jørgensen,
Michał Kępień
Cc: linux-wireless, brcm80211-dev-list.pdl, linux-gpio, Linus Walleij,
Linus Walleij
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ń <kernel@kempniu.pl>
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
This patch set is a long standing attempt to get rid of the global
GPIO numbers from the ath9k Wireless driver.
Maybe Kalle can merge this to the Wireless tree if we agree on this
hack solution.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
Changes in v3:
- Rebased on kernel v7.0-rc1
- Fix up issues as pointed out by Michał Kępień
- Link to v2: https://lore.kernel.org/r/20240423-descriptors-wireless-v2-1-6d1d03b30bfa@linaro.org
Changes in v2:
- Define all the descriptors directly in the ATH79K
GPIO driver in case the driver want to request them directly.
- Link to v1: https://lore.kernel.org/r/20240131-descriptors-wireless-v1-0-e1c7c5d68746@linaro.org
---
drivers/gpio/gpio-ath79.c | 57 ++++++++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath9k/hw.c | 36 +++++++++++++++--------
drivers/net/wireless/ath/ath9k/hw.h | 3 +-
3 files changed, 82 insertions(+), 14 deletions(-)
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index 2ad9f6ac6636..00d8e877d1d5 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
+#include <linux/gpio/machine.h> /* For WLAN GPIOs */
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mod_devicetable.h>
@@ -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] = (struct gpiod_lookup)
+ /*
+ * 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)
+{
+}
+#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 a45351afcf6e..04b4f0347cd9 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -21,7 +21,7 @@
#include <linux/time.h>
#include <linux/bitops.h>
#include <linux/etherdevice.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/unaligned.h>
#include "hw.h"
@@ -2719,19 +2719,29 @@ 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);
- if (err) {
+ /*
+ * 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);
+
+ if (IS_ERR(gpiod)) {
+ err = PTR_ERR(gpiod);
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 +2801,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 +2834,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 +2858,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 eaa07d6dbde0..d9d2f64c5570 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -19,6 +19,7 @@
#include <linux/if_ether.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/firmware.h>
@@ -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;
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20240122-descriptors-wireless-b8da95dcab35
Best regards,
--
Linus Walleij <linusw@kernel.org>
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH v3] wifi: ath9k: Obtain system GPIOS from descriptors
2026-03-12 15:09 [PATCH v3] wifi: ath9k: Obtain system GPIOS from descriptors Linus Walleij
@ 2026-03-12 16:23 ` Andy Shevchenko
2026-03-12 21:26 ` Linus Walleij
0 siblings, 1 reply; 4+ messages in thread
From: Andy Shevchenko @ 2026-03-12 16:23 UTC (permalink / raw)
To: Linus Walleij
Cc: Kalle Valo, Arnd Bergmann, Alban Bedel, Bartosz Golaszewski,
Toke Høiland-Jørgensen, Michał Kępień,
linux-wireless, brcm80211-dev-list.pdl, linux-gpio
On Thu, Mar 12, 2026 at 04:09:53PM +0100, Linus Walleij wrote:
> 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.
...
> +#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);
> +
Redundant blank line.
> + 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] = (struct gpiod_lookup)
The macro below is already compound literal, the '(struct gpiod_lookup)'
is redundant.
> + /*
> + * 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;
> +}
...
> + /*
> + * 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);
> +
Redundant blank line.
> + if (IS_ERR(gpiod)) {
> + err = PTR_ERR(gpiod);
What about
err = PTR_ERR_OR_ZERO(...);
if (err) {
...
?
> ath_err(ath9k_hw_common(ah), "request GPIO%d failed:%d\n",
> gpio, err);
> return;
> }
...
Have you considered using software nodes instead?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v3] wifi: ath9k: Obtain system GPIOS from descriptors
2026-03-12 16:23 ` Andy Shevchenko
@ 2026-03-12 21:26 ` Linus Walleij
2026-03-13 8:19 ` Andy Shevchenko
0 siblings, 1 reply; 4+ messages in thread
From: Linus Walleij @ 2026-03-12 21:26 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Kalle Valo, Arnd Bergmann, Alban Bedel, Bartosz Golaszewski,
Toke Høiland-Jørgensen, Michał Kępień,
linux-wireless, brcm80211-dev-list.pdl, linux-gpio
Hi Andy,
(I'll obviously fix all the syntax issues for v4)
On Thu, Mar 12, 2026 at 5:24 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
> Have you considered using software nodes instead?
That's the big question. And also: have I considered adding device
tree bindings to map to those look-ups, that would provide a way for
new users of these devices to actually do the right thing. I thought of both!
The big problem is that we don't have a handle on the device
and it's name, because it comes from the device tree and
could be named anything. Same thing with the GPIO controller.
If we register lookups or software nodes in the GPIO driver we
don't have a reference to the ath9k device or its name, and if we
register it in the ath9k device, we don't have a handle on the
GPIO controller or its name.
All of these GPIOs "should have" had bindings and "should have"
been in the device tree, but they are not, and I think some of those
device trees are even outside of the Linux kernel so we can't really
fix them either :( it's a mess, I'm just stirring the mud to try and
make it a bit better by removing the global GPIO numbers.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v3] wifi: ath9k: Obtain system GPIOS from descriptors
2026-03-12 21:26 ` Linus Walleij
@ 2026-03-13 8:19 ` Andy Shevchenko
0 siblings, 0 replies; 4+ messages in thread
From: Andy Shevchenko @ 2026-03-13 8:19 UTC (permalink / raw)
To: Linus Walleij
Cc: Kalle Valo, Arnd Bergmann, Alban Bedel, Bartosz Golaszewski,
Toke Høiland-Jørgensen, Michał Kępień,
linux-wireless, brcm80211-dev-list.pdl, linux-gpio
On Thu, Mar 12, 2026 at 10:26:06PM +0100, Linus Walleij wrote:
> Hi Andy,
>
> (I'll obviously fix all the syntax issues for v4)
>
> On Thu, Mar 12, 2026 at 5:24 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
>
> > Have you considered using software nodes instead?
>
> That's the big question. And also: have I considered adding device
> tree bindings to map to those look-ups, that would provide a way for
> new users of these devices to actually do the right thing. I thought of both!
>
> The big problem is that we don't have a handle on the device
> and it's name, because it comes from the device tree and
> could be named anything. Same thing with the GPIO controller.
>
> If we register lookups or software nodes in the GPIO driver we
> don't have a reference to the ath9k device or its name, and if we
> register it in the ath9k device, we don't have a handle on the
> GPIO controller or its name.
>
> All of these GPIOs "should have" had bindings and "should have"
> been in the device tree, but they are not, and I think some of those
> device trees are even outside of the Linux kernel so we can't really
> fix them either :( it's a mess, I'm just stirring the mud to try and
> make it a bit better by removing the global GPIO numbers.
Thanks for this elaboration! Since Bart is the person who wants to move
the lookup tables to software nodes, I leave that part to him. For time
being your patch looks good (after addressing style and minor issues I
pointed out), feel free to add
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
to the next version.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-03-13 8:19 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-12 15:09 [PATCH v3] wifi: ath9k: Obtain system GPIOS from descriptors Linus Walleij
2026-03-12 16:23 ` Andy Shevchenko
2026-03-12 21:26 ` Linus Walleij
2026-03-13 8:19 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox