* [PATCH 1/4] wifi: brcmfmac: pcie: set the state before D0_INFORM on resume
2026-11-07 15:48 [PATCH 0/4] wifi: brcmfmac: pcie: S3 suspend and resume on the T2 Mac chips Andre Eikmeyer
@ 2026-11-07 15:48 ` Andre Eikmeyer
2026-11-07 15:48 ` [PATCH 2/4] wifi: brcmfmac: pcie: skip the host ready doorbell at init once it is a capability Andre Eikmeyer
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Andre Eikmeyer @ 2026-11-07 15:48 UTC (permalink / raw)
To: Arend van Spriel
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, linux-kernel,
Michael Reeves, Hector Martin, Janne Grunau, asahi,
Sebastian Reichel
With the mailbox on the control ring, D0_INFORM goes through the ring
write path, which refuses to write while devinfo->state is DOWN.
brcmf_pcie_pm_leave_D3() sets the state only after D0_INFORM, so every
hot resume fails and falls back to remove and re-probe:
brcmf_pcie_send_mb_data: Failed to send H2D mailbox data (-5)
Set the state before D0_INFORM and back to DOWN when the hot resume
fails.
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 4de157d18ff..231a2fb89f0 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -2838,10 +2838,10 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
/* Check if device is still up and running, if so we are ready */
if (brcmf_pcie_read_pcie32(devinfo, devinfo->reginfo->intmask) != 0) {
brcmf_dbg(PCIE, "Try to wakeup device....\n");
+ devinfo->state = BRCMFMAC_PCIE_STATE_UP;
if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM))
goto cleanup;
brcmf_dbg(PCIE, "Hot resume, continue....\n");
- devinfo->state = BRCMFMAC_PCIE_STATE_UP;
brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
brcmf_bus_change_state(bus, BRCMF_BUS_UP);
brcmf_pcie_intr_enable(devinfo);
@@ -2851,6 +2851,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
}
cleanup:
+ devinfo->state = BRCMFMAC_PCIE_STATE_DOWN;
brcmf_chip_detach(devinfo->ci);
devinfo->ci = NULL;
pdev = devinfo->pdev;
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/4] wifi: brcmfmac: pcie: skip the host ready doorbell at init once it is a capability
2026-11-07 15:48 [PATCH 0/4] wifi: brcmfmac: pcie: S3 suspend and resume on the T2 Mac chips Andre Eikmeyer
2026-11-07 15:48 ` [PATCH 1/4] wifi: brcmfmac: pcie: set the state before D0_INFORM on resume Andre Eikmeyer
@ 2026-11-07 15:48 ` Andre Eikmeyer
2026-11-07 15:48 ` [PATCH 3/4] wifi: brcmfmac: pcie: probe again after a resume through firmware Andre Eikmeyer
2026-11-07 15:48 ` [PATCH 4/4] wifi: brcmfmac: msgbuf: send the flowring delete without waiting for tx status Andre Eikmeyer
3 siblings, 0 replies; 5+ messages in thread
From: Andre Eikmeyer @ 2026-11-07 15:48 UTC (permalink / raw)
To: Arend van Spriel
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, linux-kernel,
Michael Reeves, Hector Martin, Janne Grunau, asahi,
Sebastian Reichel
With host ready in the host capabilities, the BCM4364 firmware halts on
the doorbell that brcmf_pcie_preinit() rings, before it answers the
first ioctl. The B3 of the MacBookPro16,1 does so on every load, the B2
of the MacBookPro15,1 on some:
brcmf_pcie_handle_mb_data D2H_MB_DATA: FW HALT
brcmf_attach: dongle is not responding: err=-5
The BCM4377 stays silent without the capability, so it stays. Once host
ready is a capability the firmware takes the doorbell as the exit from
D3, where Broadcom's DHD driver rings it, after D0_INFORM. Ring it at
init only when host ready is not a capability.
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
.../net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 231a2fb89f0..fc0f23477eb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -325,6 +325,7 @@ struct brcmf_pcie_shared_info {
dma_addr_t ringupd_dmahandle;
u8 version;
bool mb_via_ctl;
+ bool hostrdy;
};
#define BRCMF_OTP_MAX_PARAM_LEN 16
@@ -1599,7 +1600,13 @@ static int brcmf_pcie_preinit(struct device *dev)
brcmf_dbg(PCIE, "Enter\n");
brcmf_pcie_intr_enable(buspub->devinfo);
- brcmf_pcie_hostready(buspub->devinfo);
+ /*
+ * Once the host capabilities enable host ready, the firmware takes the
+ * doorbell as the exit from D3 only. The BCM4364 firmware halts on it
+ * here.
+ */
+ if (!buspub->devinfo->shared.hostrdy)
+ brcmf_pcie_hostready(buspub->devinfo);
return 0;
}
@@ -1806,8 +1813,10 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
!(shared->flags & BRCMF_PCIE_SHARED_USE_MAILBOX);
host_cap = shared->version;
- if (shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1)
+ if (shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1) {
host_cap |= BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY;
+ shared->hostrdy = true;
+ }
if (shared->flags & BRCMF_PCIE_SHARED_DAR)
host_cap |= BRCMF_HOSTCAP_H2D_DAR;
host_cap |= BRCMF_HOSTCAP_DS_NO_OOB_DW;
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/4] wifi: brcmfmac: pcie: probe again after a resume through firmware
2026-11-07 15:48 [PATCH 0/4] wifi: brcmfmac: pcie: S3 suspend and resume on the T2 Mac chips Andre Eikmeyer
2026-11-07 15:48 ` [PATCH 1/4] wifi: brcmfmac: pcie: set the state before D0_INFORM on resume Andre Eikmeyer
2026-11-07 15:48 ` [PATCH 2/4] wifi: brcmfmac: pcie: skip the host ready doorbell at init once it is a capability Andre Eikmeyer
@ 2026-11-07 15:48 ` Andre Eikmeyer
2026-11-07 15:48 ` [PATCH 4/4] wifi: brcmfmac: msgbuf: send the flowring delete without waiting for tx status Andre Eikmeyer
3 siblings, 0 replies; 5+ messages in thread
From: Andre Eikmeyer @ 2026-11-07 15:48 UTC (permalink / raw)
To: Arend van Spriel
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, linux-kernel,
Michael Reeves, Hector Martin, Janne Grunau, asahi,
Sebastian Reichel
After ACPI S3 the chip comes back without its firmware, but the
interrupt mask can read back nonzero, so brcmf_pcie_pm_leave_D3()
takes the hot resume path and every command times out afterwards.
Seen on the BCM4377 and BCM4364 of the T2 Macs.
When the platform firmware handled the resume and WoWL is off, go
straight to the existing remove and re-probe path.
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
.../net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index fc0f23477eb..faa1e16ed4f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/pci.h>
+#include <linux/suspend.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -2844,6 +2845,15 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
devinfo = bus->bus_priv.pcie->devinfo;
brcmf_dbg(PCIE, "Enter, dev=%p, bus=%p\n", dev, bus);
+ /*
+ * Without WoWL the device is powered off when the platform firmware
+ * handles the system sleep, as with ACPI S3, and comes back without
+ * its firmware. Its registers can still read back nonzero, so probe
+ * it again.
+ */
+ if (pm_resume_via_firmware() && !devinfo->wowl_enabled)
+ goto cleanup;
+
/* Check if device is still up and running, if so we are ready */
if (brcmf_pcie_read_pcie32(devinfo, devinfo->reginfo->intmask) != 0) {
brcmf_dbg(PCIE, "Try to wakeup device....\n");
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 4/4] wifi: brcmfmac: msgbuf: send the flowring delete without waiting for tx status
2026-11-07 15:48 [PATCH 0/4] wifi: brcmfmac: pcie: S3 suspend and resume on the T2 Mac chips Andre Eikmeyer
` (2 preceding siblings ...)
2026-11-07 15:48 ` [PATCH 3/4] wifi: brcmfmac: pcie: probe again after a resume through firmware Andre Eikmeyer
@ 2026-11-07 15:48 ` Andre Eikmeyer
3 siblings, 0 replies; 5+ messages in thread
From: Andre Eikmeyer @ 2026-11-07 15:48 UTC (permalink / raw)
To: Arend van Spriel
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, linux-kernel,
Michael Reeves, Hector Martin, Janne Grunau, asahi,
Sebastian Reichel
The BCM4377 firmware returns the tx status of queued packets only when
it gets the flowring delete request. brcmf_msgbuf_delete_flowring()
waits up to 100 ms for them before it sends the request, so every
disconnect logs a timeout, and the cleared counter goes below zero when
the status arrives.
The race the wait was added for in commit 0fa24196e425 ("wifi: brcmfmac:
fix continuous 802.1x tx pending timeout error") is covered by
RING_CLOSING since commit 09be7546a602 ("wifi: brcmfmac: fix scheduling
while atomic issue when deleting flowring"), and late tx status still
releases the packet. Send the delete right away and reset the counter
when the ring is created.
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
.../broadcom/brcm80211/brcmfmac/msgbuf.c | 23 ++++++++-----------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index 8e5c697e7f1..a9ccf647f9a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -73,7 +73,6 @@
#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
-#define BRCMF_MAX_TXSTATUS_WAIT_RETRIES 10
struct msgbuf_common_hdr {
u8 msgtype;
@@ -645,6 +644,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf,
brcmf_commonring_config(msgbuf->flowrings[flowid],
BRCMF_H2D_TXFLOWRING_MAX_ITEM,
BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf);
+ atomic_set(&msgbuf->flowrings[flowid]->outstanding_tx, 0);
commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
brcmf_commonring_lock(commonring);
@@ -1330,6 +1330,10 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
}
brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid,
status);
+ if (atomic_read(&msgbuf->flowrings[flowid]->outstanding_tx))
+ brcmf_dbg(MSGBUF, "Flowring %d deleted with %d tx status outstanding\n",
+ flowid,
+ atomic_read(&msgbuf->flowrings[flowid]->outstanding_tx));
brcmf_msgbuf_remove_flowring(msgbuf, flowid);
}
@@ -1471,23 +1475,16 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
void *ret_ptr;
u8 ifidx;
int err;
- int retry = BRCMF_MAX_TXSTATUS_WAIT_RETRIES;
- /* make sure it is not in txflow */
+ /*
+ * Make sure txflow posts nothing more to the ring. The firmware returns
+ * the tx status of the posted packets when it deletes the ring, before
+ * it sends the delete response.
+ */
brcmf_commonring_lock(commonring_del);
flow->rings[flowid]->status = RING_CLOSING;
brcmf_commonring_unlock(commonring_del);
- /* wait for commonring txflow finished */
- while (retry && atomic_read(&commonring_del->outstanding_tx)) {
- usleep_range(5000, 10000);
- retry--;
- }
- if (!retry) {
- brcmf_err("timed out waiting for txstatus\n");
- atomic_set(&commonring_del->outstanding_tx, 0);
- }
-
/* no need to submit if firmware can not be reached */
if (drvr->bus_if->state != BRCMF_BUS_UP) {
brcmf_dbg(MSGBUF, "bus down, flowring will be removed\n");
^ permalink raw reply [flat|nested] 5+ messages in thread