[Linux-stm32] [PATCH v4 05/10] mmc: mmci: Make busy complete state machine explicit
Ulf Hansson
ulf.hansson at linaro.org
Thu Jun 15 11:56:58 UTC 2023
On Tue, 13 Jun 2023 at 22:34, Linus Walleij <linus.walleij at linaro.org> wrote:
>
> This refactors the ->busy_complete() callback currently
> only used by Ux500 and STM32 to handle busy detection on
> hardware where one and the same IRQ is fired whether we get
> a start or an end signal on busy detect.
>
> The code is currently using the cached status from the
> command IRQ in ->busy_status as a state to select what to
> do next: if this state is non-zero we are waiting for
> IRQs and if it is zero we treat the state as the starting
> point for a busy detect wait cycle.
>
> Make this explicit by creating a state machine where the
> ->busy_complete callback moves between three states.
>
> The Ux500 busy detect code currently assumes this order:
> we enable the busy detect IRQ, get a busy start IRQ, then a
> busy end IRQ, and then we clear and mask this IRQ and
> proceed.
>
> We insert debug prints for unexpected states.
>
> Augment the STM32 driver with similar states for
> completeness.
>
> This works as before on most cards, however on a
> problematic card that is not working with busy detect, and
> which I have been debugging, this happens:
>
> [127220.662719] mmci-pl18x 80005000.mmc: lost busy status
> when waiting for busy end IRQ
>
> This probably means that the busy detect start IRQ has
> already occurred when we start executing the
> ->busy_complete() callbacks, and the busy detect end IRQ
> is counted as the start IRQ, and this is what is causing
> the card to not be detected properly.
>
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
> ---
> ChangeLog v3->v4:
> - Assign state MMCI_BUSY_DONE outside the if()-clause for
> the busy detect initialization.
> ChangeLog v2->v3:
> - Drop surplus states and merge IDLE and DONE states into one,
> we start out DONE. Name states *_WAITING_FOR_* so it is clear
> what is going on.
> - Rebase on other changes.
> - Reword commit message.
> ChangeLog v1->v2:
> - No changes
> ---
> drivers/mmc/host/mmci.c | 55 +++++++++++++++++++++++++------------
> drivers/mmc/host/mmci.h | 14 ++++++++++
> drivers/mmc/host/mmci_stm32_sdmmc.c | 6 +++-
> 3 files changed, 56 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index d632658d9d20..3b1a23e4ec1c 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -670,6 +670,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
> writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> writel(readl(base + MMCIMASK0) &
> ~host->variant->busy_detect_mask, base + MMCIMASK0);
> + host->busy_state = MMCI_BUSY_DONE;
> host->busy_status = 0;
> return true;
> }
> @@ -687,7 +688,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
> * while, to allow it to be set, but tests indicates that it
> * isn't needed.
> */
> - if (!host->busy_status) {
> + if (host->busy_state == MMCI_BUSY_DONE) {
> status = readl(base + MMCISTATUS);
> if (status & host->variant->busy_detect_flag) {
> writel(readl(base + MMCIMASK0) |
> @@ -695,6 +696,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
> base + MMCIMASK0);
>
> host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
> + host->busy_state = MMCI_BUSY_WAITING_FOR_START_IRQ;
> return false;
> }
> }
> @@ -710,25 +712,40 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
> * both the start and the end interrupts needs to be cleared,
> * one after the other. So, clear the busy start IRQ here.
> */
> - if (host->busy_status &&
> - (status & host->variant->busy_detect_flag)) {
> - host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
> - writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> - return false;
> + if (host->busy_state == MMCI_BUSY_WAITING_FOR_START_IRQ) {
> + if (status & host->variant->busy_detect_flag) {
> + host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
> + writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> + host->busy_state = MMCI_BUSY_WAITING_FOR_END_IRQ;
> + return false;
> + } else {
> + dev_dbg(mmc_dev(host->mmc),
> + "lost busy status when waiting for busy start IRQ\n");
> + writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> + writel(readl(base + MMCIMASK0) &
> + ~host->variant->busy_detect_mask, base + MMCIMASK0);
> + host->busy_state = MMCI_BUSY_DONE;
> + host->busy_status = 0;
> + return true;
> + }
> }
>
> - /*
> - * If there is a command in-progress that has been successfully
> - * sent and the busy bit isn't set, it means we have received
> - * the busy end IRQ. Clear and mask the IRQ, then continue to
> - * process the command.
> - */
> - if (host->busy_status) {
> - writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> -
> - writel(readl(base + MMCIMASK0) &
> - ~host->variant->busy_detect_mask, base + MMCIMASK0);
> - host->busy_status = 0;
> + if (host->busy_state == MMCI_BUSY_WAITING_FOR_END_IRQ) {
> + if (status & host->variant->busy_detect_flag) {
I realize that I have already queued up $subject patch, but I haven't
been able to test this myself yet.
However, I wonder if the above is really correct? More precisely, did
you confirm that the variant->busy_detect_flag-bit becomes set for the
ux500 variant, when the card stops signaling busy? If not, we will
always end up in the else clause below?
> + host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
> + writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> + host->busy_state = MMCI_BUSY_DONE;
> + return false;
> + } else {
> + dev_dbg(mmc_dev(host->mmc),
> + "lost busy status when waiting for busy end IRQ\n");
> + writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> + writel(readl(base + MMCIMASK0) &
> + ~host->variant->busy_detect_mask, base + MMCIMASK0);
> + host->busy_state = MMCI_BUSY_DONE;
> + host->busy_status = 0;
> + return true;
> + }
> }
>
[...]
Kind regards
Uffe
More information about the Linux-stm32
mailing list