Re: [PATCH 1/2] mmc: core: Add sdio_trigger_replug() API

From: Matthias Kaehlcke
Date: Mon Jul 22 2019 - 15:36:03 EST


On Tue, Jul 16, 2019 at 09:42:08AM -0700, Douglas Anderson wrote:
> When using Marvell WiFi SDIO cards, it is not uncommon for Linux WiFi
> driver to fully lose the communication channel to the firmware running
> on the card. Presumably the firmware on the card has a bug or two in
> it and occasionally crashes.
>
> The Marvell WiFi driver attempts to recover from this problem.
> Specifically the driver has the function mwifiex_sdio_card_reset()
> which is called when communcation problems are found. That function
> attempts to reset the state of things by utilizing the mmc_hw_reset()
> function.
>
> The current solution is a bit complex because the Marvell WiFi driver
> needs to manually deinit and reinit the WiFi driver around the reset
> call. This means it's going through a bunch of code paths that aren't
> normally tested. However, complexity isn't our only problem. The
> other (bigger) problem is that Marvell WiFi cards are often combo
> WiFi/Bluetooth cards and Bluetooth runs on a second SDIO func. While
> the WiFi driver knows that it should re-init its own state around the
> mmc_hw_reset() call there is no good way to inform the Bluetooth
> driver. That means that in Linux today when you reset the Marvell
> WiFi driver you lose all Bluetooth communication. Doh!
>
> One way to fix the above problems is to leverage a more standard way
> to reset the Marvell WiFi card where we go through the same code paths
> as card unplug and the card plug. In this patch we introduce a new
> API call for doing just that: sdio_trigger_replug(). This API call
> will trigger an unplug of the SDIO card followed by a plug of the
> card. As part of this the card will be nicely reset.
>
> Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx>
> ---
>
> drivers/mmc/core/core.c | 28 ++++++++++++++++++++++++++--
> drivers/mmc/core/sdio_io.c | 20 ++++++++++++++++++++
> include/linux/mmc/host.h | 15 ++++++++++++++-
> include/linux/mmc/sdio_func.h | 2 ++
> 4 files changed, 62 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 9020cb2490f7..48a7d23aed26 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -2164,6 +2164,12 @@ int mmc_sw_reset(struct mmc_host *host)
> }
> EXPORT_SYMBOL(mmc_sw_reset);
>
> +void mmc_trigger_replug(struct mmc_host *host)
> +{
> + host->trigger_replug_state = MMC_REPLUG_STATE_UNPLUG;
> + _mmc_detect_change(host, 0, false);
> +}
> +
> static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
> {
> host->f_init = freq;
> @@ -2217,6 +2223,11 @@ int _mmc_detect_card_removed(struct mmc_host *host)
> if (!host->card || mmc_card_removed(host->card))
> return 1;
>
> + if (host->trigger_replug_state == MMC_REPLUG_STATE_UNPLUG) {
> + mmc_card_set_removed(host->card);
> + return 1;
> + }
> +
> ret = host->bus_ops->alive(host);
>
> /*
> @@ -2329,8 +2340,21 @@ void mmc_rescan(struct work_struct *work)
> mmc_bus_put(host);
>
> mmc_claim_host(host);
> - if (mmc_card_is_removable(host) && host->ops->get_cd &&
> - host->ops->get_cd(host) == 0) {
> +
> + /*
> + * Move through the state machine if we're triggering an unplug
> + * followed by a re-plug.
> + */
> + if (host->trigger_replug_state == MMC_REPLUG_STATE_UNPLUG) {
> + host->trigger_replug_state = MMC_REPLUG_STATE_PLUG;
> + _mmc_detect_change(host, 0, false);
> + } else if (host->trigger_replug_state == MMC_REPLUG_STATE_PLUG) {
> + host->trigger_replug_state = MMC_REPLUG_STATE_NONE;
> + }
> +
> + if (host->trigger_replug_state == MMC_REPLUG_STATE_PLUG ||
> + (mmc_card_is_removable(host) && host->ops->get_cd &&
> + host->ops->get_cd(host) == 0)) {

at first I was concerned there could be race conditions with the
different invocations of mmc_rescan(), but IIUC all calls are through
the host->detect work, so only one instance should be running at any
time.

> mmc_power_off(host);
> mmc_release_host(host);
> goto out;
> diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
> index 2ba00acf64e6..1c5c2a3ebe5e 100644
> --- a/drivers/mmc/core/sdio_io.c
> +++ b/drivers/mmc/core/sdio_io.c
> @@ -811,3 +811,23 @@ void sdio_retune_release(struct sdio_func *func)
> mmc_retune_release(func->card->host);
> }
> EXPORT_SYMBOL_GPL(sdio_retune_release);
> +
> +/**
> + * sdio_trigger_replug - trigger an "unplug" + "plug" of the card
> + * @func: SDIO function attached to host
> + *
> + * When you call this function we will schedule events that will
> + * make it look like the card contining the given SDIO func was

nit: containing

> + * unplugged and then re-plugged-in. This is as close as possible
> + * to a full reset of the card that can be achieved.
> + *
> + * NOTE: routnine will temporarily make the card look as if it is

nit: routine

Other than the typos this looks sane to me, I don't claim to have a
deep understanding of the MMC codebase though.

Reviewed-by: Matthias Kaehlcke <mka@xxxxxxxxxxxx>