From 80e5e68c223b676698288fde62e527b96e1ac8e2 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Tue, 28 Apr 2026 21:37:37 +0200 Subject: [PATCH] bes2600: handle multi-function SDIO cards in mmc_hw_reset bus_reset c5.2 (recover-wedged-firmware-via-mmc-hw-reset) wraps mmc_hw_reset() and treats any non-zero return as a recovery failure. On single-function SDIO cards mmc_hw_reset returns 0 after doing the remove + rescan inline. On multi-function cards (BES2600 has WLAN func 1 + BT companion func 2) the kernel's mmc_sdio_hw_reset() does NOT do the rescan: it tears the card down and returns 1 to signal "caller must trigger rescan". Field observation on PineTab2 (linux-pinetab2 6.19.10-danctnix1): when a real LMAC wedge fired bes2600_chrdev_wifi_force_close -> bes2600_chrdev_do_bus_reset, mmc_hw_reset returned 1, c5.2's wrapper treated that as "bus_reset failed: 1", logged the error, and gave up. The card was already removed (mmc2: card 0001 removed) but nothing scheduled a rescan; wifi (and the BT companion which shares the same SDIO host) stayed silent until the user rebooted four minutes later. Fix: - Capture the mmc_host pointer before calling mmc_hw_reset (the card pointer is invalid after the remove). - On positive return (multi-function path), log informationally and call mmc_detect_change(host, 0) to schedule a rescan. Return 0 so callers see the recovery as successful. - Negative return is still treated as failure as before. The mmc_detect_change side effect is asynchronous; the chrdev's wait_event_timeout(probe_done_wq, !sbus_priv) still observes the remove half synchronously, and the rescan + re-probe runs out of the host detect work afterwards. Signed-off-by: Markus Fritsche --- bes2600/bes2600_sdio.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c index c9cb073..b998381 100644 --- a/bes2600/bes2600_sdio.c +++ b/bes2600/bes2600_sdio.c @@ -1810,10 +1810,32 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self) */ static int bes2600_sdio_bus_reset(struct sbus_priv *self) { + struct mmc_host *host; + int ret; + if (!self || !self->func || !self->func->card) return -EINVAL; - return mmc_hw_reset(self->func->card); + host = self->func->card->host; + ret = mmc_hw_reset(self->func->card); + + /* + * On multi-function SDIO cards (BES2600 has WLAN func 1 + BT + * companion func 2), mmc_sdio_hw_reset() removes the card and + * returns 1 to signal "remove happened, caller must trigger + * rescan". The kernel does NOT auto-rescan in this case; + * single-function cards take the rescan path inline and return 0. + * Treat any non-negative return as success and force a rescan if + * mmc_hw_reset signalled the multi-function path - otherwise the + * card stays removed indefinitely after a wedge recovery, + * leaving wifi (and the BT companion) silent until reboot. + */ + if (ret > 0) { + bes_info("multi-func mmc_hw_reset removed card; scheduling rescan\n"); + mmc_detect_change(host, 0); + ret = 0; + } + return ret; } static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)