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 <fritsche.markus@gmail.com>
This commit is contained in:
2026-04-28 21:37:37 +02:00
parent 983bd62dd0
commit 80e5e68c22
+23 -1
View File
@@ -1810,10 +1810,32 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
*/ */
static int bes2600_sdio_bus_reset(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) if (!self || !self->func || !self->func->card)
return -EINVAL; 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) static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)