Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH 0/4] bcma/ssb/b43: BCM4352 AC-PHY wip
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri

This series introduce the initial support for BCM4352, an AC class
chip, this is just a gentle introduction before the bulk of edits, 
because in reality i already have AC support working up to RX signal 
and now debugging TX.
From the latest dmesg grepped from my router, this patch series allow
the user to reach to the "Bus registered" message plus the correct init
of the DMA that took me two weeks to discover.

[   18.280000] bcma-pci-bridge 0000:01:00.0: enabling device (0000 -> 0002)
[   18.280000] bcma-pci-bridge 0000:01:00.0: bus0: Found chip with id 0x4352, rev 0x03 and package 0x01
[   18.290000] bcma-pci-bridge 0000:01:00.0: bus0: Core 0 found: ChipCommon (manuf 0x4BF, id 0x800, rev 0x2B, class 0x0)
[   18.300000] bcma-pci-bridge 0000:01:00.0: bus0: Core 1 found: IEEE 802.11 (manuf 0x4BF, id 0x812, rev 0x2A, class 0x0)
[   18.320000] bcma-pci-bridge 0000:01:00.0: bus0: Core 2 found: ARM CR4 (manuf 0x4BF, id 0x83E, rev 0x02, class 0x0)
[   18.330000] bcma-pci-bridge 0000:01:00.0: bus0: Core 3 found: PCIe Gen2 (manuf 0x4BF, id 0x83C, rev 0x01, class 0x0)
[   18.340000] bcma-pci-bridge 0000:01:00.0: bus0: Core 4 found: USB 2.0 Device (manuf 0x4BF, id 0x81A, rev 0x11, class 0x0)
[   18.380000] bcma: bcma: SPROM rev 11 parsed: board=0x0668 rev=4947 ccode=0x0000 txchain=0x3 rxchain=0x3 antswitch=0 subband5gver=4
[   18.390000] bcma: bcma: SPROM r11 chain0 rxgains 2g={0,0,0} 5gl={3,6,1} 5gm={7,15,1} 5gh={7,15,1}
[   18.400000] bcma: bcma: SPROM r11 chain1 rxgains 2g={0,0,0} 5gl={3,6,1} 5gm={7,15,1} 5gh={7,15,1}
[   18.410000] bcma: bcma: SPROM r11 chain2 rxgains 2g={0,0,0} 5gl={3,6,1} 5gm={7,15,1} 5gh={7,15,1}
[   18.420000] bcma-pci-bridge 0000:01:00.0: bus0: Bus registered
[   19.340000] b43-phy0: Broadcom 4352 WLAN found (core revision 42)
[   19.340000] b43-phy0: Found PHY: Analog 12, Type 11 (AC), Revision 1
[   19.350000] b43-phy0: Found Radio: Manuf 0x17F, ID 0x2069, Revision 4, Version 0
[   44.250000] b43-phy0: DEBUG pre-PSM_RUN: MACCTL=0x00020504 IRQ_REASON=0x00000000 SHM[UCODEREV]=0x0000
[   44.260000] b43-phy0: DEBUG post-PSM_RUN: MACCTL=0x00020502 IRQ_REASON=0x00000000
[   44.260000] b43-phy0: Loading firmware version 784.2 (2012-08-15 21:43:22)
[   44.270000] b43-phy0: phy-ac: rxgain_init applied (2 cores, UNII-1 5gl, sprom rev 11)
[   44.280000] b43-phy0: phy-ac: rxgain core0: triso=6 elnagain=3 trelnabyp=1 (gainctx=0x14)
[   44.290000] b43-phy0: phy-ac: rxgain core1: triso=6 elnagain=3 trelnabyp=1 (gainctx=0x14)
[   44.500000] b43-phy0: DEBUG dma_setup RX DMA64: base=0x020c0000 alo=0x020c0000 ahi=0x80000000 addrext=0x0 translation=0x80000000 in_low=0 ctl=0x0000084d
[   44.720000] b43 bcma0:1 phy0-ap0: entered allmulticast mode
[   44.730000] b43 bcma0:1 phy0-ap0: entered promiscuous mode

The two patches generated with the help of claude are marked as such.
The other two are minimal and did not require assistance from claude.

Assisted-by: claude:claude-Opus-4.8
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
Alessio Ferri (4):
      bcma: host_pci: add BCM4352 device ID 0x43b3
      bcma: pmu: program max resource mask on BCM4352/BCM4360
      ssb: bcma: add SPROM revision 11 extraction
      b43: align DMA64 descriptor ring to 64K on AC cores

 drivers/bcma/driver_chipcommon_pmu.c    |   8 +++
 drivers/bcma/host_pci.c                 |   1 +
 drivers/bcma/sprom.c                    | 100 +++++++++++++++++++++++++++++++-
 drivers/net/wireless/broadcom/b43/dma.c |  20 +++++--
 include/linux/ssb/ssb.h                 |  31 ++++++++++
 include/linux/ssb/ssb_regs.h            |  44 ++++++++++++++
 6 files changed, 199 insertions(+), 5 deletions(-)
---
base-commit: e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
change-id: 20260617-b43-add-4352-wip-f3c3a3d159d8

Best regards,
-- 
Alessio Ferri <alessio.ferri.3012@gmail.com>


^ permalink raw reply

* [PATCH 2/4] bcma: pmu: program max resource mask on BCM4352/BCM4360
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

bcma_pmu_resources_init() has no case for the BCM4352/BCM4360 family.
The vendor instead set the max mask for chip rev > 2 with CS bit 5 clear.
Match the vendor behaviour.

Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/bcma/driver_chipcommon_pmu.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index 263ef6fa1d0f..cdd5f7d668ba 100644
--- a/drivers/bcma/driver_chipcommon_pmu.c
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -188,6 +188,14 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
 			  BCMA_RES_4314_WL_CORE_READY;
 		max_msk = 0x3FFFFFFF;
 		break;
+	case BCMA_CHIP_ID_BCM4352:
+	case BCMA_CHIP_ID_BCM4360:
+		/*
+		 * Vendor programs the max mask here for chip rev > 2 with ChipStatus bit 5 clear
+		 */
+		if (bus->chipinfo.rev > 2 && !(cc->status & BIT(5)))
+			max_msk = 0x1ff;
+		break;
 	default:
 		bcma_debug(bus, "PMU resource config unknown or not needed for device 0x%04X\n",
 			   bus->chipinfo.id);

-- 
2.54.0


^ permalink raw reply related

* [PATCH 4/4] b43: align DMA64 descriptor ring to 64K on AC cores
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

The AC d11 core (core_rev 40/42, BCM4352) exposes a 16-bit DMA descriptor
pointer (wrap mask 0xffff, not the 0x1fff b43 assumes). With an 8K-aligned
ring the engine wraps the pointer outside the ring and raises I_DE on RX.
Allocate the 64-bit ring with 64K size on these cores so the base is
64K-aligned and (base & 0xffff) == 0; dma_alloc_coherent() aligns to the
allocation size. ring_mem_size widens to u32 to hold 0x10000. N-PHY cores
report 0x1fff and keep the 8K allocation.

Assisted-by: claude:claude-Opus-4.8
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/net/wireless/broadcom/b43/dma.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c
index 05da6987a845..cb308080312b 100644
--- a/drivers/net/wireless/broadcom/b43/dma.c
+++ b/drivers/net/wireless/broadcom/b43/dma.c
@@ -404,6 +404,18 @@ static inline
 	}
 }
 
+static u32 b43_dma64_ringmem_size(struct b43_dmaring *ring)
+{
+	/* AC d11 cores (core_rev 40/42) expose a 16-bit descriptor pointer
+	 * (mask 0xffff) and need a 64K-aligned ring; dma_alloc_coherent()
+	 * aligns to the (power-of-2) allocation size. Smaller cores use the
+	 * 0x1fff mask and are fine with 8K.
+	 */
+	if (ring->dev->dev->core_rev == 40 || ring->dev->dev->core_rev == 42)
+		return 0x10000;
+	return B43_DMA64_RINGMEMSIZE;
+}
+
 static int alloc_ringmemory(struct b43_dmaring *ring)
 {
 	/* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
@@ -415,8 +427,8 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
 	 * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use
 	 * more than 256 slots for ring.
 	 */
-	u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
-				B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+	u32 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+			b43_dma64_ringmem_size(ring) : B43_DMA32_RINGMEMSIZE;
 
 	ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev,
 					    ring_mem_size, &(ring->dmabase),
@@ -429,8 +441,8 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
 
 static void free_ringmemory(struct b43_dmaring *ring)
 {
-	u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
-				B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+	u32 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+			b43_dma64_ringmem_size(ring) : B43_DMA32_RINGMEMSIZE;
 	dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size,
 			  ring->descbase, ring->dmabase);
 }

-- 
2.54.0


^ permalink raw reply related

* [PATCH 3/4] ssb: bcma: add SPROM revision 11 extraction
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

SPROM revision 11 is needed with the BCM4352/BCM4360 family. Extend
struct ssb_sprom with the rev-11 fields actually parsed (per-band rxgains,
per-chain maxp/pa power info, 80/160 MHz MCS power offsets, pdoffset40ma)
and add bcma_sprom_extract_r11(), dispatched from bcma_sprom_get() when the
SPROM revision word reads as 11. bcma_sprom_get() already probes the rev-11
buffer size, and bcma_sprom_valid() populates bus->sprom.revision before
the dispatch. Revisions <= 10 keep the extract_r8 path.

Header fields (IL0MAC/ANTAVAIL/TXRXC/CCODE) reuse the rev-8 masks/shifts;
only the absolute offsets moved. CCODE uses 0x96, since rev-8 0x92 collides
with the middle word of the rev-11 IL0MAC block.

Assisted-by: Claude:claude-Opus-4.8
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/bcma/sprom.c         | 100 ++++++++++++++++++++++++++++++++++++++++++-
 include/linux/ssb/ssb.h      |  31 ++++++++++++++
 include/linux/ssb/ssb_regs.h |  44 +++++++++++++++++++
 3 files changed, 174 insertions(+), 1 deletion(-)

diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index e668ad7963fc..514c8149dbe7 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -474,6 +474,101 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
 	     SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
 }
 
+static void bcma_sprom_unpack_rxgains(struct ssb_sprom_rxgains *out,
+				      int chain, u8 packed)
+{
+	out->trelnabyp[chain] = (packed >> 7) & 0x01;
+	out->triso[chain]     = (packed >> 3) & 0x0f;
+	out->elnagain[chain]  =  packed       & 0x07;
+}
+
+static void bcma_sprom_extract_r11(struct bcma_bus *bus, const u16 *sprom)
+{
+	static const u16 pwr_info_offset[] = {
+		SSB_SPROM11_PWR_INFO_CORE0,
+		SSB_SPROM11_PWR_INFO_CORE1,
+		SSB_SPROM11_PWR_INFO_CORE2,
+	};
+	u16 v;
+	int i, j;
+
+	BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) >
+		     ARRAY_SIZE(bus->sprom.core_pwr_info));
+
+	/* Header: rev-11 offsets, rev-8 masks/shifts. */
+	for (i = 0; i < 3; i++) {
+		v = sprom[SPOFF(SSB_SPROM11_IL0MAC) + i];
+		*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
+	}
+
+	SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
+	SPEX(board_type, SSB_SPROM1_SPID, ~0, 0);
+
+	SPEX(country_code, SSB_SPROM11_CCODE, ~0, 0);
+	SPEX(ant_available_a, SSB_SPROM11_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
+	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
+	SPEX(ant_available_bg, SSB_SPROM11_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
+	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
+	SPEX(txchain, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
+	     SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
+	SPEX(rxchain, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
+	     SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
+	SPEX(antswitch, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
+	     SSB_SPROM8_TXRXC_SWITCH_SHIFT);
+	SPEX(subband5gver, SSB_SPROM11_SUBBAND5GVER, 0x00ff, 0);
+
+	/* Per-chain power info. */
+	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
+		struct ssb_sprom_core_pwr_info *p = &bus->sprom.core_pwr_info[i];
+		u16 base = pwr_info_offset[i];
+
+		p->maxp2ga = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP2GA)] & 0xff;
+
+		for (j = 0; j < 3; j++)
+			p->pa2ga[j] = sprom[SPOFF(base + SSB_SPROM11_PWR_PA2GA) + j];
+
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP5GA)];
+		p->maxp5ga[0] = v & 0xff;
+		p->maxp5ga[1] = (v >> 8) & 0xff;
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP5GA) + 1];
+		p->maxp5ga[2] = v & 0xff;
+		p->maxp5ga[3] = (v >> 8) & 0xff;
+
+		for (j = 0; j < 12; j++)
+			p->pa5ga[j] = sprom[SPOFF(base + SSB_SPROM11_PWR_PA5GA) + j];
+
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_RXGAINS0)];
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gm, i,  v       & 0xff);
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gh, i, (v >> 8) & 0xff);
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_RXGAINS1)];
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_2g,  i,  v       & 0xff);
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gl, i, (v >> 8) & 0xff);
+	}
+
+	for (i = 0; i < 3; i++)
+		bus->sprom.pdoffset40ma[i] =
+			sprom[SPOFF(SSB_SPROM11_PDOFFSET40MA) + i];
+
+	SPEX(cckbw202gpo,           SSB_SPROM11_CCKBW202GPO,           ~0, 0);
+	SPEX(cckbw20ul2gpo,         SSB_SPROM11_CCKBW20UL2GPO,         ~0, 0);
+	SPEX32(mcsbw202gpo,         SSB_SPROM11_MCSBW202GPO,           ~0, 0);
+	SPEX32(mcsbw402gpo,         SSB_SPROM11_MCSBW402GPO,           ~0, 0);
+	SPEX(dot11agofdmhrbw202gpo, SSB_SPROM11_DOT11AGOFDMHRBW202GPO, ~0, 0);
+	SPEX(ofdmlrbw202gpo,        SSB_SPROM11_OFDMLRBW202GPO,        ~0, 0);
+	SPEX32(mcsbw205glpo,        SSB_SPROM11_MCSBW205GLPO,          ~0, 0);
+	SPEX32(mcsbw405glpo,        SSB_SPROM11_MCSBW405GLPO,          ~0, 0);
+	SPEX32(mcsbw805glpo,        SSB_SPROM11_MCSBW805GLPO,          ~0, 0);
+	SPEX32(mcsbw1605glpo,       SSB_SPROM11_MCSBW1605GLPO,         ~0, 0);
+	SPEX32(mcsbw205gmpo,        SSB_SPROM11_MCSBW205GMPO,          ~0, 0);
+	SPEX32(mcsbw405gmpo,        SSB_SPROM11_MCSBW405GMPO,          ~0, 0);
+	SPEX32(mcsbw805gmpo,        SSB_SPROM11_MCSBW805GMPO,          ~0, 0);
+	SPEX32(mcsbw1605gmpo,       SSB_SPROM11_MCSBW1605GMPO,         ~0, 0);
+	SPEX32(mcsbw205ghpo,        SSB_SPROM11_MCSBW205GHPO,          ~0, 0);
+	SPEX32(mcsbw405ghpo,        SSB_SPROM11_MCSBW405GHPO,          ~0, 0);
+	SPEX32(mcsbw805ghpo,        SSB_SPROM11_MCSBW805GHPO,          ~0, 0);
+	SPEX32(mcsbw1605ghpo,       SSB_SPROM11_MCSBW1605GHPO,         ~0, 0);
+}
+
 /*
  * Indicates the presence of external SPROM.
  */
@@ -640,7 +735,10 @@ int bcma_sprom_get(struct bcma_bus *bus)
 		bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
 		err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
 	} else {
-		bcma_sprom_extract_r8(bus, sprom);
+		if (bus->sprom.revision == 11)
+			bcma_sprom_extract_r11(bus, sprom);
+		else
+			bcma_sprom_extract_r8(bus, sprom);
 		kfree(sprom);
 	}
 
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index e1fb11e0f12c..9d4a13c4eca3 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -23,6 +23,19 @@ struct ssb_sprom_core_pwr_info {
 	u8 itssi_2g, itssi_5g;
 	u8 maxpwr_2g, maxpwr_5gl, maxpwr_5g, maxpwr_5gh;
 	u16 pa_2g[4], pa_5gl[4], pa_5g[4], pa_5gh[4];
+
+	/* SROM rev 11 */
+	u8 maxp2ga;
+	u8 maxp5ga[4];
+	u16 pa2ga[3];
+	u16 pa5ga[12];
+};
+
+/* Per-band rx gain context, one value per RF chain. SROM rev 11. */
+struct ssb_sprom_rxgains {
+	u8 elnagain[3];
+	u8 triso[3];
+	u8 trelnabyp[3];
 };
 
 struct ssb_sprom {
@@ -192,6 +205,24 @@ struct ssb_sprom {
 	u16 legofdm40duppo;
 	u8 sar2g;
 	u8 sar5g;
+
+	/* SROM rev 11. Populated only by the rev 11 extractor; zero on
+	 * rev <= 10 boards.
+	 */
+	struct ssb_sprom_rxgains rxgains_2g;
+	struct ssb_sprom_rxgains rxgains_5gl;
+	struct ssb_sprom_rxgains rxgains_5gm;
+	struct ssb_sprom_rxgains rxgains_5gh;
+	u8 subband5gver;
+	u16 dot11agofdmhrbw202gpo;
+	u16 ofdmlrbw202gpo;
+	u32 mcsbw805glpo;
+	u32 mcsbw805gmpo;
+	u32 mcsbw805ghpo;
+	u32 mcsbw1605glpo;
+	u32 mcsbw1605gmpo;
+	u32 mcsbw1605ghpo;
+	u16 pdoffset40ma[3];
 };
 
 /* Information about the PCB the circuitry is soldered on. */
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index 210f46494065..9fedf5dcdccd 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -606,6 +606,50 @@
 #define SSB_SPROM8_BW40PO		0x0196
 #define SSB_SPROM8_BWDUPPO		0x0198
 
+/* SROM revision 11. Only the header fields whose absolute offset moved
+ * are redefined; the bitfield masks/shifts are reused from rev 8.
+ */
+#define SSB_SPROM11_IL0MAC		0x0090	/* 6 byte MAC address */
+#define SSB_SPROM11_CCODE		0x0096	/* 2 byte country code */
+#define SSB_SPROM11_ANTAVAIL		0x00A0
+#define SSB_SPROM11_TXRXC		0x00A8
+#define SSB_SPROM11_PDOFFSET40MA	0x00CA	/* 3 x u16, one per chain */
+#define SSB_SPROM11_SUBBAND5GVER	0x00D6
+
+/* Per-chain power info blocks. Stride 0x28 bytes; field layout given by
+ * SSB_SPROM11_PWR_* below.
+ */
+#define SSB_SPROM11_PWR_INFO_CORE0	0x00D8
+#define SSB_SPROM11_PWR_INFO_CORE1	0x0100
+#define SSB_SPROM11_PWR_INFO_CORE2	0x0128
+
+#define SSB_SPROM11_PWR_MAXP2GA		0x0000	/* u8 in low byte */
+#define SSB_SPROM11_PWR_PA2GA		0x0002	/* 3 x u16 */
+#define SSB_SPROM11_PWR_RXGAINS0	0x0008	/* 5gm (lo) / 5gh (hi) */
+#define SSB_SPROM11_PWR_RXGAINS1	0x000A	/* 2g  (lo) / 5gl (hi) */
+#define SSB_SPROM11_PWR_MAXP5GA		0x000C	/* 2 x u16 -> 4 x u8 */
+#define SSB_SPROM11_PWR_PA5GA		0x0010	/* 12 x u16 */
+
+/* Power-per-rate region. */
+#define SSB_SPROM11_CCKBW202GPO		0x0150
+#define SSB_SPROM11_CCKBW20UL2GPO	0x0152
+#define SSB_SPROM11_MCSBW202GPO		0x0154
+#define SSB_SPROM11_MCSBW402GPO		0x0158
+#define SSB_SPROM11_DOT11AGOFDMHRBW202GPO 0x015C
+#define SSB_SPROM11_OFDMLRBW202GPO	0x015E
+#define SSB_SPROM11_MCSBW205GLPO	0x0160
+#define SSB_SPROM11_MCSBW405GLPO	0x0164
+#define SSB_SPROM11_MCSBW805GLPO	0x0168
+#define SSB_SPROM11_MCSBW1605GLPO	0x016C
+#define SSB_SPROM11_MCSBW205GMPO	0x0170
+#define SSB_SPROM11_MCSBW405GMPO	0x0174
+#define SSB_SPROM11_MCSBW805GMPO	0x0178
+#define SSB_SPROM11_MCSBW1605GMPO	0x017C
+#define SSB_SPROM11_MCSBW205GHPO	0x0180
+#define SSB_SPROM11_MCSBW405GHPO	0x0184
+#define SSB_SPROM11_MCSBW805GHPO	0x0188
+#define SSB_SPROM11_MCSBW1605GHPO	0x018C
+
 /* Values for boardflags_lo read from SPROM */
 #define SSB_BFL_BTCOEXIST		0x0001	/* implements Bluetooth coexistance */
 #define SSB_BFL_PACTRL			0x0002	/* GPIO 9 controlling the PA */

-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/4] bcma: host_pci: add BCM4352 device ID 0x43b3
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

The BCM4352 is an AC PHY. It is missing from bcma_pci_bridge_tbl[],
so bcma-pci-bridge does not bind. Add the ID.

The chip identifies as BCM4352 (chip rev 3, PHY type AC); b43 with AC-PHY
support handles it once bcma reaches the D11 core.

Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/bcma/host_pci.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 3dc2985063f1..f6cf722a3798 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -298,6 +298,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b1) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b3) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) },	/* 0xa8db, BCM43217 (sic!) */
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) },	/* 0xa8dc */

-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v1 1/2] overflow: Allow to sum a few arguments at once
From: David Laight @ 2026-06-17 21:30 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Andy Shevchenko, linux-hardening, linux-kernel, linux-wireless,
	Kees Cook, Gustavo A. R. Silva
In-Reply-To: <1e656f5798a9f2f36daa00aba60d2196b2456335.camel@sipsolutions.net>

On Wed, 17 Jun 2026 14:56:09 +0200
Johannes Berg <johannes@sipsolutions.net> wrote:

> On Wed, 2026-06-17 at 13:12 +0200, Andy Shevchenko wrote:
> > Convert size_add() to take variadic argument, so we can simplify users
> > with using a macro only once.  
> 
> > +#define __size_add3(addend1, addend2, addend3, addend4, ...)			\
> > +	__size_add(__size_add2(addend1,  addend2, addend3), addend4)
> > +#define __size_add4(addend1, addend2, addend3, addend4, addend5, ...)		\
> > +	__size_add(__size_add3(addend1,  addend2, addend3, addend4), addend5)  
> 
> I guess it's not going to really matter, but it would generate fewer
> calls to have something more like
> 
> #define __size_add3(a1, a2, a3, a4) \
> 	size_add(size_add(a1, a2), size_add(a3, a4))
> #define __size_add4(a1, a2, a3, a4, a5) \
> 	size_add(size_add(a1, a2), size_add(a3, a4, a5))
> 
> as a binary tree, rather than only cutting one off every time. Not sure
> that results in hugely different code though - maybe fewer overflow
> checks?

The binary tree stands a chance of executing less slowly because the leaf
adds can be executed in parallel.
Excluding the saturation checks (wtf is it called size_add() not
saturating_add() ?) (a + b) + (c + d) will usually execute faster than
((a + b) + c) + d because the (a + b) and (c + d) can execute at the
same time; unfortunately gcc will always generate the latter.

	David

> 
> Although your version make it really completely equivalent to the
> nl80211.c code, clearly it doesn't matter if all the values are "good",
> and I believe the overflow behaviour means it doesn't matter for the
> overflow case either?
> 
> johannes
> 


^ permalink raw reply

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Manivannan Sadhasivam @ 2026-06-17 16:55 UTC (permalink / raw)
  To: Jose Ignacio Tornos Martinez
  Cc: alex, ath11k, ath12k, bhelgaas, jjohnson, linux-kernel, linux-pci,
	linux-wireless, mhi
In-Reply-To: <20260617154709.186286-1-jtornosm@redhat.com>

On Wed, Jun 17, 2026 at 05:47:04PM +0200, Jose Ignacio Tornos Martinez wrote:
> Hi Mani,
> 
> Thank you for the internal clarification and sharing this information.
> 
> I understand the behavior is firmware error recovery, not a proper reset.
> However, these devices are widely used, and the inability to use them in VMs
> is a significant problem. Could we explore options to achieve safe VFIO
> operation?
> 
>   1. Are there ANY alternative reset mechanisms besides D3cold? For example:
>      - Device-specific registers or commands?
>      - MHI bus-level operations?
>      - Firmware commands that could trigger proper reset?
> 
>      If such mechanisms exist, I'm willing to implement whatever is needed.
> 
>   2. If firmware error recovery is the only option available on platforms
>      without _PR3, could we add software steps to make it VFIO-safe?
>      For example, before/after the D3hot transition:
>      - Explicit MHI state teardown?
>      - Firmware commands to clear sensitive device state?
>      - Additional verification or cleanup steps?
> 
>   3. The practical challenge is that _PR3 support is not available on most
>      platforms where these devices need to be deployed (desktops, servers).
>      Additionally, the general d3cold reset method has limitations and
>      remains unimplemented due to the concerns raised earlier (ACPI
>      portability, bridge issues, runtime PM complications).
> 
>      If D3cold is the only proper reset but requires _PR3, and no alternative
>      mechanisms exist, could we consider accepting the firmware error recovery
>      behavior as a last resort - clearly documented as a platform-specific
>      workaround?
> 
>      Currently these devices have no reset capability on most platforms,
>      making them completely unusable for VFIO. Even an imperfect reset is
>      significantly better than no reset at all.
> 
> My goal is ensuring these devices can be safely reassigned between VMs.
> I'm open to implementing any of the above approaches - or others you might
> suggest.
> 

Can you share the exact steps that you tried for passthrough? I'm curious to see
whether you unbinded the MHI host/WLAN driver from the device or not. For the
modem devices, the MHI Host driver's (drivers/bus/mhi/host/pci_generic.c) remove
callback should've quiesced the device and moved the MHI state to RESET if the
driver was unbinded before binding the device with vfio-pci.

I certainly feel that the MHI/WLAN driver should be able to reset the device
during unbind. But I'm not sure if that reset will affect only the firmware
state or the device's config state also. This is something I need to
investigate.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Jose Ignacio Tornos Martinez @ 2026-06-17 15:47 UTC (permalink / raw)
  To: mani
  Cc: alex, ath11k, ath12k, bhelgaas, jjohnson, jtornosm, linux-kernel,
	linux-pci, linux-wireless, mhi
In-Reply-To: <6nivb5fncfd5dwqkzlxwhtgbsiqvifazcbgpsgukp44iib45ke@65qpwgrvtkgn>

Hi Mani,

Thank you for the internal clarification and sharing this information.

I understand the behavior is firmware error recovery, not a proper reset.
However, these devices are widely used, and the inability to use them in VMs
is a significant problem. Could we explore options to achieve safe VFIO
operation?

  1. Are there ANY alternative reset mechanisms besides D3cold? For example:
     - Device-specific registers or commands?
     - MHI bus-level operations?
     - Firmware commands that could trigger proper reset?

     If such mechanisms exist, I'm willing to implement whatever is needed.

  2. If firmware error recovery is the only option available on platforms
     without _PR3, could we add software steps to make it VFIO-safe?
     For example, before/after the D3hot transition:
     - Explicit MHI state teardown?
     - Firmware commands to clear sensitive device state?
     - Additional verification or cleanup steps?

  3. The practical challenge is that _PR3 support is not available on most
     platforms where these devices need to be deployed (desktops, servers).
     Additionally, the general d3cold reset method has limitations and
     remains unimplemented due to the concerns raised earlier (ACPI
     portability, bridge issues, runtime PM complications).

     If D3cold is the only proper reset but requires _PR3, and no alternative
     mechanisms exist, could we consider accepting the firmware error recovery
     behavior as a last resort - clearly documented as a platform-specific
     workaround?

     Currently these devices have no reset capability on most platforms,
     making them completely unusable for VFIO. Even an imperfect reset is
     significantly better than no reset at all.

My goal is ensuring these devices can be safely reassigned between VMs.
I'm open to implementing any of the above approaches - or others you might
suggest.

Thank you

Best regards
José Ignacio


^ permalink raw reply

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Manivannan Sadhasivam @ 2026-06-17 14:47 UTC (permalink / raw)
  To: Jose Ignacio Tornos Martinez
  Cc: bhelgaas, alex, jjohnson, linux-pci, linux-wireless, ath11k,
	ath12k, mhi, linux-kernel
In-Reply-To: <20260612142638.1243895-1-jtornosm@redhat.com>

On Fri, Jun 12, 2026 at 04:26:38PM +0200, Jose Ignacio Tornos Martinez wrote:
> Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
> lack working reset methods for VFIO passthrough scenarios. These devices
> have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
> broken bus reset.
> 
> The problem manifests in VFIO passthrough scenarios:
> 
> - WCN6855 WiFi card (17cb:1103): Normal VM operation works fine, including
>   clean shutdown/reboot. However, when the VM terminates uncleanly
>   (crash, force-off), VFIO attempts to reset the device before it can
>   be assigned to another VM. Without a working reset method, the device
>   remains in an undefined state, preventing reuse.
> 
> - WCN7850 WiFi card (17cb:1107): Same behavior as WCN6855.
> 
> - SDX62/SDX65 5G modems (17cb:0308): Never successfully initialize even
>   on first VM assignment without proper reset capability.
> 
> Add device-specific reset entries for these Qualcomm devices using D3hot
> power cycling. Testing shows that despite advertising NoSoftRst+, D3hot
> transition provides sufficient reset for VFIO reuse, particularly after
> unexpected VM termination. While not a complete reset (BARs preserved),
> it provides the only viable reset mechanism for these devices.
> 

I checked internally within Qcom and I was told that these PCIe devices retain
the context during D3Hot to D0 transition and that's why they advertise
No_Soft_Reset.

The partial reset behavior you are seeing might be due to firmware handling the
transition as an error state. All these devices use MHI bus and when the MHI
state doesn't match the PCIe device state, then the firmware will treat it as an
error and will try to torn down resources. This could be the reason why you are
seeing partial reset.

Nevertheless, these devices do not support any form of Soft Reset and only way
to reset them would be by doing D3Cold. But that depends on platform support
though. So it would be inaccurate/wrong to assume that these devices support
Soft Reset during D3Hot to D0 transition.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply

* Re: [PATCH v1 1/2] overflow: Allow to sum a few arguments at once
From: Johannes Berg @ 2026-06-17 12:56 UTC (permalink / raw)
  To: Andy Shevchenko, linux-hardening, linux-kernel, linux-wireless
  Cc: Kees Cook, Gustavo A. R. Silva
In-Reply-To: <20260617112250.2791461-2-andriy.shevchenko@linux.intel.com>

On Wed, 2026-06-17 at 13:12 +0200, Andy Shevchenko wrote:
> Convert size_add() to take variadic argument, so we can simplify users
> with using a macro only once.

> +#define __size_add3(addend1, addend2, addend3, addend4, ...)			\
> +	__size_add(__size_add2(addend1,  addend2, addend3), addend4)
> +#define __size_add4(addend1, addend2, addend3, addend4, addend5, ...)		\
> +	__size_add(__size_add3(addend1,  addend2, addend3, addend4), addend5)

I guess it's not going to really matter, but it would generate fewer
calls to have something more like

#define __size_add3(a1, a2, a3, a4) \
	size_add(size_add(a1, a2), size_add(a3, a4))
#define __size_add4(a1, a2, a3, a4, a5) \
	size_add(size_add(a1, a2), size_add(a3, a4, a5))

as a binary tree, rather than only cutting one off every time. Not sure
that results in hugely different code though - maybe fewer overflow
checks?

Although your version make it really completely equivalent to the
nl80211.c code, clearly it doesn't matter if all the values are "good",
and I believe the overflow behaviour means it doesn't matter for the
overflow case either?

johannes

^ permalink raw reply

* [rfc, PATCH v1 0/2] overflow: Convert size_add() to take variadic arguments
From: Andy Shevchenko @ 2026-06-17 11:12 UTC (permalink / raw)
  To: Johannes Berg, linux-hardening, linux-kernel, linux-wireless
  Cc: Kees Cook, Gustavo A. R. Silva, Johannes Berg, Andy Shevchenko

This is an RFC!

We have already users that want add sizes of up to 5 arguments and
I know about at least one that also wants 3 or 4.

This is brave move to make size_add() to take variadic arguments.
The second patch is an example of use.

The implementation includes a case with a single argument on a purpose.
In the future it might be extended to take an array as an argument,
something like

	int sizes[21];
	size_add(sizes);

where the first element is amount of entries in the array (the same format
as used in get_options() call) or other possible variants. This can be
distinguished by _Generic().

But it may be dropped and we require always two arguments at minimum.

The RFC just to collect opinions and perception. Note, array3*(), min3()/max3()
and all like that also can use similar approach.

Andy Shevchenko (2):
  overflow: Allow to sum a few arguments at once
  wifi: nl80211: Call size_add() only once

 include/linux/overflow.h | 37 ++++++++++++++++++++++++++-----------
 net/wireless/nl80211.c   | 11 ++++-------
 2 files changed, 30 insertions(+), 18 deletions(-)

-- 
2.50.1


^ permalink raw reply

* [PATCH v1 1/2] overflow: Allow to sum a few arguments at once
From: Andy Shevchenko @ 2026-06-17 11:12 UTC (permalink / raw)
  To: Johannes Berg, linux-hardening, linux-kernel, linux-wireless
  Cc: Kees Cook, Gustavo A. R. Silva, Johannes Berg, Andy Shevchenko
In-Reply-To: <20260617112250.2791461-1-andriy.shevchenko@linux.intel.com>

Convert size_add() to take variadic argument, so we can simplify users
with using a macro only once.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 include/linux/overflow.h | 37 ++++++++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/include/linux/overflow.h b/include/linux/overflow.h
index a8cb6319b4fb..a8b0325e73f3 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.h
@@ -2,9 +2,10 @@
 #ifndef __LINUX_OVERFLOW_H
 #define __LINUX_OVERFLOW_H
 
+#include <linux/args.h>
 #include <linux/compiler.h>
-#include <linux/limits.h>
 #include <linux/const.h>
+#include <linux/limits.h>
 
 /*
  * We need to compute the minimum and maximum values representable in a given
@@ -337,16 +338,7 @@ static __always_inline size_t __must_check size_mul(size_t factor1, size_t facto
 	return bytes;
 }
 
-/**
- * size_add() - Calculate size_t addition with saturation at SIZE_MAX
- * @addend1: first addend
- * @addend2: second addend
- *
- * Returns: calculate @addend1 + @addend2, both promoted to size_t,
- * with any overflow causing the return value to be SIZE_MAX. The
- * lvalue must be size_t to avoid implicit type conversion.
- */
-static __always_inline size_t __must_check size_add(size_t addend1, size_t addend2)
+static __always_inline size_t __must_check __size_add(size_t addend1, size_t addend2)
 {
 	size_t bytes;
 
@@ -356,6 +348,29 @@ static __always_inline size_t __must_check size_add(size_t addend1, size_t adden
 	return bytes;
 }
 
+#define __size_add0(addend1, ...)						\
+	__size_add(addend1, 0)
+#define __size_add1(addend1, addend2, ...)					\
+	__size_add(addend1,  addend2)
+#define __size_add2(addend1, addend2, addend3, ...)				\
+	__size_add(__size_add(addend1,  addend2), addend3)
+#define __size_add3(addend1, addend2, addend3, addend4, ...)			\
+	__size_add(__size_add2(addend1,  addend2, addend3), addend4)
+#define __size_add4(addend1, addend2, addend3, addend4, addend5, ...)		\
+	__size_add(__size_add3(addend1,  addend2, addend3, addend4), addend5)
+
+/**
+ * size_add() - Calculate size_t addition with saturation at SIZE_MAX
+ * @addend1: first addend
+ * @...: more to add (optional)
+ *
+ * Returns: calculate @addend1 + @addend2, both promoted to size_t,
+ * with any overflow causing the return value to be SIZE_MAX. The
+ * lvalue must be size_t to avoid implicit type conversion.
+ */
+#define size_add(addend1, ...)	\
+	CONCATENATE(__size_add, COUNT_ARGS(__VA_ARGS__))(addend1, __VA_ARGS__)
+
 /**
  * size_sub() - Calculate size_t subtraction with saturation at SIZE_MAX
  * @minuend: value to subtract from
-- 
2.50.1


^ permalink raw reply related

* [PATCH v1 2/2] wifi: nl80211: Call size_add() only once
From: Andy Shevchenko @ 2026-06-17 11:12 UTC (permalink / raw)
  To: Johannes Berg, linux-hardening, linux-kernel, linux-wireless
  Cc: Kees Cook, Gustavo A. R. Silva, Johannes Berg, Andy Shevchenko
In-Reply-To: <20260617112250.2791461-1-andriy.shevchenko@linux.intel.com>

Since size_add() may take a few arguments at once, call it only once.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 net/wireless/nl80211.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 53b4b3f76697..98f92c268944 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -11560,13 +11560,10 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
 	     attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]))
 		return ERR_PTR(-EINVAL);
 
-	size = struct_size(request, channels, n_channels);
-	size = size_add(size, array_size(sizeof(*request->ssids), n_ssids));
-	size = size_add(size, array_size(sizeof(*request->match_sets),
-					 n_match_sets));
-	size = size_add(size, array_size(sizeof(*request->scan_plans),
-					 n_plans));
-	size = size_add(size, ie_len);
+	size = size_add(struct_size(request, channels, n_channels), ie_len,
+			array_size(sizeof(*request->ssids), n_ssids),
+			array_size(sizeof(*request->match_sets), n_match_sets),
+			array_size(sizeof(*request->scan_plans), n_plans));
 	request = kzalloc(size, GFP_KERNEL);
 	if (!request)
 		return ERR_PTR(-ENOMEM);
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH 7/7] arm64: dts: qcom: sm8350-hdk: describe WiFi/BT chip
From: Konrad Dybcio @ 2026-06-17 10:46 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Qiang Yu,
	Jeff Johnson, Liam Girdwood, Mark Brown, Krzysztof Kozlowski,
	Conor Dooley, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-pci,
	linux-kernel, linux-wireless, ath11k, devicetree,
	Bartosz Golaszewski, linux-bluetooth
In-Reply-To: <dz4qovpquxgynnzviqg6g23oa2o3trlkznremnmlqfnlj3b62x@oqgcuvalfcph>

On 6/11/26 9:44 PM, Dmitry Baryshkov wrote:
> On Thu, Jun 11, 2026 at 02:09:27PM +0200, Konrad Dybcio wrote:
>> On 6/1/26 11:46 AM, Dmitry Baryshkov wrote:
>>> The SM8350 HDK has onboard WiFi/BT chip, WCN6851. It is an earlier
>>> version of well-known WCN6855 WiFI/BT SoC. Describe the PMU, BT and WiFI
>>> parts of the device.
>>
>> [...]
>>
>>> +	wcn6855-pmu {
>>> +		compatible = "qcom,wcn6851-pmu", "qcom,wcn6855-pmu";
>>> +
>>> +		pinctrl-0 = <&bt_en>, <&wlan_en>, <&swctrl>;
>>> +		pinctrl-names = "default";
>>> +
>>> +		wlan-enable-gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>;
>>> +		bt-enable-gpios = <&tlmm 65 GPIO_ACTIVE_HIGH>;
>>> +		swctrl-gpios = <&tlmm 153 GPIO_ACTIVE_HIGH>;
>>> +
>>> +		vddio-supply = <&vreg_s10b_1p8>;
>>> +		vddaon-supply = <&vreg_s11b_0p95>;
>>> +		vddpmu-supply = <&vreg_s11b_0p95>;
>>> +		vddpmumx-supply = <&vreg_s2e_0p85>;
>>> +		vddpmucx-supply = <&vreg_s11b_0p95>;
>>> +		vddrfa0p95-supply = <&vreg_s11b_0p95>;
>>> +		vddrfa1p3-supply = <&vreg_s12b_1p25>;
>>> +		vddrfa1p9-supply = <&vreg_s1c_1p86>;
>>> +		vddpcie1p3-supply = <&vreg_s12b_1p25>;
>>> +		vddpcie1p9-supply = <&vreg_s1c_1p86>;
>>
>> [...]
>>
>>> @@ -373,6 +437,13 @@ vreg_l7e_2p8: ldo7 {
>>>  			regulator-name = "vreg_l7e_2p8";
>>>  			regulator-min-microvolt = <2800000>;
>>>  			regulator-max-microvolt = <2800000>;
>>> +
>>> +			/*
>>> +			 * This is used by the RF front-end for which there is
>>> +			 * no way to represent it in DT (yet?).
>>> +			 */
>>> +			regulator-boot-on;
>>> +			regulator-always-on;
>>
>> msm-5.4 maps this to bt-vdd-asd-supply (asd being a keyboard smash,
>> perhaps?) - what is its actual use?
> 
> WiFI/BT RF front-end, a separate chip containing amplifiers, couplers,
> etc.
> 
> It is a separate chip (or a module), it is not powered on by the PMU,
> etc.

Ah, that thing.. yeah I don't really know. Although it'd be a shame to
keep it powered forever, so maybe one day someone will revisit this

Konrad

^ permalink raw reply

* [PATCH] wifi: ath12k: fix survey indexing across bands
From: Matthew Leach @ 2026-06-17 10:18 UTC (permalink / raw)
  To: Jeff Johnson, Vasanthakumar Thiagarajan, Baochen Qiang,
	Ramya Gnanasekar, Karthikeyan Periyasamy
  Cc: Kalle Valo, Pradeep Kumar Chitrapu, P Praneesh, Sriram R,
	linux-wireless, ath12k, linux-kernel, kernel, Matthew Leach

When running 'iw dev wlan0 survey dump' the values for the channel busy
time have the same sequence across bands. This is caused by indexing
into the ath12k survey array using a band-local index rather than the
global index passed by mac80211. This results in surveys for 5 GHz and 6
GHz channels returning values from 2.4 GHz slots, making the survey
unusable on those bands.

Fix by indexing into ar->survey[] using the original index passed by
mac80211.

Band busy-times Before this fix:

2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
5 GHz:   9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
6 GHz:   9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5

After this fix, times are independent:

2.4 GHz: 23, 5,  5,  12, 2,   12,  26,  5,   3,  1,  27
5 GHz:   30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
6 GHz:   1,  0,  0,  0,  0,   0,   0,   0,   0,  0,  1

Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1

Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
---
 drivers/net/wireless/ath/ath12k/mac.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 2cff9485c95a..9474d7e70823 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -13351,6 +13351,7 @@ int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx,
 	struct ath12k *ar;
 	struct ieee80211_supported_band *sband;
 	struct survey_info *ar_survey;
+	int survey_index = idx;
 
 	lockdep_assert_wiphy(hw->wiphy);
 
@@ -13385,7 +13386,7 @@ int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx,
 		return -ENOENT;
 	}
 
-	ar_survey = &ar->survey[idx];
+	ar_survey = &ar->survey[survey_index];
 
 	ath12k_mac_update_bss_chan_survey(ar, &sband->channels[idx]);
 

---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260617-ath12-survey-band-fix-4b5e78579379

Best regards,
--  
Matt


^ permalink raw reply related

* [PATCH] wifi: rtw89: regd: fix ww domain blocking on 6GHz
From: Matthew Leach @ 2026-06-17 10:13 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, kernel, Matthew Leach

The current code keeps a separate instance for the ww reg domain outside
the static map, and treats rtw89_regd_get_index() returning
RTW89_REGD_MAX_COUNTRY_NUM as the index for the ww domain. This
conflates the "not found" sentinel with a real index and causes the
block_* bitmap lookups to skip ww entirely, explicitly blocking 6GHz on
the ww domain rather than deferring to the kernel regulatory policy.

Fold the standalone rtw89_ww_regd into rtw89_regd_map[0] and drop the
special case in rtw89_regd_get_index() that returned
RTW89_REGD_MAX_COUNTRY_NUM for ww. rtw89_regd_find_reg_by_name() now
returns NULL on miss, and its callers either translate that into the
RTW89_REGD_MAX_COUNTRY_NUM sentinel (rtw89_regd_get_index_by_name()) or
fall back to the ww entry explicitly (rtw89_regd_notifier_apply()). With
ww living at a real index, the block-list checks apply and 6GHz is no
longer unconditionally blocked for ww.

Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
---
 drivers/net/wireless/realtek/rtw89/regd.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c
index 28466cb35ea2..698b8b7f6129 100644
--- a/drivers/net/wireless/realtek/rtw89/regd.c
+++ b/drivers/net/wireless/realtek/rtw89/regd.c
@@ -21,10 +21,8 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request
 
 static_assert(BITS_PER_TYPE(unsigned long) >= NUM_OF_RTW89_REGD_FUNC);
 
-static const struct rtw89_regd rtw89_ww_regd =
-	COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0);
-
 static const struct rtw89_regd rtw89_regd_map[] = {
+	COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0),
 	COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0),
 	COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
 	COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
@@ -316,12 +314,13 @@ static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rt
 			return &regd_ctrl->map[i];
 	}
 
-	return &rtw89_ww_regd;
+	return NULL;
 }
 
 static bool rtw89_regd_is_ww(const struct rtw89_regd *regd)
 {
-	return regd == &rtw89_ww_regd;
+	/* Index 0 in the static map contains the WW domain entry. */
+	return regd == &rtw89_regd_map[0];
 }
 
 static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd)
@@ -331,9 +330,6 @@ static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd
 
 	BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM);
 
-	if (rtw89_regd_is_ww(regd))
-		return RTW89_REGD_MAX_COUNTRY_NUM;
-
 	return regd - regd_ctrl->map;
 }
 
@@ -342,6 +338,10 @@ static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alp
 	const struct rtw89_regd *regd;
 
 	regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2);
+
+	if (!regd)
+		return RTW89_REGD_MAX_COUNTRY_NUM;
+
 	return rtw89_regd_get_index(rtwdev, regd);
 }
 
@@ -721,7 +721,7 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev)
 		return -EINVAL;
 
 	chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code);
-	if (!rtw89_regd_is_ww(chip_regd)) {
+	if (chip_regd && !rtw89_regd_is_ww(chip_regd)) {
 		rtwdev->regulatory.regd = chip_regd;
 		rtwdev->regulatory.programmed = true;
 
@@ -859,7 +859,15 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
 				      struct wiphy *wiphy,
 				      struct regulatory_request *request)
 {
-	rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);
+	const struct rtw89_regd *regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);
+
+	if (!regd) {
+		/* Fallback to WW domain if name not found. */
+		regd = &rtw89_regd_map[0];
+	}
+
+	rtwdev->regulatory.regd = regd;
+
 	/* This notification might be set from the system of distros,
 	 * and it does not expect the regulatory will be modified by
 	 * connecting to an AP (i.e. country ie).

---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260616-rtw89-6ghz-regd-fixes-d8f816880bd0

Best regards,
--  
Matt


^ permalink raw reply related

* [PATCH ath-next 2/2] wifi: ath12k: fix MLO peer delete race
From: Baochen Qiang @ 2026-06-17  9:28 UTC (permalink / raw)
  To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260617-ath12k-mlo-peer-delete-race-v1-0-ab3c4f455dfb@oss.qualcomm.com>

ath12k_peer_mlo_link_peers_delete() sends WMI peer_delete for every
link before waiting for any peer_unmap / peer_delete_resp event. The
shared per-radio completion ar->peer_delete_done could not
disambiguate which peer a response was for: every call to
ath12k_peer_delete_send() did
reinit_completion(&ar->peer_delete_done), so when an event for the
first link arrived between two sends it raised the count to 1 and
the second send promptly cleared it; the wait for the second link
then timed out with

    Timeout in receiving peer delete response

Replace the shared completion with a per-radio waiter list, with
each pending ath12k_peer_delete() caller queueing an
ath12k_peer_delete_wait carrying its (vdev_id, addr) and a private
struct completion. ath12k_peer_delete_resp_event() matches the
response against the list under ar->data_lock and signals the
matching waiter.

Also correct the endian conversion in ath12k_peer_delete_resp_event()
logging, and add the missing \n in some logging.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3

Fixes: 8e6f8bc28603 ("wifi: ath12k: Add MLO station state change handling")
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath12k/core.c |   2 +-
 drivers/net/wireless/ath/ath12k/core.h |   5 +-
 drivers/net/wireless/ath/ath12k/mac.c  |   2 +-
 drivers/net/wireless/ath/ath12k/peer.c | 130 ++++++++++++++++++++++++++-------
 drivers/net/wireless/ath/ath12k/peer.h |  19 ++++-
 drivers/net/wireless/ath/ath12k/wmi.c  |  16 ++--
 6 files changed, 136 insertions(+), 38 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 742d4fd1b598..f71650039292 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1524,7 +1524,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
 			complete_all(&ar->scan.completed);
 			complete(&ar->scan.on_channel);
 			complete(&ar->peer_assoc_done);
-			complete(&ar->peer_delete_done);
+			ath12k_peer_delete_wait_flush(ar);
 			complete(&ar->install_key_done);
 			complete(&ar->vdev_setup_done);
 			complete(&ar->vdev_delete_done);
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index fc5127b5c1a3..1436ff4316e7 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -665,7 +665,8 @@ struct ath12k {
 
 	/* protects the radio specific data like debug stats, ppdu_stats_info stats,
 	 * vdev_stop_status info, scan data, ath12k_sta info, ath12k_link_vif info,
-	 * channel context data, survey info, test mode data, regd_channel_update_queue.
+	 * channel context data, survey info, test mode data, regd_channel_update_queue,
+	 * peer_delete_waits.
 	 */
 	spinlock_t data_lock;
 
@@ -687,7 +688,7 @@ struct ath12k {
 	u8 radio_idx;
 
 	struct completion peer_assoc_done;
-	struct completion peer_delete_done;
+	struct list_head peer_delete_waits;
 
 	int install_key_status;
 	struct completion install_key_done;
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 2e5a075191ae..4c86a8eb5841 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -15040,11 +15040,11 @@ static void ath12k_mac_setup(struct ath12k *ar)
 	spin_lock_init(&ar->dp.ppdu_list_lock);
 	INIT_LIST_HEAD(&ar->arvifs);
 	INIT_LIST_HEAD(&ar->dp.ppdu_stats_info);
+	INIT_LIST_HEAD(&ar->peer_delete_waits);
 
 	init_completion(&ar->vdev_setup_done);
 	init_completion(&ar->vdev_delete_done);
 	init_completion(&ar->peer_assoc_done);
-	init_completion(&ar->peer_delete_done);
 	init_completion(&ar->install_key_done);
 	init_completion(&ar->bss_survey_done);
 	init_completion(&ar->scan.started);
diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c
index c222bdaa333c..98509c63c580 100644
--- a/drivers/net/wireless/ath/ath12k/peer.c
+++ b/drivers/net/wireless/ath/ath12k/peer.c
@@ -9,6 +9,55 @@
 #include "debug.h"
 #include "debugfs.h"
 
+void ath12k_peer_delete_wait_register(struct ath12k *ar,
+				      struct ath12k_peer_delete_wait *wait,
+				      u32 vdev_id, const u8 *addr)
+{
+	wait->vdev_id = vdev_id;
+	ether_addr_copy(wait->addr, addr);
+	init_completion(&wait->done);
+
+	spin_lock_bh(&ar->data_lock);
+	list_add(&wait->list, &ar->peer_delete_waits);
+	spin_unlock_bh(&ar->data_lock);
+}
+
+void ath12k_peer_delete_wait_unregister(struct ath12k *ar,
+					struct ath12k_peer_delete_wait *wait)
+{
+	spin_lock_bh(&ar->data_lock);
+	list_del(&wait->list);
+	spin_unlock_bh(&ar->data_lock);
+}
+
+void ath12k_peer_delete_resp_signal(struct ath12k *ar, u32 vdev_id, const u8 *addr)
+{
+	struct ath12k_peer_delete_wait *wait;
+
+	guard(spinlock_bh)(&ar->data_lock);
+
+	list_for_each_entry(wait, &ar->peer_delete_waits, list) {
+		if (wait->vdev_id == vdev_id &&
+		    ether_addr_equal(wait->addr, addr)) {
+			complete(&wait->done);
+			return;
+		}
+	}
+
+	ath12k_warn(ar->ab, "failed to find link peer with vdev id %u addr %pM\n",
+		    vdev_id, addr);
+}
+
+void ath12k_peer_delete_wait_flush(struct ath12k *ar)
+{
+	struct ath12k_peer_delete_wait *wait;
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry(wait, &ar->peer_delete_waits, list)
+		complete(&wait->done);
+	spin_unlock_bh(&ar->data_lock);
+}
+
 static int ath12k_wait_for_dp_link_peer_common(struct ath12k_base *ab, int vdev_id,
 					       const u8 *addr, bool expect_mapped)
 {
@@ -62,20 +111,19 @@ static int ath12k_wait_for_peer_deleted(struct ath12k *ar, int vdev_id, const u8
 	return ath12k_wait_for_dp_link_peer_common(ar->ab, vdev_id, addr, false);
 }
 
-int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id,
-				     const u8 *addr)
+int ath12k_wait_for_peer_delete_done(struct ath12k *ar,
+				     struct ath12k_peer_delete_wait *wait)
 {
-	int ret;
 	unsigned long time_left;
+	int ret;
 
-	ret = ath12k_wait_for_peer_deleted(ar, vdev_id, addr);
+	ret = ath12k_wait_for_peer_deleted(ar, wait->vdev_id, wait->addr);
 	if (ret) {
-		ath12k_warn(ar->ab, "failed wait for peer deleted");
+		ath12k_warn(ar->ab, "failed wait for peer deleted\n");
 		return ret;
 	}
 
-	time_left = wait_for_completion_timeout(&ar->peer_delete_done,
-						3 * HZ);
+	time_left = wait_for_completion_timeout(&wait->done, 3 * HZ);
 	if (time_left == 0) {
 		ath12k_warn(ar->ab, "Timeout in receiving peer delete response\n");
 		return -ETIMEDOUT;
@@ -91,8 +139,6 @@ static int ath12k_peer_delete_send(struct ath12k *ar, u32 vdev_id, const u8 *add
 
 	lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
 
-	reinit_completion(&ar->peer_delete_done);
-
 	ret = ath12k_wmi_send_peer_delete_cmd(ar, addr, vdev_id);
 	if (ret) {
 		ath12k_warn(ab,
@@ -106,6 +152,7 @@ static int ath12k_peer_delete_send(struct ath12k *ar, u32 vdev_id, const u8 *add
 
 int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr)
 {
+	struct ath12k_peer_delete_wait wait;
 	int ret;
 
 	lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -114,17 +161,25 @@ int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr)
 				     &(ath12k_ar_to_ah(ar)->dp_hw), vdev_id,
 				     addr, ar->hw_link_id);
 
+	/*
+	 * Register the stack waiter before sending so the resp_event for
+	 * this peer cannot arrive while no waiter is queued.
+	 */
+	ath12k_peer_delete_wait_register(ar, &wait, vdev_id, addr);
+
 	ret = ath12k_peer_delete_send(ar, vdev_id, addr);
 	if (ret)
-		return ret;
+		goto out;
 
-	ret = ath12k_wait_for_peer_delete_done(ar, vdev_id, addr);
+	ret = ath12k_wait_for_peer_delete_done(ar, &wait);
 	if (ret)
-		return ret;
+		goto out;
 
 	ar->num_peers--;
 
-	return 0;
+out:
+	ath12k_peer_delete_wait_unregister(ar, &wait);
+	return ret;
 }
 
 static int ath12k_wait_for_peer_created(struct ath12k *ar, int vdev_id, const u8 *addr)
@@ -184,22 +239,26 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
 	peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arg->vdev_id,
 							 arg->peer_addr);
 	if (!peer) {
+		struct ath12k_peer_delete_wait wait;
+
 		spin_unlock_bh(&dp->dp_lock);
 		ath12k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n",
 			    arg->peer_addr, arg->vdev_id);
 
-		reinit_completion(&ar->peer_delete_done);
+		ath12k_peer_delete_wait_register(ar, &wait, arg->vdev_id,
+						 arg->peer_addr);
 
 		ret = ath12k_wmi_send_peer_delete_cmd(ar, arg->peer_addr,
 						      arg->vdev_id);
 		if (ret) {
 			ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n",
 				    arg->vdev_id, arg->peer_addr);
+			ath12k_peer_delete_wait_unregister(ar, &wait);
 			return ret;
 		}
 
-		ret = ath12k_wait_for_peer_delete_done(ar, arg->vdev_id,
-						       arg->peer_addr);
+		ret = ath12k_wait_for_peer_delete_done(ar, &wait);
+		ath12k_peer_delete_wait_unregister(ar, &wait);
 		if (ret)
 			return ret;
 
@@ -283,13 +342,14 @@ u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah)
 
 int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta)
 {
+	DECLARE_BITMAP(registered, IEEE80211_MLD_MAX_NUM_LINKS);
 	struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta);
 	struct ath12k_hw *ah = ahvif->ah;
 	struct ath12k_link_vif *arvif;
 	struct ath12k_link_sta *arsta;
+	int ret, err_ret = 0;
 	unsigned long links;
 	struct ath12k *ar;
-	int ret, err_ret = 0;
 	u8 link_id;
 
 	lockdep_assert_wiphy(ah->hw->wiphy);
@@ -297,8 +357,19 @@ int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_st
 	if (!sta->mlo)
 		return -EINVAL;
 
-	/* FW expects delete of all link peers at once before waiting for reception
-	 * of peer unmap or delete responses
+	struct ath12k_peer_delete_wait *waits __free(kfree) =
+				kzalloc_objs(*waits, IEEE80211_MLD_MAX_NUM_LINKS);
+	if (!waits)
+		return -ENOMEM;
+
+	bitmap_zero(registered, IEEE80211_MLD_MAX_NUM_LINKS);
+
+	/*
+	 * Firmware expects delete of all link peers at once before waiting
+	 * for reception of peer unmap or delete responses. Phase 1 registers
+	 * a per-link stack waiter and sends WMI peer delete for every
+	 * link; the resp_event handler matches each response to its
+	 * (vdev_id, addr) waiter on ar->peer_delete_waits.
 	 */
 	links = ahsta->links_map;
 	for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
@@ -318,29 +389,36 @@ int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_st
 					     arvif->vdev_id, arsta->addr,
 					     ar->hw_link_id);
 
+		ath12k_peer_delete_wait_register(ar, &waits[link_id],
+						 arvif->vdev_id, arsta->addr);
+
 		ret = ath12k_peer_delete_send(ar, arvif->vdev_id, arsta->addr);
 		if (ret) {
 			ath12k_warn(ar->ab,
 				    "failed to delete peer vdev_id %d addr %pM ret %d\n",
 				    arvif->vdev_id, arsta->addr, ret);
 			err_ret = ret;
+			ath12k_peer_delete_wait_unregister(ar, &waits[link_id]);
 			continue;
 		}
+
+		set_bit(link_id, registered);
 	}
 
-	/* Ensure all link peers are deleted and unmapped */
+	/*
+	 * Phase 2: wait for unmap + delete_resp on each registered link
+	 * and tear down the waiter.
+	 */
 	links = ahsta->links_map;
 	for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
-		arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]);
-		arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]);
-		if (!arvif || !arsta)
+		if (!test_bit(link_id, registered))
 			continue;
 
+		arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]);
 		ar = arvif->ar;
-		if (!ar)
-			continue;
 
-		ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arsta->addr);
+		ret = ath12k_wait_for_peer_delete_done(ar, &waits[link_id]);
+		ath12k_peer_delete_wait_unregister(ar, &waits[link_id]);
 		if (ret) {
 			err_ret = ret;
 			continue;
diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h
index 49d89796bc46..3dc720a3dc12 100644
--- a/drivers/net/wireless/ath/ath12k/peer.h
+++ b/drivers/net/wireless/ath/ath12k/peer.h
@@ -9,13 +9,28 @@
 
 #include "dp_peer.h"
 
+struct ath12k_peer_delete_wait {
+	struct list_head list;
+	u32 vdev_id;
+	u8 addr[ETH_ALEN];
+	struct completion done;
+};
+
+void ath12k_peer_delete_wait_register(struct ath12k *ar,
+				      struct ath12k_peer_delete_wait *wait,
+				      u32 vdev_id, const u8 *addr);
+void ath12k_peer_delete_wait_unregister(struct ath12k *ar,
+					struct ath12k_peer_delete_wait *wait);
+void ath12k_peer_delete_resp_signal(struct ath12k *ar, u32 vdev_id, const u8 *addr);
+void ath12k_peer_delete_wait_flush(struct ath12k *ar);
+
 void ath12k_peer_cleanup(struct ath12k *ar, u32 vdev_id);
 int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr);
 int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
 		       struct ieee80211_sta *sta,
 		       struct ath12k_wmi_peer_create_arg *arg);
-int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id,
-				     const u8 *addr);
+int ath12k_wait_for_peer_delete_done(struct ath12k *ar,
+				     struct ath12k_peer_delete_wait *wait);
 int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta);
 struct ath12k_ml_peer *ath12k_peer_ml_find(struct ath12k_hw *ah,
 					   const u8 *addr);
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 84a31b953db8..6066ca8d9fc4 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -7072,25 +7072,29 @@ static void ath12k_peer_delete_resp_event(struct ath12k_base *ab, struct sk_buff
 {
 	struct wmi_peer_delete_resp_event peer_del_resp;
 	struct ath12k *ar;
+	u32 vdev_id;
 
 	if (ath12k_pull_peer_del_resp_ev(ab, skb, &peer_del_resp) != 0) {
-		ath12k_warn(ab, "failed to extract peer delete resp");
+		ath12k_warn(ab, "failed to extract peer delete resp\n");
 		return;
 	}
 
+	vdev_id = le32_to_cpu(peer_del_resp.vdev_id);
+
 	rcu_read_lock();
-	ar = ath12k_mac_get_ar_by_vdev_id(ab, le32_to_cpu(peer_del_resp.vdev_id));
+	ar = ath12k_mac_get_ar_by_vdev_id(ab, vdev_id);
 	if (!ar) {
-		ath12k_warn(ab, "invalid vdev id in peer delete resp ev %d",
-			    peer_del_resp.vdev_id);
+		ath12k_warn(ab, "invalid vdev id in peer delete resp ev %d\n",
+			    vdev_id);
 		rcu_read_unlock();
 		return;
 	}
 
-	complete(&ar->peer_delete_done);
+	ath12k_peer_delete_resp_signal(ar, vdev_id,
+				       peer_del_resp.peer_macaddr.addr);
 	rcu_read_unlock();
 	ath12k_dbg(ab, ATH12K_DBG_WMI, "peer delete resp for vdev id %d addr %pM\n",
-		   peer_del_resp.vdev_id, peer_del_resp.peer_macaddr.addr);
+		   vdev_id, peer_del_resp.peer_macaddr.addr);
 }
 
 static void ath12k_vdev_delete_resp_event(struct ath12k_base *ab,

-- 
2.25.1


^ permalink raw reply related

* [PATCH ath-next 1/2] wifi: ath12k: fix dp_link_peer dangling references on AP vdev rollback
From: Baochen Qiang @ 2026-06-17  9:28 UTC (permalink / raw)
  To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260617-ath12k-mlo-peer-delete-race-v1-0-ab3c4f455dfb@oss.qualcomm.com>

ath12k_mac_vdev_create() for an AP vdev creates the bss self-peer via
ath12k_peer_create(), which finishes by calling
ath12k_dp_link_peer_assign() to publish the dp_link_peer in the
dp_hw->dp_peers[peerid_index] RCU table, in the dp_peer's
link_peers[] array, and in the per-addr rhashtable.

If a step after ath12k_peer_create() fails the function jumps to
err_peer_del, which open-codes a WMI peer_delete and waits for the
unmap / delete_resp events. The wait_for_peer_delete_done() path
relies on ath12k_dp_link_peer_unmap_event() freeing the dp_link_peer
when the unmap arrives, but err_peer_del never calls
ath12k_dp_link_peer_unassign() first. The published references in
the dp_hw RCU table, dp_peer->link_peers[] and the rhashtable are
left pointing at the dp_link_peer that unmap_event then frees,
producing dangling pointers and use-after-free on subsequent
lookups.

Replace the open-coded sequence with a call to ath12k_peer_delete(),
which already does ath12k_dp_link_peer_unassign() before sending the
WMI command. This drops the published references before the
dp_link_peer is freed, in the same order as the normal teardown path
in ath12k_mac_remove_link_interface().

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3

Fixes: 5525f12fa671 ("wifi: ath12k: Attach and detach ath12k_dp_link_peer to ath12k_dp_peer")
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath12k/mac.c | 18 ++----------------
 1 file changed, 2 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index af354bef5c0d..2e5a075191ae 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -10564,22 +10564,8 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
 
 err_peer_del:
 	if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
-		reinit_completion(&ar->peer_delete_done);
-
-		ret = ath12k_wmi_send_peer_delete_cmd(ar, arvif->bssid,
-						      arvif->vdev_id);
-		if (ret) {
-			ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n",
-				    arvif->vdev_id, arvif->bssid);
-			goto err_dp_peer_del;
-		}
-
-		ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id,
-						       arvif->bssid);
-		if (ret)
-			goto err_dp_peer_del;
-
-		ar->num_peers--;
+		/* ignore return value: propagate the original error */
+		ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid);
 	}
 
 err_dp_peer_del:

-- 
2.25.1


^ permalink raw reply related

* [PATCH ath-next 0/2] wifi: ath12k: fix peer delete race in MLO scenario
From: Baochen Qiang @ 2026-06-17  9:28 UTC (permalink / raw)
  To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang

Patch 1 fixes a pre-existing UAF in ath12k_mac_vdev_create()'s
err_peer_del rollback path.

Patch 2 fixes "Timeout in receiving peer delete response" on MLO
disconnect, caused by a per-radio shared completion that gets
clobbered between back-to-back WMI peer_delete sends.

Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
Baochen Qiang (2):
      wifi: ath12k: fix dp_link_peer dangling references on AP vdev rollback
      wifi: ath12k: fix MLO peer delete race

 drivers/net/wireless/ath/ath12k/core.c |   2 +-
 drivers/net/wireless/ath/ath12k/core.h |   5 +-
 drivers/net/wireless/ath/ath12k/mac.c  |  20 +----
 drivers/net/wireless/ath/ath12k/peer.c | 130 ++++++++++++++++++++++++++-------
 drivers/net/wireless/ath/ath12k/peer.h |  19 ++++-
 drivers/net/wireless/ath/ath12k/wmi.c  |  16 ++--
 6 files changed, 138 insertions(+), 54 deletions(-)
---
base-commit: 4987a85fb0475defee458fa11af877c8e02f764a
change-id: 20260602-ath12k-mlo-peer-delete-race-74fdaf880017

Best regards,
-- 
Baochen Qiang <baochen.qiang@oss.qualcomm.com>


^ permalink raw reply

* [PATCH RESEND v3] wifi: mt76: mt7921: fix resource leak in probe error path
From: Hongling Zeng @ 2026-06-17  8:58 UTC (permalink / raw)
  To: nbd, lorenzo, ryder.lee, shayne.chen, sean.wang, matthias.bgg,
	angelogioacchino.delregno, xiong.huang, madhurkumar004
  Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
	zhongling0719, Hongling Zeng

When pcim_iomap_region() or devm_kmemdup() fail, the code returns
directly without cleaning up previously allocated resources:
  - mt76_device allocated by mt76_alloc_device()
  - pci irq vectors allocated by pci_alloc_irq_vectors()
Fix this by jumping to the existing error cleanup path instead of
returning directly.
To avoid using an uninitialized variable in the error path, move the
dev initialization before the error checks.

Fixes: ee5bb35d2b83 ("wifi: mt76: mt7921: Replace deprecated PCI function")
Fixes: 222606f43b58 ("wifi: mt76: mt7921: handle MT7902 irq_map quirk with mutable copy")
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>

---
 Change in v3
  - Fix incorrect Fixes: tag as pointed out by Sean
  - Correct tag from unrelated phy/ti-pipe3 commit to the actual mt76 commit
    that introduced the resource leak
---
 drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index 7a790ddf43bb..49a37185f056 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -343,11 +343,14 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
 
 	pci_set_drvdata(pdev, mdev);
 
+	dev = container_of(mdev, struct mt792x_dev, mt76);
+
 	regs =  pcim_iomap_region(pdev, 0, pci_name(pdev));
-	if (IS_ERR(regs))
-		return PTR_ERR(regs);
+	if (IS_ERR(regs)) {
+		ret = PTR_ERR(regs);
+		goto err_free_dev;
+	}
 
-	dev = container_of(mdev, struct mt792x_dev, mt76);
 	dev->fw_features = features;
 	dev->hif_ops = &mt7921_pcie_ops;
 	dev->irq_map = &irq_map;
@@ -359,8 +362,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
 		/* MT7902 needs a mutable copy because wm2_complete_mask differs */
 		map = devm_kmemdup(&pdev->dev, &irq_map,
 				   sizeof(irq_map), GFP_KERNEL);
-		if (!map)
-			return -ENOMEM;
+		if (!map) {
+			ret = -ENOMEM;
+			goto err_free_dev;
+		}
 
 		map->rx.wm2_complete_mask = 0;
 		dev->irq_map = map;
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH ath-next 0/5] wifi: ath12k: fix dp_mon RX parsing for 32-bit TLV
From: Baochen Qiang @ 2026-06-17  8:57 UTC (permalink / raw)
  To: Miaoqing Pan, jjohnson; +Cc: ath12k, linux-wireless, linux-kernel
In-Reply-To: <20260509025819.1641630-1-miaoqing.pan@oss.qualcomm.com>



On 5/9/2026 10:58 AM, Miaoqing Pan wrote:
> This series fixes RX monitor status parsing issues on platforms using
> 32-bit TLV headers (e.g. QCC2072), and tightens TLV decoding robustness
> in ath12k datapath monitor handling.
> 
> Signed-off-by: Miaoqing Pan <miaoqing.pan@oss.qualcomm.com>
> ---
> 
> Miaoqing Pan (5):
>   wifi: ath12k: fix TLV32 length mask
>   wifi: ath12k: refactor HAL TLV32/64 decode helpers
>   wifi: ath12k: add HAL ops for monitor TLV header decode and alignment
>   wifi: ath12k: add dp_mon support 32-bit TLV headers
>   wifi: ath12k: tighten RX monitor TLV bounds check
> 
>  drivers/net/wireless/ath/ath12k/hal.c         | 42 +++++++++----
>  drivers/net/wireless/ath/ath12k/hal.h         | 13 ++--
>  .../net/wireless/ath/ath12k/wifi7/dp_mon.c    | 59 ++++++++++---------
>  .../wireless/ath/ath12k/wifi7/hal_qcc2072.c   |  4 +-
>  .../wireless/ath/ath12k/wifi7/hal_qcn9274.c   | 13 +++-
>  .../wireless/ath/ath12k/wifi7/hal_wcn7850.c   | 13 +++-
>  6 files changed, 94 insertions(+), 50 deletions(-)
> 
> 
> base-commit: 14d99bd40a8e3a80398dc597375fc7516ca488dd

Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH ath-next v2] wifi: ath12k: avoid setting 320MHZ support on non 6GHz band
From: Nicolas Escande @ 2026-06-17  8:45 UTC (permalink / raw)
  To: Rameshkumar Sundaram, ath12k; +Cc: linux-wireless
In-Reply-To: <3fb00a3b-2092-43d2-a8f8-9f934505dcee@oss.qualcomm.com>

On Wed Jun 17, 2026 at 7:03 AM CEST, Rameshkumar Sundaram wrote:
> On 6/11/2026 2:14 PM, Nicolas Escande wrote:
>> On a split phy qcn9274 (2.4GHz + 5GHz low), "iw phy" reports 320MHz
>> realated features on the 5GHz band while it should not:
>
>
> s/realated/related

sure

>
>> 
>>      Wiphy phy1
>>      [...]
>>          Band 2:
>>      [...]
>>              EHT Iftypes: managed
>>      [...]
>>                  EHT PHY Capabilities: (0xe2ffdbe018778000):
>>                      320MHz in 6GHz Supported
>>      [...]
>>                      Beamformee SS (320MHz): 7
>>      [...]
>>                      Number Of Sounding Dimensions (320MHz): 3
>>      [...]
>>                  EHT MCS/NSS: (0x22222222222222222200000000):
>> 
>> This is also reflected in the beacons sent by a mesh interface started on
>> that band. They erroneously advertise 320MHZ support too.
>
> s/320MHZ/320MHz

sure

>
>> 
>> This should not happen as the spec at section 9.4.2.323.3 says we should
>
> Reword the spec citation to: IEEE Std 802.11-2024, subclause 9.4.2.323.3 ...
>
>
>> not set the 320MHz related fields when not operating on a 6GHz band.
>> For example it says about Bit 0 "Support For 320 MHz In 6 GHz"
>> 
>>    "Reserved if the EHT Capabilities element is indicating capabilities for
>>     the 2.4 GHz or 5 GHz bands."
>> 
>> Fix this by clearing the related bits when converting from WMI eht phy
>> capabilities to mac80211 phy capabilities, for bands other than 6GHz.
>> 
>> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00218-QCAHKSWPL_SILICONZ-1
>> 
>
> The commit log mentions wrong `iw phy` output and wrong mesh beacon
> advertisement, so this appears user visible. If that is the intended bug 
> being fixed, please add a Fixes tag.
>

Well I did not put a fixes tag as this does not create any real problems
per say. It does not impede any sta to connect in any way. Only
people explicitely parsing the beacons IE see an unexpected value.

So unless you guys really insist, I'd just skip it. Otherwise the 2
candidates are are:
  - 1476014fadb6 ("wifi: ath12k: WMI support to process EHT capabilities")
    It added the EHT caps parsing but lacked any logic to handle 6G so
    that means backporting would imply a different patch than this one
  - d4e244c85e45 ("wifi: ath12k: enable 320 MHz bandwidth for 6 GHz band
    in EHT PHY capability for WCN7850"). This one added 6GHz support and
    is the one I'm fixing but for a different chipset in mind than mine.

>> Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
>> ---
>> Changes from v1:
>>    - rebased on ath-next
>>    - clear all 6GHz / 320MHz related phy capabilities fields from the firmware
>> ---
>>   drivers/net/wireless/ath/ath12k/wmi.c | 17 ++++++++++++++++-
>>   1 file changed, 16 insertions(+), 1 deletion(-)
>> 
>> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
>> index 84a31b953db8..a8a4654c4f34 100644
>> --- a/drivers/net/wireless/ath/ath12k/wmi.c
>> +++ b/drivers/net/wireless/ath/ath12k/wmi.c
>> @@ -5154,6 +5154,7 @@ static void ath12k_wmi_eht_caps_parse(struct ath12k_pdev *pdev, u32 band,
>>   				       __le32 cap_info_internal)
>>   {
>>   	struct ath12k_band_cap *cap_band = &pdev->cap.band[band];
>> +	u8 *phy_cap = (u8 *)&cap_band->eht_cap_phy_info[0];
>>   	u32 support_320mhz;
>>   	u8 i;
>>   
>> @@ -5167,8 +5168,22 @@ static void ath12k_wmi_eht_caps_parse(struct ath12k_pdev *pdev, u32 band,
>>   	for (i = 0; i < WMI_MAX_EHTCAP_PHY_SIZE; i++)
>>   		cap_band->eht_cap_phy_info[i] = le32_to_cpu(cap_phy_info[i]);
>>   
>> -	if (band == NL80211_BAND_6GHZ)
>> +	if (band == NL80211_BAND_6GHZ) {	
>>   		cap_band->eht_cap_phy_info[0] |= support_320mhz;
>> +	} else {
>> +		/*
>> +		 * It seems the firmware can report capabilities specific to
>> +		 * 6GHz also for 5GHz, so lets explicitely clear them out.
>
> The code comment should be less tentative, how about:
>
>       /*
>        * Firmware may report 6 GHz/320 MHz specific capabilities for
>        * non-6 GHz bands, so explicitly clear them.
>        */
>

sure

>
>> +		 */
>> +		phy_cap[0] &= ~IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
>> +		phy_cap[1] &= ~IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK;
>> +		phy_cap[2] &= ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK;
>> +		phy_cap[3] &= ~IEEE80211_EHT_PHY_CAP3_SOUNDING_DIM_320MHZ_MASK;
>> +		phy_cap[6] &= ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ;
>> +		phy_cap[6] &= ~IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP;
>> +		phy_cap[7] &= ~IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ;
>> +		phy_cap[7] &= ~IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;	;
>> +	}
>>   
>>   	cap_band->eht_mcs_20_only = le32_to_cpu(supp_mcs[0]);
>>   	cap_band->eht_mcs_80 = le32_to_cpu(supp_mcs[1]);
>
>
> checkpatch warnings:
> ERROR: trailing whitespace
> #61: FILE: drivers/net/wireless/ath/ath12k/wmi.c:5171:
> +^Iif (band == NL80211_BAND_6GHZ) {^I$
>
> WARNING: 'explicitely' may be misspelled - perhaps 'explicitly'?
> #66: FILE: drivers/net/wireless/ath/ath12k/wmi.c:5176:
> +                * 6GHz also for 5GHz, so lets explicitely clear them out.
>                                                 ^^^^^^^^^^^
>
> WARNING: space prohibited before semicolon
> #75: FILE: drivers/net/wireless/ath/ath12k/wmi.c:5185:
> +               phy_cap[7] &= 
> ~IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;     ;
>
> WARNING: Statements terminations use 1 semicolon
> #75: FILE: drivers/net/wireless/ath/ath12k/wmi.c:5185:
> +               phy_cap[7] &= 
> ~IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ;     ;

And all that will be fixed obviously

Thanks for the review

^ permalink raw reply

* RE: [PATCH wireless] wifi: rtw88: pci: fix resource leak on failed NAPI setup
From: Ping-Ke Shih @ 2026-06-17  8:33 UTC (permalink / raw)
  To: Dawei Feng
  Cc: leitao@debian.org, linux-wireless@vger.kernel.org,
	linux-kernel@vger.kernel.org, jianhao.xu@seu.edu.cn,
	zilin@seu.edu.cn, stable@vger.kernel.org
In-Reply-To: <20260617013502.114057-1-dawei.feng@seu.edu.cn>

Dawei Feng <dawei.feng@seu.edu.cn> wrote:
> rtw_pci_probe() allocates PCI resources through
> rtw_pci_setup_resource() before it sets up NAPI. If
> rtw_pci_napi_init() fails, the error path jumps straight to
> err_pci_declaim and skips rtw_pci_destroy(), leaving the PCI
> resources allocated by rtw_pci_setup_resource() behind.
> 
> Add a dedicated cleanup label for the NAPI setup failure path so probe
> destroys the PCI resources.
> 
> The bug was first flagged by an experimental analysis tool we are
> developing for kernel memory-management bugs while analyzing current
> mainline kernels. The tool is still under development and is not yet
> publicly available. Manual inspection confirms that the bug is still
> present in v7.1-rc7.
> 
> An x86_64 allyesconfig build showed no new warnings. As we do not have a
> suitable rtw88 PCI board to test with, no runtime testing was able to be
> performed.
> 
> Fixes: d0bcb10e7b94 ("wifi: rtw88: Un-embed dummy device")
> Cc: stable@vger.kernel.org
> Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>

Acked-by: Ping-Ke Shih <pkshih@realtek.com>



^ permalink raw reply

* Re: [PATCH rtw-next 2/3] wifi: rtlwifi: convert pci ID if-statement to table
From: Uwe Kleine-König @ 2026-06-17  8:26 UTC (permalink / raw)
  To: William Hansen-Baird; +Cc: pkshih, linux-wireless, linux-kernel
In-Reply-To: <20260614135508.70307-3-william.hansen.baird@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1164 bytes --]

On Sun, Jun 14, 2026 at 03:55:07PM +0200, William Hansen-Baird wrote:
> Refactor the ASUSTek quirk logic from an if-statement to a standard
> pci_device_id table. This allows future devices with the same quirk
> to be added more easily and avoiding a large if-chain.
> 
> Signed-off-by: William Hansen-Baird <william.hansen.baird@gmail.com>
> ---
>  drivers/net/wireless/realtek/rtlwifi/pci.c | 11 +++++++----
>  1 file changed, 7 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
> index 4ef1faf649e9..03b743809258 100644
> --- a/drivers/net/wireless/realtek/rtlwifi/pci.c
> +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
> @@ -31,6 +31,11 @@ static const u8 ac_to_hwq[] = {
>  	BK_QUEUE
>  };
>  
> +static const struct pci_device_id rtl8723be_aspm_quirks[] = {
> +	{ PCI_DEVICE_SUB(PCI_ANY_ID, PCI_ANY_ID, 0x11ad, 0x1723) },
> +	{ 0 }

I recently reworked many pci_device_id arrays and en passant made the
terminator an empty initializer with a single space (i.e. `{ }`).

If you resend, adapting to that style would be nice.

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* RE: [PATCH rtw-next v2 3/3] wifi: rtlwifi: disable ASPM for RTL8723BE with subsystem ID 17aa:b736
From: Ping-Ke Shih @ 2026-06-17  8:24 UTC (permalink / raw)
  To: William Hansen-Baird
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260616141620.91081-4-william.hansen.baird@gmail.com>

William Hansen-Baird <william.hansen.baird@gmail.com> wrote:
> RTL8723BE outputs a large amount of PCIe AER errors during and
> after boot, even before probe and when driver is never loaded.
> This causes significant system slowdown.
> 
> The errors are the same as reported by
> commit 77a6407c6ab2 ("wifi: rtlwifi: disable ASPM for RTL8723BE with subsystem ID 11ad:1723")
> 
> Add the RTL8723BE with subsystem ID 17aa:b736 to the rtl_aspm_quirks
> table to stop the AER errors. AER errors can still be present prior to
> pci probe, as the device by default may have ASPM enabled.
> 
> Testing on a Razer Blade 14 2017 which shipped from the
> OEM equipped with an RTL8723BE card with this subsystem ID
> confirms that this patch resolves the AER flood and allows the
> wireless card to function normally once the driver takes over.
> 
> Signed-off-by: William Hansen-Baird <william.hansen.baird@gmail.com>

Only minor suggestion, so

Acked-by: Ping-Ke Shih <pkshih@realtek.com>

> ---
>  drivers/net/wireless/realtek/rtlwifi/pci.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c
> b/drivers/net/wireless/realtek/rtlwifi/pci.c
> index 9de396879806..9919c8588cbd 100644
> --- a/drivers/net/wireless/realtek/rtlwifi/pci.c
> +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
> @@ -33,6 +33,7 @@ static const u8 ac_to_hwq[] = {
> 
>  static const struct pci_device_id rtl_aspm_quirks[] = {
>         { PCI_DEVICE_SUB(PCI_VENDOR_ID_REALTEK, 0xb723, 0x11ad, 0x1723) },
> +       { PCI_DEVICE_SUB(PCI_VENDOR_ID_REALTEK, 0xb723, 0x17aa, 0xb736) },

Would you like a comment "Razer Blade 14 2017"?

>         { 0 }
>  };
> 
> --
> 2.54.0


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox