All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI
@ 2023-04-08 22:00 Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 01/12] mmc: mmci: Clear busy_status when starting command Linus Walleij
                   ` (12 more replies)
  0 siblings, 13 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

This series fixes a pretty serious problem in the MMCI
busy detect handling, discovered only after going up and
down a ladder of refactorings.

The code is written expecting the Ux500 busy detect
to fire two interrupts: one at the start of the busy
signalling and one at the end of the busy signalling.

The root cause of the problem seen on some devices
is that only the first IRQ arrives, and then the device
hangs, waiting perpetually for the next IRQ to arrive.

This is eventually solved by adding a timeout using
a delayed work that fire after 10 ms if the busy detect
has not stopped at this point. (Other delay spans can
be suggested.) This is the last patch in the series.

I included the rewrite of the entire busy detect logic
to use a state machine as this makes it way easier to
debug and will print messages about other error
conditions as well.

The problem affects especially the Skomer
(Samsung GT-I9070) and Kyle (Samsung SGH-I407).

It is fine to just apply this for the -next kernel,
despite it fixes the busy detect that has been broken
for these devices for a while, we can think about
backporting a simpler version of the timeout for
stable kernels if we want.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
Changes in v2:
- Drop pointless patch nr 1
- Unconditionally intialize some state variables
- Use a less fragile method to look for busy status when
  using busy detect, should fix Yann's problem
- Link to v1: https://lore.kernel.org/r/20230405-pl180-busydetect-fix-v1-0-28ac19a74e5e@linaro.org

---
Linus Walleij (12):
      mmc: mmci: Clear busy_status when starting command
      mmc: mmci: Unwind big if() clause
      mmc: mmci: Stash status while waiting for busy
      mmc: mmci: Break out error check in busy detect
      mmc: mmci: Make busy complete state machine explicit
      mmc: mmci: Retry the busy start condition
      mmc: mmci: Use state machine state as exit condition
      mmc: mmci: Use a switch statement machine
      mmc: mmci: Break out a helper function
      mmc: mmci: mmci_card_busy() from state machine
      mmc: mmci: Drop end IRQ from Ux500 busydetect
      mmc: mmci: Add busydetect timeout

 drivers/mmc/host/mmci.c             | 166 ++++++++++++++++++++++++++++--------
 drivers/mmc/host/mmci.h             |  17 ++++
 drivers/mmc/host/mmci_stm32_sdmmc.c |   6 +-
 3 files changed, 151 insertions(+), 38 deletions(-)
---
base-commit: fe15c26ee26efa11741a7b632e9f23b01aca4cc6
change-id: 20230405-pl180-busydetect-fix-66a0360d398a

Best regards,
-- 
Linus Walleij <linus.walleij@linaro.org>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 01/12] mmc: mmci: Clear busy_status when starting command
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-17 13:06   ` Ulf Hansson
  2023-04-08 22:00 ` [PATCH v2 02/12] mmc: mmci: Unwind big if() clause Linus Walleij
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

If we are starting a command which can generate a busy
response, then clear the variable host->busy_status
if the variant is using a ->busy_complete callback.

We are lucky that the member is zero by default and
hopefully always gets cleared in the ->busy_complete
callback even on errors, but it's just fragile so
make sure it is always initialized to zero.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Unconditionally clear host->busy_status if we get a
  busy response.
---
 drivers/mmc/host/mmci.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b9e5dfe74e5c..9b48df842425 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1238,17 +1238,21 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
 			c |= host->variant->cmdreg_srsp;
 	}
 
-	if (host->variant->busy_timeout && cmd->flags & MMC_RSP_BUSY) {
-		if (!cmd->busy_timeout)
-			cmd->busy_timeout = 10 * MSEC_PER_SEC;
+	if (cmd->flags & MMC_RSP_BUSY) {
+		host->busy_status = 0;
 
-		if (cmd->busy_timeout > host->mmc->max_busy_timeout)
-			clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk;
-		else
-			clks = (unsigned long long)cmd->busy_timeout * host->cclk;
+		if (host->variant->busy_timeout) {
+			if (!cmd->busy_timeout)
+				cmd->busy_timeout = 10 * MSEC_PER_SEC;
 
-		do_div(clks, MSEC_PER_SEC);
-		writel_relaxed(clks, host->base + MMCIDATATIMER);
+			if (cmd->busy_timeout > host->mmc->max_busy_timeout)
+				clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk;
+			else
+				clks = (unsigned long long)cmd->busy_timeout * host->cclk;
+
+			do_div(clks, MSEC_PER_SEC);
+			writel_relaxed(clks, host->base + MMCIDATATIMER);
+		}
 	}
 
 	if (host->ops->pre_sig_volt_switch && cmd->opcode == SD_SWITCH_VOLTAGE)

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 02/12] mmc: mmci: Unwind big if() clause
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 01/12] mmc: mmci: Clear busy_status when starting command Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 03/12] mmc: mmci: Stash status while waiting for busy Linus Walleij
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

This does two things: firsr replace the hard-to-read long
if-expression:

  if (!host->busy_status && !(status & err_msk) &&
      (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {

With the more readable:

  if (!host->busy_status && !(status & err_msk)) {
     status = readl(base + MMCISTATUS);
     if (status & host->variant->busy_detect_flag) {

Second notice that the re-read MMCISTATUS register is now
stored into the status variable, using logic OR because what
if something else changed too?

While we are at it, explain what the function is doing.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Only assign the cached status in host->busy_status if
  we have busy detect signalling going on.
---
 drivers/mmc/host/mmci.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 9b48df842425..8496df2020d9 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -654,6 +654,13 @@ static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host)
 	return MCI_DPSM_ENABLE | (host->data->blksz << 16);
 }
 
+/*
+ * ux500_busy_complete() - this will wait until the busy status
+ * goes off, saving any status that occur in the meantime into
+ * host->busy_status until we know the card is not busy any more.
+ * The function returns true when the busy detection is ended
+ * and we should continue processing the command.
+ */
 static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 {
 	void __iomem *base = host->base;
@@ -671,14 +678,16 @@ 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 && !(status & err_msk) &&
-	    (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
-		writel(readl(base + MMCIMASK0) |
-		       host->variant->busy_detect_mask,
-		       base + MMCIMASK0);
+	if (!host->busy_status && !(status & err_msk)) {
+		status = readl(base + MMCISTATUS);
+		if (status & host->variant->busy_detect_flag) {
+			writel(readl(base + MMCIMASK0) |
+			       host->variant->busy_detect_mask,
+			       base + MMCIMASK0);
 
-		host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
-		return false;
+			host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
+			return false;
+		}
 	}
 
 	/*

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 03/12] mmc: mmci: Stash status while waiting for busy
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 01/12] mmc: mmci: Clear busy_status when starting command Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 02/12] mmc: mmci: Unwind big if() clause Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 04/12] mmc: mmci: Break out error check in busy detect Linus Walleij
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

Some interesting flags can arrive while we are waiting for
the first busy detect IRQ so OR then onto the stashed
flags so they are not missed.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 8496df2020d9..e742dedaca1a 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -703,6 +703,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 	 */
 	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;
 	}

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 04/12] mmc: mmci: Break out error check in busy detect
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (2 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 03/12] mmc: mmci: Stash status while waiting for busy Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-17 13:23   ` Ulf Hansson
  2023-04-08 22:00 ` [PATCH v2 05/12] mmc: mmci: Make busy complete state machine explicit Linus Walleij
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

The busy detect callback for Ux500 checks for an error
in the status in the first if() clause. The only practical
reason is that if an error occurs, the if()-clause is not
executed, and the code falls through to the last
if()-clause if (host->busy_status) which will clear and
disable the irq. Make this explicit instead: it is easier
to read.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index e742dedaca1a..7d42625f2356 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -665,6 +665,15 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 {
 	void __iomem *base = host->base;
 
+	if (status & err_msk) {
+		/* Stop any ongoing busy detection if an error occurs */
+		writel(host->variant->busy_detect_mask, base + MMCICLEAR);
+		writel(readl(base + MMCIMASK0) &
+		       ~host->variant->busy_detect_mask, base + MMCIMASK0);
+		host->busy_status = 0;
+		return true;
+	}
+
 	/*
 	 * Before unmasking for the busy end IRQ, confirm that the
 	 * command was sent successfully. To keep track of having a
@@ -678,7 +687,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 && !(status & err_msk)) {
+	if (!host->busy_status) {
 		status = readl(base + MMCISTATUS);
 		if (status & host->variant->busy_detect_flag) {
 			writel(readl(base + MMCIMASK0) |

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 05/12] mmc: mmci: Make busy complete state machine explicit
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (3 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 04/12] mmc: mmci: Break out error check in busy detect Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-17 14:44   ` Ulf Hansson
  2023-04-08 22:00 ` [PATCH v2 06/12] mmc: mmci: Retry the busy start condition Linus Walleij
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

This refactors the ->busy_complete() callback currently
only used by Ux500 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 four states:
MMCI_BUSY_NOT_STARTED, MMCI_BUSY_WAITING_FOR_IRQS,
MMCI_BUSY_START_IRQ and MMCI_BUSY_END_IRQ.

The 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 dev_err() 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@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c             | 55 +++++++++++++++++++++++++++++++------
 drivers/mmc/host/mmci.h             | 16 +++++++++++
 drivers/mmc/host/mmci_stm32_sdmmc.c |  6 +++-
 3 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 7d42625f2356..887b83e392a4 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_IDLE) {
 		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_IRQS;
 			return false;
 		}
 	}
@@ -710,11 +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_IRQS) {
+		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_START_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 (host->busy_state == MMCI_BUSY_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_END_IRQ;
+			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;
+		}
 	}
 
 	/*
@@ -723,11 +754,18 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 	 * 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);
+	if (host->busy_state == MMCI_BUSY_END_IRQ) {
+		if (status & host->variant->busy_detect_flag) {
+			/* We should just get two IRQs for busy detect */
+			dev_err(mmc_dev(host->mmc), "spurious busy detect IRQ\n");
+			return false;
+		}
 
+		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;
 	}
 
@@ -1258,6 +1296,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
 	}
 
 	if (cmd->flags & MMC_RSP_BUSY) {
+		host->busy_state = MMCI_BUSY_IDLE;
 		host->busy_status = 0;
 
 		if (host->variant->busy_timeout) {
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index e1a9b96a3396..82f3850325c8 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -261,6 +261,21 @@ struct clk;
 struct dma_chan;
 struct mmci_host;
 
+/**
+ * enum mmci_busy_state - enumerate the busy detect wait states
+ *
+ * This is used for the state machine waiting for different busy detect
+ * interrupts on hardware that fire a single IRQ for start and end of
+ * the busy detect phase on DAT0.
+ */
+enum mmci_busy_state {
+	MMCI_BUSY_IDLE,
+	MMCI_BUSY_WAITING_FOR_IRQS,
+	MMCI_BUSY_START_IRQ,
+	MMCI_BUSY_END_IRQ,
+	MMCI_BUSY_DONE,
+};
+
 /**
  * struct variant_data - MMCI variant-specific quirks
  * @clkreg: default value for MCICLOCK register
@@ -409,6 +424,7 @@ struct mmci_host {
 	u32			clk_reg;
 	u32			clk_reg_add;
 	u32			datactrl_reg;
+	enum mmci_busy_state	busy_state;
 	u32			busy_status;
 	u32			mask1_reg;
 	u8			vqmmc_enabled:1;
diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c
index 60bca78a72b1..24831a1092b2 100644
--- a/drivers/mmc/host/mmci_stm32_sdmmc.c
+++ b/drivers/mmc/host/mmci_stm32_sdmmc.c
@@ -393,8 +393,10 @@ static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 	busy_d0 = sdmmc_status & MCI_STM32_BUSYD0;
 
 	/* complete if there is an error or busy_d0end */
-	if ((status & err_msk) || busy_d0end)
+	if ((status & err_msk) || busy_d0end) {
+		host->busy_state = MMCI_BUSY_DONE;
 		goto complete;
+	}
 
 	/*
 	 * On response the busy signaling is reflected in the BUSYD0 flag.
@@ -408,6 +410,7 @@ static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			host->busy_status = status &
 				(MCI_CMDSENT | MCI_CMDRESPEND);
 		}
+		host->busy_state = MMCI_BUSY_END_IRQ;
 		return false;
 	}
 
@@ -416,6 +419,7 @@ static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		writel_relaxed(mask & ~host->variant->busy_detect_mask,
 			       base + MMCIMASK0);
 		host->busy_status = 0;
+		host->busy_state = MMCI_BUSY_DONE;
 	}
 
 	writel_relaxed(host->variant->busy_detect_mask, base + MMCICLEAR);

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 06/12] mmc: mmci: Retry the busy start condition
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (4 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 05/12] mmc: mmci: Make busy complete state machine explicit Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 07/12] mmc: mmci: Use state machine state as exit condition Linus Walleij
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

This makes the ux500 ->busy_complete() callback re-read
the status register 10 times while waiting for the busy
signal to assert in the status register.

If this does not happen, we bail out regarding the
command completed already, i.e. before we managed to
start to check the busy status.

There is a comment in the code about this, let's just
implement it to be certain that we can catch this glitch
if it happens.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Move over the initial saving of host->busy_status from
  an unrelated patch to this one: it is clear what we are
  doing: we don't want to miss any transient
  (MCI_CMDSENT | MCI_CMDRESPEND) in the status register.
---
 drivers/mmc/host/mmci.c | 40 ++++++++++++++++++++++++++++------------
 1 file changed, 28 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 887b83e392a4..590703075bbc 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -664,6 +664,7 @@ static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host)
 static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 {
 	void __iomem *base = host->base;
+	int retries = 10;
 
 	if (status & err_msk) {
 		/* Stop any ongoing busy detection if an error occurs */
@@ -684,21 +685,36 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 	 * Note that, the card may need a couple of clock cycles before
 	 * it starts signaling busy on DAT0, hence re-read the
 	 * MMCISTATUS register here, to allow the busy bit to be set.
-	 * Potentially we may even need to poll the register for a
-	 * while, to allow it to be set, but tests indicates that it
-	 * isn't needed.
 	 */
 	if (host->busy_state == MMCI_BUSY_IDLE) {
-		status = readl(base + MMCISTATUS);
-		if (status & host->variant->busy_detect_flag) {
-			writel(readl(base + MMCIMASK0) |
-			       host->variant->busy_detect_mask,
-			       base + MMCIMASK0);
-
-			host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
-			host->busy_state = MMCI_BUSY_WAITING_FOR_IRQS;
-			return false;
+		/*
+		 * Save the first status register read to be sure to catch
+		 * all bits that may be lost will retrying. If the command
+		 * is still busy this will result in assigning 0 to
+		 * host->busy_status, which is what it should be in IDLE.
+		 */
+		host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
+		while (retries) {
+			status = readl(base + MMCISTATUS);
+			if (status & host->variant->busy_detect_flag) {
+				writel(readl(base + MMCIMASK0) |
+				       host->variant->busy_detect_mask,
+				       base + MMCIMASK0);
+
+				/* Keep accumulating status bits */
+				host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
+				host->busy_state = MMCI_BUSY_WAITING_FOR_IRQS;
+				return false;
+			}
+			retries--;
 		}
+		dev_dbg(mmc_dev(host->mmc), "no busy signalling in time\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;
 	}
 
 	/*

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 07/12] mmc: mmci: Use state machine state as exit condition
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (5 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 06/12] mmc: mmci: Retry the busy start condition Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 08/12] mmc: mmci: Use a switch statement machine Linus Walleij
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

Return true if and only if we reached the state
MMCI_BUSY_DONE in the ux500 ->busy_complete() callback.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 590703075bbc..2689c6bb62d6 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -673,7 +673,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		       ~host->variant->busy_detect_mask, base + MMCIMASK0);
 		host->busy_state = MMCI_BUSY_DONE;
 		host->busy_status = 0;
-		return true;
+		goto out_ret_state;
 	}
 
 	/*
@@ -704,7 +704,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 				/* Keep accumulating status bits */
 				host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
 				host->busy_state = MMCI_BUSY_WAITING_FOR_IRQS;
-				return false;
+				goto out_ret_state;
 			}
 			retries--;
 		}
@@ -713,8 +713,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		writel(readl(base + MMCIMASK0) &
 		       ~host->variant->busy_detect_mask, base + MMCIMASK0);
 		host->busy_state = MMCI_BUSY_DONE;
-		host->busy_status = 0;
-		return true;
+		goto out_ret_state;
 	}
 
 	/*
@@ -733,7 +732,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
 			writel(host->variant->busy_detect_mask, base + MMCICLEAR);
 			host->busy_state = MMCI_BUSY_START_IRQ;
-			return false;
+			goto out_ret_state;
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy start IRQ\n");
@@ -742,7 +741,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			       ~host->variant->busy_detect_mask, base + MMCIMASK0);
 			host->busy_state = MMCI_BUSY_DONE;
 			host->busy_status = 0;
-			return true;
+			goto out_ret_state;
 		}
 	}
 
@@ -751,7 +750,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
 			writel(host->variant->busy_detect_mask, base + MMCICLEAR);
 			host->busy_state = MMCI_BUSY_END_IRQ;
-			return false;
+			goto out_ret_state;
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy end IRQ\n");
@@ -760,7 +759,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			       ~host->variant->busy_detect_mask, base + MMCIMASK0);
 			host->busy_state = MMCI_BUSY_DONE;
 			host->busy_status = 0;
-			return true;
+			goto out_ret_state;
 		}
 	}
 
@@ -774,7 +773,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		if (status & host->variant->busy_detect_flag) {
 			/* We should just get two IRQs for busy detect */
 			dev_err(mmc_dev(host->mmc), "spurious busy detect IRQ\n");
-			return false;
+			goto out_ret_state;
 		}
 
 		writel(host->variant->busy_detect_mask, base + MMCICLEAR);
@@ -783,9 +782,13 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 
 		host->busy_state = MMCI_BUSY_DONE;
 		host->busy_status = 0;
+	} else {
+		dev_dbg(mmc_dev(host->mmc), "fell through on state %d\n",
+			host->busy_state);
 	}
 
-	return true;
+out_ret_state:
+	return (host->busy_state == MMCI_BUSY_DONE);
 }
 
 /*

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 08/12] mmc: mmci: Use a switch statement machine
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (6 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 07/12] mmc: mmci: Use state machine state as exit condition Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 09/12] mmc: mmci: Break out a helper function Linus Walleij
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

As is custom, use a big switch statement to transition
between the edges of the state machine inside
the ux500 ->busy_complete callback.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 34 ++++++++++++++++++++--------------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 2689c6bb62d6..76d885d7e49f 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -676,6 +676,12 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		goto out_ret_state;
 	}
 
+	/*
+	 * The state transitions are encoded in a state machine crossing
+	 * the edges in this switch statement.
+	 */
+	switch (host->busy_state) {
+
 	/*
 	 * Before unmasking for the busy end IRQ, confirm that the
 	 * command was sent successfully. To keep track of having a
@@ -686,7 +692,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 	 * it starts signaling busy on DAT0, hence re-read the
 	 * MMCISTATUS register here, to allow the busy bit to be set.
 	 */
-	if (host->busy_state == MMCI_BUSY_IDLE) {
+	case MMCI_BUSY_IDLE:
 		/*
 		 * Save the first status register read to be sure to catch
 		 * all bits that may be lost will retrying. If the command
@@ -713,8 +719,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		writel(readl(base + MMCIMASK0) &
 		       ~host->variant->busy_detect_mask, base + MMCIMASK0);
 		host->busy_state = MMCI_BUSY_DONE;
-		goto out_ret_state;
-	}
+		break;
 
 	/*
 	 * If there is a command in-progress that has been successfully
@@ -727,12 +732,11 @@ 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_state == MMCI_BUSY_WAITING_FOR_IRQS) {
+	case MMCI_BUSY_WAITING_FOR_IRQS:
 		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_START_IRQ;
-			goto out_ret_state;
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy start IRQ\n");
@@ -741,16 +745,14 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			       ~host->variant->busy_detect_mask, base + MMCIMASK0);
 			host->busy_state = MMCI_BUSY_DONE;
 			host->busy_status = 0;
-			goto out_ret_state;
 		}
-	}
+		break;
 
-	if (host->busy_state == MMCI_BUSY_START_IRQ) {
+	case MMCI_BUSY_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_END_IRQ;
-			goto out_ret_state;
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy end IRQ\n");
@@ -759,9 +761,8 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			       ~host->variant->busy_detect_mask, base + MMCIMASK0);
 			host->busy_state = MMCI_BUSY_DONE;
 			host->busy_status = 0;
-			goto out_ret_state;
 		}
-	}
+		break;
 
 	/*
 	 * If there is a command in-progress that has been successfully
@@ -769,11 +770,10 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 	 * the busy end IRQ. Clear and mask the IRQ, then continue to
 	 * process the command.
 	 */
-	if (host->busy_state == MMCI_BUSY_END_IRQ) {
+	case MMCI_BUSY_END_IRQ:
 		if (status & host->variant->busy_detect_flag) {
 			/* We should just get two IRQs for busy detect */
 			dev_err(mmc_dev(host->mmc), "spurious busy detect IRQ\n");
-			goto out_ret_state;
 		}
 
 		writel(host->variant->busy_detect_mask, base + MMCICLEAR);
@@ -782,9 +782,15 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 
 		host->busy_state = MMCI_BUSY_DONE;
 		host->busy_status = 0;
-	} else {
+		break;
+
+	case MMCI_BUSY_DONE:
+		break;
+
+	default:
 		dev_dbg(mmc_dev(host->mmc), "fell through on state %d\n",
 			host->busy_state);
+		break;
 	}
 
 out_ret_state:

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 09/12] mmc: mmci: Break out a helper function
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (7 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 08/12] mmc: mmci: Use a switch statement machine Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine Linus Walleij
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

These four lines clearing, masking and resetting the state
of the busy detect state machine is repeated five times in
the code so break this out to a small helper so things are
easier to read.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 42 ++++++++++++++++--------------------------
 1 file changed, 16 insertions(+), 26 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 76d885d7e49f..9a7f441ec9d6 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -654,6 +654,17 @@ static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host)
 	return MCI_DPSM_ENABLE | (host->data->blksz << 16);
 }
 
+static void ux500_busy_clear_mask_done(struct mmci_host *host)
+{
+	void __iomem *base = host->base;
+
+	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;
+}
+
 /*
  * ux500_busy_complete() - this will wait until the busy status
  * goes off, saving any status that occur in the meantime into
@@ -668,11 +679,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 
 	if (status & err_msk) {
 		/* Stop any ongoing busy detection if an error occurs */
-		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;
+		ux500_busy_clear_mask_done(host);
 		goto out_ret_state;
 	}
 
@@ -715,10 +722,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			retries--;
 		}
 		dev_dbg(mmc_dev(host->mmc), "no busy signalling in time\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;
+		ux500_busy_clear_mask_done(host);
 		break;
 
 	/*
@@ -740,11 +744,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		} 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;
+			ux500_busy_clear_mask_done(host);
 		}
 		break;
 
@@ -756,11 +756,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		} 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;
+			ux500_busy_clear_mask_done(host);
 		}
 		break;
 
@@ -775,13 +771,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			/* We should just get two IRQs for busy detect */
 			dev_err(mmc_dev(host->mmc), "spurious busy detect 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;
+		ux500_busy_clear_mask_done(host);
 		break;
 
 	case MMCI_BUSY_DONE:

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (8 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 09/12] mmc: mmci: Break out a helper function Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-17 14:48   ` Ulf Hansson
  2023-04-08 22:00 ` [PATCH v2 11/12] mmc: mmci: Drop end IRQ from Ux500 busydetect Linus Walleij
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

If we have a .busy_complete() callback, then check if
the state machine triggered from the busy detect interrupts
is busy: then we are certainly busy.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Do this in a safer way that falls back to reading busy
  status from the hardware if the state machine is NOT
  busy.
---
 drivers/mmc/host/mmci.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 9a7f441ec9d6..180a7b719347 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -339,6 +339,12 @@ static int mmci_card_busy(struct mmc_host *mmc)
 	unsigned long flags;
 	int busy = 0;
 
+	/* If we are waiting for IRQs we are certainly busy */
+	if (host->ops->busy_complete &&
+	    host->busy_state != MMCI_BUSY_IDLE &&
+	    host->busy_state != MMCI_BUSY_DONE)
+		return 1;
+
 	spin_lock_irqsave(&host->lock, flags);
 	if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)
 		busy = 1;

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 11/12] mmc: mmci: Drop end IRQ from Ux500 busydetect
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (9 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-08 22:00 ` [PATCH v2 12/12] mmc: mmci: Add busydetect timeout Linus Walleij
  2023-04-12  7:48 ` [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Yann Gautier
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

The Ux500 has these state transition edges:

IDLE -> WAITING_FOR_IRQS -> START_IRQ -> DONE

The first IRQ move us from WAITING_FOR_IRQS to START_IRQ
and the second IRQ moves us from START_IRQ to DONE.

This didn't come out until after all refactoring.

For STM32 we keep the END_IRQ state around, because that
is indeed what we are waiting for in that case.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 180a7b719347..17233702e7fb 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -758,7 +758,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		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_END_IRQ;
+			ux500_busy_clear_mask_done(host);
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy end IRQ\n");
@@ -766,20 +766,6 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		}
 		break;
 
-	/*
-	 * 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.
-	 */
-	case MMCI_BUSY_END_IRQ:
-		if (status & host->variant->busy_detect_flag) {
-			/* We should just get two IRQs for busy detect */
-			dev_err(mmc_dev(host->mmc), "spurious busy detect IRQ\n");
-		}
-		ux500_busy_clear_mask_done(host);
-		break;
-
 	case MMCI_BUSY_DONE:
 		break;
 

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 12/12] mmc: mmci: Add busydetect timeout
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (10 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 11/12] mmc: mmci: Drop end IRQ from Ux500 busydetect Linus Walleij
@ 2023-04-08 22:00 ` Linus Walleij
  2023-04-12  7:48 ` [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Yann Gautier
  12 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-08 22:00 UTC (permalink / raw)
  To: Yann Gautier, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32, Linus Walleij

Add a timeout for busydetect IRQs using a delayed work.
It might happen (and does happen) on Ux500 that the first
busy detect IRQ appears and not the second one. This will
make the host hang indefinitely waiting for the second
IRQ to appear.

Fire a delayed work after 10ms and re-engage the command
IRQ so the transaction finishes: we are certainly done
at this point, or we will catch an error in the status
register.

This makes the eMMC work again on Skomer.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 23 +++++++++++++++++++++++
 drivers/mmc/host/mmci.h |  1 +
 2 files changed, 24 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 17233702e7fb..1af129fba0ed 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -37,6 +37,7 @@
 #include <linux/pinctrl/consumer.h>
 #include <linux/reset.h>
 #include <linux/gpio/consumer.h>
+#include <linux/workqueue.h>
 
 #include <asm/div64.h>
 #include <asm/io.h>
@@ -747,6 +748,8 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 			host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
 			writel(host->variant->busy_detect_mask, base + MMCICLEAR);
 			host->busy_state = MMCI_BUSY_START_IRQ;
+			schedule_delayed_work(&host->busy_timeout_work,
+					      msecs_to_jiffies(10));
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy start IRQ\n");
@@ -758,6 +761,7 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
 		if (status & host->variant->busy_detect_flag) {
 			host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
 			writel(host->variant->busy_detect_mask, base + MMCICLEAR);
+			cancel_delayed_work_sync(&host->busy_timeout_work);
 			ux500_busy_clear_mask_done(host);
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
@@ -1498,6 +1502,22 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 	}
 }
 
+/*
+ * This busy timeout worker is used to "kick" the command IRQ if a
+ * busy detect IRQ fails to appear in reasonable time. Only used on
+ * variants with busy detection IRQ delivery.
+ */
+static void busy_timeout_work(struct work_struct *work)
+{
+	struct mmci_host *host =
+		container_of(work, struct mmci_host, busy_timeout_work.work);
+	u32 status;
+
+	dev_dbg(mmc_dev(host->mmc), "timeout waiting for busy IRQ\n");
+	status = readl(host->base + MMCISTATUS);
+	mmci_cmd_irq(host, host->cmd, status);
+}
+
 static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain)
 {
 	return remain - (readl(host->base + MMCIFIFOCNT) << 2);
@@ -2311,6 +2331,9 @@ static int mmci_probe(struct amba_device *dev,
 			goto clk_disable;
 	}
 
+	if (host->variant->busy_detect && host->ops->busy_complete)
+		INIT_DELAYED_WORK(&host->busy_timeout_work, busy_timeout_work);
+
 	writel(MCI_IRQENABLE | variant->start_err, host->base + MMCIMASK0);
 
 	amba_set_drvdata(dev, mmc);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 82f3850325c8..68ce7ea4d3b2 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -453,6 +453,7 @@ struct mmci_host {
 	void			*dma_priv;
 
 	s32			next_cookie;
+	struct delayed_work	busy_timeout_work;
 };
 
 #define dma_inprogress(host)	((host)->dma_in_progress)

-- 
2.39.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI
  2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (11 preceding siblings ...)
  2023-04-08 22:00 ` [PATCH v2 12/12] mmc: mmci: Add busydetect timeout Linus Walleij
@ 2023-04-12  7:48 ` Yann Gautier
  12 siblings, 0 replies; 20+ messages in thread
From: Yann Gautier @ 2023-04-12  7:48 UTC (permalink / raw)
  To: Linus Walleij, Stefan Hansson, Ulf Hansson, Maxime Coquelin,
	Alexandre Torgue
  Cc: linux-mmc, linux-stm32

On 4/9/23 00:00, Linus Walleij wrote:
> This series fixes a pretty serious problem in the MMCI
> busy detect handling, discovered only after going up and
> down a ladder of refactorings.
> 
> The code is written expecting the Ux500 busy detect
> to fire two interrupts: one at the start of the busy
> signalling and one at the end of the busy signalling.
> 
> The root cause of the problem seen on some devices
> is that only the first IRQ arrives, and then the device
> hangs, waiting perpetually for the next IRQ to arrive.
> 
> This is eventually solved by adding a timeout using
> a delayed work that fire after 10 ms if the busy detect
> has not stopped at this point. (Other delay spans can
> be suggested.) This is the last patch in the series.
> 
> I included the rewrite of the entire busy detect logic
> to use a state machine as this makes it way easier to
> debug and will print messages about other error
> conditions as well.
> 
> The problem affects especially the Skomer
> (Samsung GT-I9070) and Kyle (Samsung SGH-I407).
> 
> It is fine to just apply this for the -next kernel,
> despite it fixes the busy detect that has been broken
> for these devices for a while, we can think about
> backporting a simpler version of the timeout for
> stable kernels if we want.
> 
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> Changes in v2:
> - Drop pointless patch nr 1
> - Unconditionally intialize some state variables
> - Use a less fragile method to look for busy status when
>    using busy detect, should fix Yann's problem

Hi Linus,

Thanks for the update, it is now OK on STM32MP1.
For the series, you can add my:
Reviewed-by: Yann Gautier <yann.gautier@foss.st.com>


Best regards,
Yann

> - Link to v1: https://lore.kernel.org/r/20230405-pl180-busydetect-fix-v1-0-28ac19a74e5e@linaro.org
> 
> ---
> Linus Walleij (12):
>        mmc: mmci: Clear busy_status when starting command
>        mmc: mmci: Unwind big if() clause
>        mmc: mmci: Stash status while waiting for busy
>        mmc: mmci: Break out error check in busy detect
>        mmc: mmci: Make busy complete state machine explicit
>        mmc: mmci: Retry the busy start condition
>        mmc: mmci: Use state machine state as exit condition
>        mmc: mmci: Use a switch statement machine
>        mmc: mmci: Break out a helper function
>        mmc: mmci: mmci_card_busy() from state machine
>        mmc: mmci: Drop end IRQ from Ux500 busydetect
>        mmc: mmci: Add busydetect timeout
> 
>   drivers/mmc/host/mmci.c             | 166 ++++++++++++++++++++++++++++--------
>   drivers/mmc/host/mmci.h             |  17 ++++
>   drivers/mmc/host/mmci_stm32_sdmmc.c |   6 +-
>   3 files changed, 151 insertions(+), 38 deletions(-)
> ---
> base-commit: fe15c26ee26efa11741a7b632e9f23b01aca4cc6
> change-id: 20230405-pl180-busydetect-fix-66a0360d398a
> 
> Best regards,


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 01/12] mmc: mmci: Clear busy_status when starting command
  2023-04-08 22:00 ` [PATCH v2 01/12] mmc: mmci: Clear busy_status when starting command Linus Walleij
@ 2023-04-17 13:06   ` Ulf Hansson
  0 siblings, 0 replies; 20+ messages in thread
From: Ulf Hansson @ 2023-04-17 13:06 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Sun, 9 Apr 2023 at 00:00, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> If we are starting a command which can generate a busy
> response, then clear the variable host->busy_status
> if the variant is using a ->busy_complete callback.
>
> We are lucky that the member is zero by default and
> hopefully always gets cleared in the ->busy_complete
> callback even on errors, but it's just fragile so
> make sure it is always initialized to zero.
>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> ChangeLog v1->v2:
> - Unconditionally clear host->busy_status if we get a
>   busy response.
> ---
>  drivers/mmc/host/mmci.c | 22 +++++++++++++---------
>  1 file changed, 13 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index b9e5dfe74e5c..9b48df842425 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -1238,17 +1238,21 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
>                         c |= host->variant->cmdreg_srsp;
>         }
>
> -       if (host->variant->busy_timeout && cmd->flags & MMC_RSP_BUSY) {
> -               if (!cmd->busy_timeout)
> -                       cmd->busy_timeout = 10 * MSEC_PER_SEC;
> +       if (cmd->flags & MMC_RSP_BUSY) {
> +               host->busy_status = 0;

To be even more safe, I don't think we should check "cmd->flags &
MMC_RSP_BUSY". Let's just reset it always instead.

>
> -               if (cmd->busy_timeout > host->mmc->max_busy_timeout)
> -                       clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk;
> -               else
> -                       clks = (unsigned long long)cmd->busy_timeout * host->cclk;
> +               if (host->variant->busy_timeout) {
> +                       if (!cmd->busy_timeout)
> +                               cmd->busy_timeout = 10 * MSEC_PER_SEC;
>
> -               do_div(clks, MSEC_PER_SEC);
> -               writel_relaxed(clks, host->base + MMCIDATATIMER);
> +                       if (cmd->busy_timeout > host->mmc->max_busy_timeout)
> +                               clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk;
> +                       else
> +                               clks = (unsigned long long)cmd->busy_timeout * host->cclk;
> +
> +                       do_div(clks, MSEC_PER_SEC);
> +                       writel_relaxed(clks, host->base + MMCIDATATIMER);
> +               }
>         }
>
>         if (host->ops->pre_sig_volt_switch && cmd->opcode == SD_SWITCH_VOLTAGE)
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 04/12] mmc: mmci: Break out error check in busy detect
  2023-04-08 22:00 ` [PATCH v2 04/12] mmc: mmci: Break out error check in busy detect Linus Walleij
@ 2023-04-17 13:23   ` Ulf Hansson
  2023-04-17 15:45     ` Linus Walleij
  0 siblings, 1 reply; 20+ messages in thread
From: Ulf Hansson @ 2023-04-17 13:23 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Sun, 9 Apr 2023 at 00:00, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> The busy detect callback for Ux500 checks for an error
> in the status in the first if() clause. The only practical
> reason is that if an error occurs, the if()-clause is not
> executed, and the code falls through to the last
> if()-clause if (host->busy_status) which will clear and
> disable the irq. Make this explicit instead: it is easier
> to read.
>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> ChangeLog v1->v2:
> - No changes
> ---
>  drivers/mmc/host/mmci.c | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index e742dedaca1a..7d42625f2356 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -665,6 +665,15 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
>  {
>         void __iomem *base = host->base;
>
> +       if (status & err_msk) {
> +               /* Stop any ongoing busy detection if an error occurs */
> +               writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> +               writel(readl(base + MMCIMASK0) &
> +                      ~host->variant->busy_detect_mask, base + MMCIMASK0);
> +               host->busy_status = 0;
> +               return true;

I agree that this makes the code more explicit, but unfortunately it
also means duplicating the code from the bottom of this function.

Can you instead add a helper function and call it from here and from
the part at the bottom?

> +       }
> +
>         /*
>          * Before unmasking for the busy end IRQ, confirm that the
>          * command was sent successfully. To keep track of having a
> @@ -678,7 +687,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 && !(status & err_msk)) {
> +       if (!host->busy_status) {
>                 status = readl(base + MMCISTATUS);
>                 if (status & host->variant->busy_detect_flag) {
>                         writel(readl(base + MMCIMASK0) |
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 05/12] mmc: mmci: Make busy complete state machine explicit
  2023-04-08 22:00 ` [PATCH v2 05/12] mmc: mmci: Make busy complete state machine explicit Linus Walleij
@ 2023-04-17 14:44   ` Ulf Hansson
  0 siblings, 0 replies; 20+ messages in thread
From: Ulf Hansson @ 2023-04-17 14:44 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Sun, 9 Apr 2023 at 00:00, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> This refactors the ->busy_complete() callback currently
> only used by Ux500 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.

Nitpick: The callback is being used by the mmci_stm32_sdmmc variant too.

>
> 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 four states:
> MMCI_BUSY_NOT_STARTED, MMCI_BUSY_WAITING_FOR_IRQS,
> MMCI_BUSY_START_IRQ and MMCI_BUSY_END_IRQ.

This makes perfect sense to me too. However, these don't really match
the names of enum types you eventually decided to use.

To be consistent, perhaps it's just easier to drop the actual enum
names from the commit message?

>
> The 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 dev_err() 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.

I agree, this whole thing has been fragile. Your observations seem
reasonable to me too.

>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> ChangeLog v1->v2:
> - No changes
> ---
>  drivers/mmc/host/mmci.c             | 55 +++++++++++++++++++++++++++++++------
>  drivers/mmc/host/mmci.h             | 16 +++++++++++
>  drivers/mmc/host/mmci_stm32_sdmmc.c |  6 +++-
>  3 files changed, 68 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 7d42625f2356..887b83e392a4 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_IDLE) {
>                 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_IRQS;
>                         return false;
>                 }
>         }
> @@ -710,11 +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_IRQS) {
> +               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_START_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 (host->busy_state == MMCI_BUSY_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_END_IRQ;
> +                       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;
> +               }
>         }
>
>         /*
> @@ -723,11 +754,18 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
>          * 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);
> +       if (host->busy_state == MMCI_BUSY_END_IRQ) {
> +               if (status & host->variant->busy_detect_flag) {
> +                       /* We should just get two IRQs for busy detect */
> +                       dev_err(mmc_dev(host->mmc), "spurious busy detect IRQ\n");
> +                       return false;
> +               }
>
> +               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;
>         }
>
> @@ -1258,6 +1296,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
>         }
>
>         if (cmd->flags & MMC_RSP_BUSY) {
> +               host->busy_state = MMCI_BUSY_IDLE;
>                 host->busy_status = 0;
>
>                 if (host->variant->busy_timeout) {
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index e1a9b96a3396..82f3850325c8 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -261,6 +261,21 @@ struct clk;
>  struct dma_chan;
>  struct mmci_host;
>
> +/**
> + * enum mmci_busy_state - enumerate the busy detect wait states
> + *
> + * This is used for the state machine waiting for different busy detect
> + * interrupts on hardware that fire a single IRQ for start and end of
> + * the busy detect phase on DAT0.
> + */
> +enum mmci_busy_state {
> +       MMCI_BUSY_IDLE,

This state name is a bit confusing to me. If I understand correctly,
this state means that the sent command has a corresponding busy
signaling that should be checked for, right? Can we perhaps reflect
that in the name somehow?

> +       MMCI_BUSY_WAITING_FOR_IRQS,
> +       MMCI_BUSY_START_IRQ,
> +       MMCI_BUSY_END_IRQ,
> +       MMCI_BUSY_DONE,
> +};
> +
>  /**
>   * struct variant_data - MMCI variant-specific quirks
>   * @clkreg: default value for MCICLOCK register
> @@ -409,6 +424,7 @@ struct mmci_host {
>         u32                     clk_reg;
>         u32                     clk_reg_add;
>         u32                     datactrl_reg;
> +       enum mmci_busy_state    busy_state;
>         u32                     busy_status;
>         u32                     mask1_reg;
>         u8                      vqmmc_enabled:1;
> diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c
> index 60bca78a72b1..24831a1092b2 100644
> --- a/drivers/mmc/host/mmci_stm32_sdmmc.c
> +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c
> @@ -393,8 +393,10 @@ static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
>         busy_d0 = sdmmc_status & MCI_STM32_BUSYD0;
>
>         /* complete if there is an error or busy_d0end */
> -       if ((status & err_msk) || busy_d0end)
> +       if ((status & err_msk) || busy_d0end) {
> +               host->busy_state = MMCI_BUSY_DONE;
>                 goto complete;
> +       }
>
>         /*
>          * On response the busy signaling is reflected in the BUSYD0 flag.
> @@ -408,6 +410,7 @@ static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
>                         host->busy_status = status &
>                                 (MCI_CMDSENT | MCI_CMDRESPEND);
>                 }
> +               host->busy_state = MMCI_BUSY_END_IRQ;
>                 return false;
>         }
>
> @@ -416,6 +419,7 @@ static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
>                 writel_relaxed(mask & ~host->variant->busy_detect_mask,
>                                base + MMCIMASK0);
>                 host->busy_status = 0;
> +               host->busy_state = MMCI_BUSY_DONE;
>         }
>
>         writel_relaxed(host->variant->busy_detect_mask, base + MMCICLEAR);
>

Overall, I have to admit that I think the code is getting harder to
follow again, even if the end result from the series looks really
nice.

Besides making sure that each step/patch compiles, I am also worried
that we may be breaking things on the way. That said, I wonder if it's
not better to actually squash some of the patches in the series, to
avoid churns - can you please look into that?

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine
  2023-04-08 22:00 ` [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine Linus Walleij
@ 2023-04-17 14:48   ` Ulf Hansson
  2023-04-17 15:47     ` Linus Walleij
  0 siblings, 1 reply; 20+ messages in thread
From: Ulf Hansson @ 2023-04-17 14:48 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Sun, 9 Apr 2023 at 00:00, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> If we have a .busy_complete() callback, then check if
> the state machine triggered from the busy detect interrupts
> is busy: then we are certainly busy.
>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> ChangeLog v1->v2:
> - Do this in a safer way that falls back to reading busy
>   status from the hardware if the state machine is NOT
>   busy.
> ---
>  drivers/mmc/host/mmci.c | 6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 9a7f441ec9d6..180a7b719347 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -339,6 +339,12 @@ static int mmci_card_busy(struct mmc_host *mmc)
>         unsigned long flags;
>         int busy = 0;
>
> +       /* If we are waiting for IRQs we are certainly busy */
> +       if (host->ops->busy_complete &&
> +           host->busy_state != MMCI_BUSY_IDLE &&
> +           host->busy_state != MMCI_BUSY_DONE)
> +               return 1;

This looks fishy to me.

If this is needed, that means that the mmc core is calling the
->card_busy() ops in the middle of a request that has not been
completed yet. This shouldn't happen - unless I am misunderstanding
some part of the internal new state machine.

> +
>         spin_lock_irqsave(&host->lock, flags);
>         if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)
>                 busy = 1;
>
> --
> 2.39.2
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 04/12] mmc: mmci: Break out error check in busy detect
  2023-04-17 13:23   ` Ulf Hansson
@ 2023-04-17 15:45     ` Linus Walleij
  0 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-17 15:45 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Mon, Apr 17, 2023 at 3:24 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> On Sun, 9 Apr 2023 at 00:00, Linus Walleij <linus.walleij@linaro.org> wrote:
> >
> > The busy detect callback for Ux500 checks for an error
> > in the status in the first if() clause. The only practical
> > reason is that if an error occurs, the if()-clause is not
> > executed, and the code falls through to the last
> > if()-clause if (host->busy_status) which will clear and
> > disable the irq. Make this explicit instead: it is easier
> > to read.
> >
> > Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> > ---
> > ChangeLog v1->v2:
> > - No changes
> > ---
> >  drivers/mmc/host/mmci.c | 11 ++++++++++-
> >  1 file changed, 10 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> > index e742dedaca1a..7d42625f2356 100644
> > --- a/drivers/mmc/host/mmci.c
> > +++ b/drivers/mmc/host/mmci.c
> > @@ -665,6 +665,15 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
> >  {
> >         void __iomem *base = host->base;
> >
> > +       if (status & err_msk) {
> > +               /* Stop any ongoing busy detection if an error occurs */
> > +               writel(host->variant->busy_detect_mask, base + MMCICLEAR);
> > +               writel(readl(base + MMCIMASK0) &
> > +                      ~host->variant->busy_detect_mask, base + MMCIMASK0);
> > +               host->busy_status = 0;
> > +               return true;
>
> I agree that this makes the code more explicit, but unfortunately it
> also means duplicating the code from the bottom of this function.
>
> Can you instead add a helper function and call it from here and from
> the part at the bottom?

I break that out as a separate function in patch 9, can you check if
that solves the problem? The end result after all the patches should
be fine.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine
  2023-04-17 14:48   ` Ulf Hansson
@ 2023-04-17 15:47     ` Linus Walleij
  0 siblings, 0 replies; 20+ messages in thread
From: Linus Walleij @ 2023-04-17 15:47 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Mon, Apr 17, 2023 at 4:49 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> On Sun, 9 Apr 2023 at 00:00, Linus Walleij <linus.walleij@linaro.org> wrote:
> >
> > If we have a .busy_complete() callback, then check if
> > the state machine triggered from the busy detect interrupts
> > is busy: then we are certainly busy.
> >
> > Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> > ---
> > ChangeLog v1->v2:
> > - Do this in a safer way that falls back to reading busy
> >   status from the hardware if the state machine is NOT
> >   busy.
> > ---
> >  drivers/mmc/host/mmci.c | 6 ++++++
> >  1 file changed, 6 insertions(+)
> >
> > diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> > index 9a7f441ec9d6..180a7b719347 100644
> > --- a/drivers/mmc/host/mmci.c
> > +++ b/drivers/mmc/host/mmci.c
> > @@ -339,6 +339,12 @@ static int mmci_card_busy(struct mmc_host *mmc)
> >         unsigned long flags;
> >         int busy = 0;
> >
> > +       /* If we are waiting for IRQs we are certainly busy */
> > +       if (host->ops->busy_complete &&
> > +           host->busy_state != MMCI_BUSY_IDLE &&
> > +           host->busy_state != MMCI_BUSY_DONE)
> > +               return 1;
>
> This looks fishy to me.
>
> If this is needed, that means that the mmc core is calling the
> ->card_busy() ops in the middle of a request that has not been
> completed yet. This shouldn't happen - unless I am misunderstanding
> some part of the internal new state machine.

You're probably right about that, I have no idea when the core can
and cannot call ->card_busy, I just assumed it could be called at
any time (even while waiting for busy interrupts). If you say it won't
get called then this patch isn't needed.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2023-04-17 15:47 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-08 22:00 [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
2023-04-08 22:00 ` [PATCH v2 01/12] mmc: mmci: Clear busy_status when starting command Linus Walleij
2023-04-17 13:06   ` Ulf Hansson
2023-04-08 22:00 ` [PATCH v2 02/12] mmc: mmci: Unwind big if() clause Linus Walleij
2023-04-08 22:00 ` [PATCH v2 03/12] mmc: mmci: Stash status while waiting for busy Linus Walleij
2023-04-08 22:00 ` [PATCH v2 04/12] mmc: mmci: Break out error check in busy detect Linus Walleij
2023-04-17 13:23   ` Ulf Hansson
2023-04-17 15:45     ` Linus Walleij
2023-04-08 22:00 ` [PATCH v2 05/12] mmc: mmci: Make busy complete state machine explicit Linus Walleij
2023-04-17 14:44   ` Ulf Hansson
2023-04-08 22:00 ` [PATCH v2 06/12] mmc: mmci: Retry the busy start condition Linus Walleij
2023-04-08 22:00 ` [PATCH v2 07/12] mmc: mmci: Use state machine state as exit condition Linus Walleij
2023-04-08 22:00 ` [PATCH v2 08/12] mmc: mmci: Use a switch statement machine Linus Walleij
2023-04-08 22:00 ` [PATCH v2 09/12] mmc: mmci: Break out a helper function Linus Walleij
2023-04-08 22:00 ` [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine Linus Walleij
2023-04-17 14:48   ` Ulf Hansson
2023-04-17 15:47     ` Linus Walleij
2023-04-08 22:00 ` [PATCH v2 11/12] mmc: mmci: Drop end IRQ from Ux500 busydetect Linus Walleij
2023-04-08 22:00 ` [PATCH v2 12/12] mmc: mmci: Add busydetect timeout Linus Walleij
2023-04-12  7:48 ` [PATCH v2 00/12] Fix busydetect on Ux500 PL180/MMCI Yann Gautier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.