mt7915: MT7916 LED never lights — init_led_mux() matches 0x7916, chip ID is 0x7906

Open
#1,157 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
1-2 days
Newbie friendliness
56/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
c, linux
Domain
embedded-iot

Research direction

Start with drivers/net/wireless/mediatek/mt76/mt7915/init.c, especially mt7915_init_led_mux() and the LED polarity setup. Check how the driver identifies the MT7916 PCI device and configures LED pins for PCIe devices without a device tree node. The reported mux and polarity behavior should be corrected while preserving boards with different LED wiring, then verified on MT7916 hardware.

Written by the indexing model from the issue text.

Description

I built a small x86 router (Intel N100, NixOS) and put an AsiaRF AW7916-NPD in it — MT7916 on a mini-PCIe card, through AsiaRF's own ACPCX1-M01 PCIe-to-mPCIe adapter. Everything works great with mt7915e: AP mode, both bands, WPA3, beamforming, no complaints at all. The only thing that never worked was the Wi-Fi LED on the adapter. It stayed dark no matter what was written to /sys/class/leds/mt76-phy*.

I first assumed the module simply doesn't route the LED, but the adapter traces go straight to the mPCIe connector, so I followed the signal from sysfs down to the pins. It turned out to be two small things in the driver; with both fixed the LED now blinks with traffic exactly like an Ethernet port LED. Details below, in case they are useful. How to fix it properly is of course up to you.

Setup

  • Card: AsiaRF AW7916-NPD (MT7916AN + MT7976DN), PCI ID 14c3:7906, DBDC (phy0 2.4 GHz, phy1 5 GHz)
  • Host: Intel N100 (x86_64), no device tree
  • Kernel: 7.2.10, in-tree mt7915e, CONFIG_MT76_LEDS=y
  • The LED code in mt7915/init.c is identical in v6.12, v7.2 and current openwrt/mt76 master; I also found no change to it in mediatek/mtk-openwrt-feeds (snapshot of 2026-10-14)

1. LED GPIO mux is never written for MT7916

mt7915_init_led_mux() switches on mt76_chip(&dev->mt76) and has case 0x7916: in all three branches. But mt76_chip() returns the PCI device ID, which is 0x7906 for MT7916 — that is what is_mt7916() compares against, and what mt7915_mmio_probe() uses to select the register map. So no case matches, and the mux stays at its reset value.

(case 0x7916 is correct in mt7915_check_eeprom(), because that one compares the ID stored in the EEPROM, which really is 0x7916 — maybe that is where the constant came from.)

What the card shows via debugfs (regidx/regval):

register address expected from code actual
MT_LED_GPIO_MUX1 0x70005054 0x33000000 0x00000000
MT_LED_EN(0/1) 0x18013040/44 1 1
MT_LED_CTRL(1) 0x18013004 BAND 0x10

The LED controller is configured correctly; only the pin mux is missing. Sanity checks: MT_HW_REV reads 0x8a00, writing MUX1 reads back, and a full chip reset via sys_recovery (which goes through mac_init() → init_led_mux() again) leaves MUX1 at 0. Writing 0x33000000 by hand makes the LED light up immediately.

I also checked each field separately: GPIO 14 (MUX1[27:24] = 3) outputs LED channel 0, GPIO 15 (MUX1[31:28] = 3) outputs channel 1, so the values in the MT7916 branch are right — only the case label is wrong. On this module both GPIOs go to the same LED_WLAN# pin (44) through a dual diode (D2 on the board), so enabling both, as the DBDC branch does, is fine here.

2. Polarity is inverted on PCIe cards without a DT node

With the mux fixed, the LED is inverted: brightness 0 lights it, 255 turns it off, and phyNtpt shows a steady glow with short dark blips.

The mini-PCIe LED_WLAN# / LED_WWAN# / LED_WPAN# lines are active-low by spec. On this adapter each LED is wired anode → 330 Ω → 3.3 V, cathode → the mPCIe pin, so the card has to pull the pin low to light it. The driver sets MT_LED_CTRL_POLARITY only when leds.al is set, and that comes only from led-active-low in the device tree, which a PCIe card in an x86 box does not have.

Setting POLARITY by hand gives the correct behaviour (off is dark, on is lit, blink blinks).

What worked for me

For reference only — I'm sure you will want to solve the polarity part differently (I can imagine boards wired the other way):

--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -243,7 +243,10 @@
 
 	/* control LED */
 	val = MT_LED_CTRL_KICK;
-	if (dev->mphy.leds.al)
+	/* PCIe cards without a DT node drive the mini-PCIe / M.2 LED_*#
+	 * lines, which are active-low by spec.
+	 */
+	if (dev->mphy.leds.al || !dev->mt76.dev->of_node)
 		val |= MT_LED_CTRL_POLARITY;
 	if (mphy->band_idx)
 		val |= MT_LED_CTRL_BAND;
@@ -571,7 +574,7 @@
 			mt76_rmw_field(dev, MT_LED_GPIO_MUX0,
 				       GENMASK(11, 8), 1);
 			break;
-		case 0x7916:
+		case 0x7906:
 			mt76_rmw_field(dev, MT_LED_GPIO_MUX1,
 				       GENMASK(27, 24), 3);
 			mt76_rmw_field(dev, MT_LED_GPIO_MUX1,
@@ -590,7 +593,7 @@
 			mt76_rmw_field(dev, MT_LED_GPIO_MUX0,
 				       GENMASK(11, 8), 1);
 			break;
-		case 0x7916:
+		case 0x7906:
 			mt76_rmw_field(dev, MT_LED_GPIO_MUX1,
 				       GENMASK(31, 28), 3);
 			break;
@@ -607,7 +610,7 @@
 			mt76_rmw_field(dev, MT_LED_GPIO_MUX0,
 				       GENMASK(7, 4), 1);
 			break;
-		case 0x7916:
+		case 0x7906:
 			mt76_rmw_field(dev, MT_LED_GPIO_MUX1,
 				       GENMASK(27, 24), 3);
 			break;

With this module loaded from a cold boot: MUX1 = 0x33000000 (written by the driver itself), CTRL1 = 0x12 (BAND | POLARITY), and the LED follows the trigger correctly. It has been running on the router since.

Side note, not a bug: I ended up using the netdev trigger on the 5 GHz interface (tx + rx, no link) and keeping the 2.4 GHz channel off. Since both channels share one physical LED here, any channel that is "on" keeps it lit, and phyNtpt (created with IEEE80211_TPT_LEDTRIG_FL_RADIO) is solid whenever the radio is up.

Happy to test patches on this hardware.

Dominant language
C
Stars
895
Forks
461
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openwrt/mt76

All issues in openwrt/mt76

Similar issues

More C issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.