* [PATCH wireless-next 0/2] wifi: mt76: mt7925: LED support for USB adapters
@ 2026-11-03 13:35 Josef Schlehofer
2026-11-03 13:35 ` [PATCH wireless-next 1/2] wifi: mt76: allow LEDs with only brightness_set_blocking Josef Schlehofer
2026-11-03 13:35 ` [PATCH wireless-next 2/2] wifi: mt76: mt7925: add LED support for USB adapters Josef Schlehofer
0 siblings, 2 replies; 3+ messages in thread
From: Josef Schlehofer @ 2026-11-03 13:35 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang
Cc: Matthias Brugger, AngeloGioacchino Del Regno, Ming Yen Hsieh,
JB Tsai, Allan Wang, Quan Zhou, linux-wireless, linux-mediatek,
linux-arm-kernel, linux-kernel
MT7925 USB adapters such as the NETGEAR A9000 have a status LED on
GPIO 8 of the chip. MediaTek's Windows driver asks the firmware to
switch it with UNI command 0x2a, but the firmware in linux-firmware
does not act on that command [1].
This series drives the pin from the host instead. The layout of the
CBTOP GPIO block comes from the coda headers in MediaTek's gen4m driver
for MT6639, which uses the same CBTOP register map as MT7925. The first
patch lets the mt76 core register an LED that only provides
brightness_set_blocking, which a USB driver needs. The second patch
adds the LED to mt7925.
Tested on a Turris Omnia with an A9000 and firmware 20231227 over
several days. The LED follows the throughput trigger and manual
brightness changes, and turns off when the radio goes down.
Sean, JB, is it fine to drive these GPIO registers from the host, or
does the firmware rely on GPIO 8 staying in its default function?
[1] https://lore.kernel.org/linux-wireless/20261012125310.13917-1-pepe.schlehofer@gmail.com/
Josef Schlehofer (2):
wifi: mt76: allow LEDs with only brightness_set_blocking
wifi: mt76: mt7925: add LED support for USB adapters
drivers/net/wireless/mediatek/mt76/mac80211.c | 12 ++++-
.../net/wireless/mediatek/mt76/mt7925/init.c | 51 +++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 2 +
.../net/wireless/mediatek/mt76/mt7925/regs.h | 8 +++
drivers/net/wireless/mediatek/mt76/mt792x.h | 2 +
5 files changed, 73 insertions(+), 2 deletions(-)
base-commit: 991a73f4909f1348ffb4b38765cdb522a775e4c2
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH wireless-next 1/2] wifi: mt76: allow LEDs with only brightness_set_blocking
2026-11-03 13:35 [PATCH wireless-next 0/2] wifi: mt76: mt7925: LED support for USB adapters Josef Schlehofer
@ 2026-11-03 13:35 ` Josef Schlehofer
2026-11-03 13:35 ` [PATCH wireless-next 2/2] wifi: mt76: mt7925: add LED support for USB adapters Josef Schlehofer
1 sibling, 0 replies; 3+ messages in thread
From: Josef Schlehofer @ 2026-11-03 13:35 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang
Cc: Matthias Brugger, AngeloGioacchino Del Regno, Ming Yen Hsieh,
JB Tsai, Allan Wang, Quan Zhou, linux-wireless, linux-mediatek,
linux-arm-kernel, linux-kernel
mt76_led_init() and mt76_led_cleanup() skip the LED unless the driver
sets brightness_set or blink_set. A USB driver that switches its LED
through register writes has to sleep and can only provide
brightness_set_blocking. Accept that callback as well.
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
drivers/net/wireless/mediatek/mt76/mac80211.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index c9762082f473..e3eea578a97c 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -193,13 +193,21 @@ static const struct cfg80211_sar_capa mt76_sar_capa = {
.freq_ranges = &mt76_sar_freq_ranges[0],
};
+static bool mt76_led_has_ops(struct mt76_phy *phy)
+{
+ struct led_classdev *cdev = &phy->leds.cdev;
+
+ return cdev->brightness_set || cdev->brightness_set_blocking ||
+ cdev->blink_set;
+}
+
static int mt76_led_init(struct mt76_phy *phy)
{
struct mt76_dev *dev = phy->dev;
struct ieee80211_hw *hw = phy->hw;
struct device_node *np = dev->dev->of_node;
- if (!phy->leds.cdev.brightness_set && !phy->leds.cdev.blink_set)
+ if (!mt76_led_has_ops(phy))
return 0;
np = of_get_child_by_name(np, "led");
@@ -242,7 +250,7 @@ static int mt76_led_init(struct mt76_phy *phy)
static void mt76_led_cleanup(struct mt76_phy *phy)
{
- if (!phy->leds.cdev.brightness_set && !phy->leds.cdev.blink_set)
+ if (!mt76_led_has_ops(phy))
return;
led_classdev_unregister(&phy->leds.cdev);
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH wireless-next 2/2] wifi: mt76: mt7925: add LED support for USB adapters
2026-11-03 13:35 [PATCH wireless-next 0/2] wifi: mt76: mt7925: LED support for USB adapters Josef Schlehofer
2026-11-03 13:35 ` [PATCH wireless-next 1/2] wifi: mt76: allow LEDs with only brightness_set_blocking Josef Schlehofer
@ 2026-11-03 13:35 ` Josef Schlehofer
1 sibling, 0 replies; 3+ messages in thread
From: Josef Schlehofer @ 2026-11-03 13:35 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang
Cc: Matthias Brugger, AngeloGioacchino Del Regno, Ming Yen Hsieh,
JB Tsai, Allan Wang, Quan Zhou, linux-wireless, linux-mediatek,
linux-arm-kernel, linux-kernel
MT7925 USB adapters such as the NETGEAR A9000 have their status LED on
GPIO 8 of the chip. MediaTek's Windows driver asks the firmware to
switch it, but the firmware in linux-firmware does not act on that
request.
Drive the pin from the host instead. Set its level, then switch it from
its default function to a GPIO output through the CBTOP GPIO registers.
The register layout follows the coda headers in MediaTek's gen4m
driver. The LED of the A9000 is active low.
While the adapter is idle, the throughput trigger keeps requesting the
same state every second. Only write the registers when the state
changes, and forget the cached state whenever the firmware is loaded.
Link: https://github.com/morrownr/USB-WiFi/issues/731
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
---
.../net/wireless/mediatek/mt76/mt7925/init.c | 51 +++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 2 +
.../net/wireless/mediatek/mt76/mt7925/regs.h | 8 +++
drivers/net/wireless/mediatek/mt76/mt792x.h | 2 +
4 files changed, 63 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index 8c3049b66a85..3dfb4b2d997c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -187,6 +187,56 @@ static int mt7925_init_nan_cap(struct mt76_dev *mdev)
return 0;
}
+static int mt7925_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct mt76_phy *mphy = container_of(led_cdev, struct mt76_phy,
+ leds.cdev);
+ struct mt792x_dev *dev = container_of(mphy->dev, struct mt792x_dev,
+ mt76);
+ bool on = brightness != LED_OFF;
+ u8 pin = mphy->leds.pin;
+ u32 mask = BIT(pin % 32);
+
+ mt792x_mutex_acquire(dev);
+ if (dev->pm.suspended || dev->led_state == on)
+ goto out;
+
+ if (on ^ mphy->leds.al)
+ mt76_wr(dev, MT_CBTOP_GPIO_DOUT_SET(pin), mask);
+ else
+ mt76_wr(dev, MT_CBTOP_GPIO_DOUT_CLR(pin), mask);
+
+ /* the pin starts in another function, make it a GPIO output */
+ if (dev->led_state < 0) {
+ mt76_wr(dev, MT_CBTOP_GPIO_DIR_SET(pin), mask);
+ mt76_wr(dev, MT_CBTOP_GPIO_MODE_MOD(pin),
+ MT_CBTOP_GPIO_MODE_MOD_EN << ((pin % 8) * 4));
+ }
+
+ dev->led_state = on;
+out:
+ mt792x_mutex_release(dev);
+
+ return 0;
+}
+
+static void mt7925_init_led(struct mt792x_dev *dev)
+{
+ struct mt76_phy *mphy = &dev->mphy;
+
+ if (!IS_ENABLED(CONFIG_MT76_LEDS) || !mt76_is_usb(&dev->mt76) ||
+ !is_mt7925(&dev->mt76))
+ return;
+
+ /* MediaTek's Windows driver uses GPIO 8 on MT7925 USB adapters */
+ mphy->leds.pin = 8;
+ /* the LED of the NETGEAR A9000 is active low */
+ mphy->leds.al = true;
+ mphy->leds.cdev.flags |= LED_HW_PLUGGABLE;
+ mphy->leds.cdev.brightness_set_blocking = mt7925_led_set_brightness;
+}
+
static void mt7925_init_work(struct work_struct *work)
{
struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
@@ -208,6 +258,7 @@ static void mt7925_init_work(struct work_struct *work)
}
dev->mt76.init_wiphy = mt7925_init_nan_cap;
+ mt7925_init_led(dev);
ret = mt76_register_device(&dev->mt76, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 26cdb3f0a98a..91564a2c1d65 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1191,6 +1191,8 @@ int mt7925_run_firmware(struct mt792x_dev *dev)
{
int err;
+ dev->led_state = -1;
+
err = mt792x_load_firmware(dev);
if (err)
return err;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
index e040dff321dd..73bd8c58dd84 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
@@ -91,6 +91,14 @@
#define MT_WFSYS_SW_RST_B 0x7c000140
+#define MT_CBTOP_GPIO_BASE 0x70005000
+#define MT_CBTOP_GPIO(ofs) (MT_CBTOP_GPIO_BASE + (ofs))
+#define MT_CBTOP_GPIO_DIR_SET(_n) MT_CBTOP_GPIO(0x004 + ((_n) / 32) * 0x10)
+#define MT_CBTOP_GPIO_DOUT_SET(_n) MT_CBTOP_GPIO(0x104 + ((_n) / 32) * 0x10)
+#define MT_CBTOP_GPIO_DOUT_CLR(_n) MT_CBTOP_GPIO(0x108 + ((_n) / 32) * 0x10)
+#define MT_CBTOP_GPIO_MODE_MOD(_n) MT_CBTOP_GPIO(0x30c + ((_n) / 8) * 0x10)
+#define MT_CBTOP_GPIO_MODE_MOD_EN BIT(3)
+
#define MT7925_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370)
#define MT7928_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x400)
#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0)
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index a7d71e007da0..73c23f509b99 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -324,6 +324,8 @@ struct mt792x_dev {
u8 fw_debug;
u8 fw_features;
+ /* LED state last written to the GPIO, -1 if unknown */
+ s8 led_state;
struct mt76_connac_pm pm;
struct mt76_connac_coredump coredump;
--
2.54.0 (Apple Git-157)
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-03 13:35 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03 13:35 [PATCH wireless-next 0/2] wifi: mt76: mt7925: LED support for USB adapters Josef Schlehofer
2026-11-03 13:35 ` [PATCH wireless-next 1/2] wifi: mt76: allow LEDs with only brightness_set_blocking Josef Schlehofer
2026-11-03 13:35 ` [PATCH wireless-next 2/2] wifi: mt76: mt7925: add LED support for USB adapters Josef Schlehofer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox