linux-mmc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI
@ 2023-06-11 19:41 Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 01/10] mmc: mmci: Clear busy_status when starting command Linus Walleij
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v3:
- Unconditionally assign busy_status = 0
- Rewrite state machine states to just three
- Drop a patch that gets absorbed into another patch
- Drop patch to get busy state from the state machine, it was
  fishy, based on a misunderstanding and not needed
- Link to v2: https://lore.kernel.org/r/20230405-pl180-busydetect-fix-v2-0-eeb10323b546@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 (10):
      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: Add busydetect timeout

 drivers/mmc/host/mmci.c             | 139 ++++++++++++++++++++++++++++--------
 drivers/mmc/host/mmci.h             |  15 ++++
 drivers/mmc/host/mmci_stm32_sdmmc.c |   6 +-
 3 files changed, 130 insertions(+), 30 deletions(-)
---
base-commit: ac9a78681b921877518763ba0e89202254349d1b
change-id: 20230405-pl180-busydetect-fix-66a0360d398a

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


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

* [PATCH v3 01/10] mmc: mmci: Clear busy_status when starting command
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-13 12:07   ` Ulf Hansson
  2023-06-11 19:41 ` [PATCH v3 02/10] mmc: mmci: Unwind big if() clause Linus Walleij
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Clear host->busy_status no matter if the MMC_RSP_BUSY flag
  is set or not.
- Now we have an if inside an if left, so combine these into
  one singel conditional.
- Resulting re-flow the if-clause.
ChangeLog v1->v2:
- Unconditionally clear host->busy_status if we get a
  busy response.
---
 drivers/mmc/host/mmci.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index f2b2e8b0574e..14d639872952 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1238,7 +1238,8 @@ 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) {
+	host->busy_status = 0;
+	if ((cmd->flags & MMC_RSP_BUSY) && (host->variant->busy_timeout)) {
 		if (!cmd->busy_timeout)
 			cmd->busy_timeout = 10 * MSEC_PER_SEC;
 

-- 
2.40.1


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

* [PATCH v3 02/10] mmc: mmci: Unwind big if() clause
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 01/10] mmc: mmci: Clear busy_status when starting command Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 03/10] mmc: mmci: Stash status while waiting for busy Linus Walleij
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
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 14d639872952..d8e7b17adaee 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.40.1


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

* [PATCH v3 03/10] mmc: mmci: Stash status while waiting for busy
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 01/10] mmc: mmci: Clear busy_status when starting command Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 02/10] mmc: mmci: Unwind big if() clause Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 04/10] mmc: mmci: Break out error check in busy detect Linus Walleij
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
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 d8e7b17adaee..73b5205592ae 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.40.1


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

* [PATCH v3 04/10] mmc: mmci: Break out error check in busy detect
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (2 preceding siblings ...)
  2023-06-11 19:41 ` [PATCH v3 03/10] mmc: mmci: Stash status while waiting for busy Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 05/10] mmc: mmci: Make busy complete state machine explicit Linus Walleij
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
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 73b5205592ae..fbc13923f4fe 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.40.1


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

* [PATCH v3 05/10] mmc: mmci: Make busy complete state machine explicit
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (3 preceding siblings ...)
  2023-06-11 19:41 ` [PATCH v3 04/10] mmc: mmci: Break out error check in busy detect Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-13 12:08   ` Ulf Hansson
  2023-06-11 19:41 ` [PATCH v3 06/10] mmc: mmci: Retry the busy start condition Linus Walleij
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 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@linaro.org>
---
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 fbc13923f4fe..edc68fc7b642 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) {
+			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;
+		}
 	}
 
 	return true;
@@ -1259,6 +1276,8 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
 
 	host->busy_status = 0;
 	if ((cmd->flags & MMC_RSP_BUSY) && (host->variant->busy_timeout)) {
+		host->busy_state = MMCI_BUSY_DONE;
+
 		if (!cmd->busy_timeout)
 			cmd->busy_timeout = 10 * MSEC_PER_SEC;
 
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index e1a9b96a3396..12a7bbd3ce26 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -261,6 +261,19 @@ 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_WAITING_FOR_START_IRQ,
+	MMCI_BUSY_WAITING_FOR_END_IRQ,
+	MMCI_BUSY_DONE,
+};
+
 /**
  * struct variant_data - MMCI variant-specific quirks
  * @clkreg: default value for MCICLOCK register
@@ -409,6 +422,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..a805647b6664 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_WAITING_FOR_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.40.1


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

* [PATCH v3 06/10] mmc: mmci: Retry the busy start condition
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (4 preceding siblings ...)
  2023-06-11 19:41 ` [PATCH v3 05/10] mmc: mmci: Make busy complete state machine explicit Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-13 12:08   ` Ulf Hansson
  2023-06-11 19:41 ` [PATCH v3 07/10] mmc: mmci: Use state machine state as exit condition Linus Walleij
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
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 edc68fc7b642..2858d8d67129 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_DONE) {
-		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_START_IRQ;
-			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_START_IRQ;
+				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.40.1


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

* [PATCH v3 07/10] mmc: mmci: Use state machine state as exit condition
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (5 preceding siblings ...)
  2023-06-11 19:41 ` [PATCH v3 06/10] mmc: mmci: Retry the busy start condition Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 08/10] mmc: mmci: Use a switch statement machine Linus Walleij
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 2858d8d67129..fe928f8f4d8f 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_START_IRQ;
-				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_WAITING_FOR_END_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_DONE;
-			return false;
+			goto out_ret_state;
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy end IRQ\n");
@@ -760,11 +759,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;
-			return true;
+			goto out_ret_state;
 		}
 	}
 
 	return true;
+
+out_ret_state:
+	return (host->busy_state == MMCI_BUSY_DONE);
 }
 
 /*

-- 
2.40.1


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

* [PATCH v3 08/10] mmc: mmci: Use a switch statement machine
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (6 preceding siblings ...)
  2023-06-11 19:41 ` [PATCH v3 07/10] mmc: mmci: Use state machine state as exit condition Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 09/10] mmc: mmci: Break out a helper function Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 10/10] mmc: mmci: Add busydetect timeout Linus Walleij
  9 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index fe928f8f4d8f..e80dd7e7a001 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_DONE) {
+	case MMCI_BUSY_DONE:
 		/*
 		 * 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_START_IRQ) {
+	case 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;
-			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_WAITING_FOR_END_IRQ) {
+	case MMCI_BUSY_WAITING_FOR_END_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_DONE;
-			goto out_ret_state;
 		} else {
 			dev_dbg(mmc_dev(host->mmc),
 				"lost busy status when waiting for busy end IRQ\n");
@@ -759,11 +761,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;
 
-	return true;
+	default:
+		dev_dbg(mmc_dev(host->mmc), "fell through on state %d\n",
+			host->busy_state);
+		break;
+	}
 
 out_ret_state:
 	return (host->busy_state == MMCI_BUSY_DONE);

-- 
2.40.1


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

* [PATCH v3 09/10] mmc: mmci: Break out a helper function
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (7 preceding siblings ...)
  2023-06-11 19:41 ` [PATCH v3 08/10] mmc: mmci: Use a switch statement machine Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-11 19:41 ` [PATCH v3 10/10] mmc: mmci: Add busydetect timeout Linus Walleij
  9 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
ChangeLog v1->v2:
- No changes
---
 drivers/mmc/host/mmci.c | 36 ++++++++++++++++--------------------
 1 file changed, 16 insertions(+), 20 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index e80dd7e7a001..05b8fad26c10 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;
 
@@ -752,15 +752,11 @@ 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_DONE;
+			ux500_busy_clear_mask_done(host);
 		} 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;
 

-- 
2.40.1


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

* [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-11 19:41 [PATCH v3 00/10] Fix busydetect on Ux500 PL180/MMCI Linus Walleij
                   ` (8 preceding siblings ...)
  2023-06-11 19:41 ` [PATCH v3 09/10] mmc: mmci: Break out a helper function Linus Walleij
@ 2023-06-11 19:41 ` Linus Walleij
  2023-06-13 12:07   ` Ulf Hansson
  9 siblings, 1 reply; 21+ messages in thread
From: Linus Walleij @ 2023-06-11 19:41 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 v2->v3:
- Rebased.
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 05b8fad26c10..7e40b8f2dbf3 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>
@@ -741,6 +742,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_WAITING_FOR_END_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");
@@ -752,6 +755,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),
@@ -1487,6 +1491,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);
@@ -2300,6 +2320,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 12a7bbd3ce26..95d3d0a6049b 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -451,6 +451,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.40.1


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

* Re: [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-11 19:41 ` [PATCH v3 10/10] mmc: mmci: Add busydetect timeout Linus Walleij
@ 2023-06-13 12:07   ` Ulf Hansson
  2023-06-13 20:32     ` Linus Walleij
  0 siblings, 1 reply; 21+ messages in thread
From: Ulf Hansson @ 2023-06-13 12:07 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Sun, 11 Jun 2023 at 21:41, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> 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.

A fixed value of 10ms doesn't work. We have lots of commands that are
associated with way longer timeouts than this.

Typically, the cmd->busy_timeout contains the current value of the
timeout that should be used for the commands that have the flags
MMC_RSP_BUSY set for it.

The stm variant already uses cmd->busy_timeout, but also assigns a
default timeout, just to make sure if the core has failed to set
cmd->busy_timeout (it shouldn't but who knows).

A few more more comments to code, see below.

>
> This makes the eMMC work again on Skomer.
>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> ChangeLog v2->v3:
> - Rebased.
> 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 05b8fad26c10..7e40b8f2dbf3 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>
> @@ -741,6 +742,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_WAITING_FOR_END_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");
> @@ -752,6 +755,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),
> @@ -1487,6 +1491,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);

There's no locking here. I assume that's because we call
cancel_delayed_work_sync() from an atomic context and we don't want
that to hang.

However, can't the call to mmci_cmd_irq() race with a proper irq being
managed in parallel?

> +}
> +
>  static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain)
>  {
>         return remain - (readl(host->base + MMCIFIFOCNT) << 2);
> @@ -2300,6 +2320,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 12a7bbd3ce26..95d3d0a6049b 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -451,6 +451,7 @@ struct mmci_host {
>         void                    *dma_priv;
>
>         s32                     next_cookie;
> +       struct delayed_work     busy_timeout_work;
>  };
>
>  #define dma_inprogress(host)   ((host)->dma_in_progress)
>

Kind regards
Uffe

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

* Re: [PATCH v3 01/10] mmc: mmci: Clear busy_status when starting command
  2023-06-11 19:41 ` [PATCH v3 01/10] mmc: mmci: Clear busy_status when starting command Linus Walleij
@ 2023-06-13 12:07   ` Ulf Hansson
  0 siblings, 0 replies; 21+ messages in thread
From: Ulf Hansson @ 2023-06-13 12:07 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Sun, 11 Jun 2023 at 21:41, 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 v2->v3:
> - Clear host->busy_status no matter if the MMC_RSP_BUSY flag
>   is set or not.
> - Now we have an if inside an if left, so combine these into
>   one singel conditional.
> - Resulting re-flow the if-clause.
> ChangeLog v1->v2:
> - Unconditionally clear host->busy_status if we get a
>   busy response.
> ---
>  drivers/mmc/host/mmci.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index f2b2e8b0574e..14d639872952 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -1238,7 +1238,8 @@ 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) {
> +       host->busy_status = 0;
> +       if ((cmd->flags & MMC_RSP_BUSY) && (host->variant->busy_timeout)) {

Why are you changing this line? There is no need to add the extra
parentheses, right?

>                 if (!cmd->busy_timeout)
>                         cmd->busy_timeout = 10 * MSEC_PER_SEC;
>
>

Kind regards
Uffe

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

* Re: [PATCH v3 05/10] mmc: mmci: Make busy complete state machine explicit
  2023-06-11 19:41 ` [PATCH v3 05/10] mmc: mmci: Make busy complete state machine explicit Linus Walleij
@ 2023-06-13 12:08   ` Ulf Hansson
  0 siblings, 0 replies; 21+ messages in thread
From: Ulf Hansson @ 2023-06-13 12:08 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

[...]

> @@ -1259,6 +1276,8 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
>
>         host->busy_status = 0;
>         if ((cmd->flags & MMC_RSP_BUSY) && (host->variant->busy_timeout)) {
> +               host->busy_state = MMCI_BUSY_DONE;
> +

Nitpick:

I would move this up a few lines, along when clearing the busy_status.
In other words, let's clear the busy_state no matter if the current
command flag has MMC_RSP_BUSY.

>                 if (!cmd->busy_timeout)
>                         cmd->busy_timeout = 10 * MSEC_PER_SEC;
>

[...]

Kind regards
Uffe

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

* Re: [PATCH v3 06/10] mmc: mmci: Retry the busy start condition
  2023-06-11 19:41 ` [PATCH v3 06/10] mmc: mmci: Retry the busy start condition Linus Walleij
@ 2023-06-13 12:08   ` Ulf Hansson
  0 siblings, 0 replies; 21+ messages in thread
From: Ulf Hansson @ 2023-06-13 12:08 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Sun, 11 Jun 2023 at 21:41, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> 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 v2->v3:
> - Rebased.
> 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 edc68fc7b642..2858d8d67129 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_DONE) {
> -               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_START_IRQ;
> -                       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);

Looks like we should accumulate the status, no matter whether the
host->variant->busy_detect_flag is set?

Perhaps move this just before the if-clause above?

> +                               host->busy_state = MMCI_BUSY_WAITING_FOR_START_IRQ;
> +                               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;
>         }
>

Kind regards
Uffe

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

* Re: [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-13 12:07   ` Ulf Hansson
@ 2023-06-13 20:32     ` Linus Walleij
  2023-06-14 10:04       ` Ulf Hansson
  0 siblings, 1 reply; 21+ messages in thread
From: Linus Walleij @ 2023-06-13 20:32 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Tue, Jun 13, 2023 at 2:08 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:

> Typically, the cmd->busy_timeout contains the current value of the
> timeout that should be used for the commands that have the flags
> MMC_RSP_BUSY set for it.
>
> The stm variant already uses cmd->busy_timeout, but also assigns a
> default timeout, just to make sure if the core has failed to set
> cmd->busy_timeout (it shouldn't but who knows).

I generalized the STM32 solution with the core-specified timeout
and default and used that.

If we know the core will always provide correct timeouts we should
probably delete hacks like this though (but that would be a separate
patch).

> > +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);
>
> There's no locking here. I assume that's because we call
> cancel_delayed_work_sync() from an atomic context and we don't want
> that to hang.
>
> However, can't the call to mmci_cmd_irq() race with a proper irq being
> managed in parallel?

It will not be a problem AFAIC: the MMCI is using level IRQs, meaning it
will handle all flags that are set for command or data IRQs before exiting
the IRQ handler, the order of the IRQ handling if several fire at the same
time is actually dependent on the order the IRQ flags are checked in the
code.

When the interrupt handler exits, all IRQs should be handled and the
respective IRQ lines and thus the IRQ from the MMCI should be
de-asserted.

In this case, our problem is that an interrupt is not fired at all, but if
the actual IRQ (that should have been fired) or a totally different IRQ
(such as an error) is fired at the same time doesn't matter: the pending
IRQs will be handled, and if the timer then fires an additional IRQ
the IRQ handler will check if there are any IRQs to handle and then
conclude there isn't and then just return.

At least the way I read it.

I made a v4, sending it out so you can check!

Yours,
Linus Walleij

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

* Re: [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-13 20:32     ` Linus Walleij
@ 2023-06-14 10:04       ` Ulf Hansson
  2023-06-14 11:17         ` Linus Walleij
  0 siblings, 1 reply; 21+ messages in thread
From: Ulf Hansson @ 2023-06-14 10:04 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Tue, 13 Jun 2023 at 22:32, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> On Tue, Jun 13, 2023 at 2:08 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> > Typically, the cmd->busy_timeout contains the current value of the
> > timeout that should be used for the commands that have the flags
> > MMC_RSP_BUSY set for it.
> >
> > The stm variant already uses cmd->busy_timeout, but also assigns a
> > default timeout, just to make sure if the core has failed to set
> > cmd->busy_timeout (it shouldn't but who knows).
>
> I generalized the STM32 solution with the core-specified timeout
> and default and used that.
>
> If we know the core will always provide correct timeouts we should
> probably delete hacks like this though (but that would be a separate
> patch).
>
> > > +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);
> >
> > There's no locking here. I assume that's because we call
> > cancel_delayed_work_sync() from an atomic context and we don't want
> > that to hang.
> >
> > However, can't the call to mmci_cmd_irq() race with a proper irq being
> > managed in parallel?
>
> It will not be a problem AFAIC: the MMCI is using level IRQs, meaning it
> will handle all flags that are set for command or data IRQs before exiting
> the IRQ handler, the order of the IRQ handling if several fire at the same
> time is actually dependent on the order the IRQ flags are checked in the
> code.
>
> When the interrupt handler exits, all IRQs should be handled and the
> respective IRQ lines and thus the IRQ from the MMCI should be
> de-asserted.

Right, I think I follow.

>
> In this case, our problem is that an interrupt is not fired at all, but if
> the actual IRQ (that should have been fired) or a totally different IRQ
> (such as an error) is fired at the same time doesn't matter: the pending
> IRQs will be handled, and if the timer then fires an additional IRQ
> the IRQ handler will check if there are any IRQs to handle and then
> conclude there isn't and then just return.

To clarify, I am not worried about the IRQ handling as such.

However, we use the spin_lock to protect some members in the struct
mmci_host. In this case, the mmci_cmd_irq() is using "host->cmd" to
understand whether there is an active command to manage. When the
command has been completed, we set host->cmd to NULL.

[...]

Kind regards
Uffe

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

* Re: [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-14 10:04       ` Ulf Hansson
@ 2023-06-14 11:17         ` Linus Walleij
  2023-06-14 12:21           ` Ulf Hansson
  0 siblings, 1 reply; 21+ messages in thread
From: Linus Walleij @ 2023-06-14 11:17 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Wed, Jun 14, 2023 at 12:05 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:

> However, we use the spin_lock to protect some members in the struct
> mmci_host. In this case, the mmci_cmd_irq() is using "host->cmd" to
> understand whether there is an active command to manage. When the
> command has been completed, we set host->cmd to NULL.

Hm right...

I'm leaning toward some catch-all like:

if (!host->cmd)
  state = MMCI_BUSY_DONE;

as if there is no command going on then surely nothing is busy on the
host controller.

Yours,
Linus Walleij

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

* Re: [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-14 11:17         ` Linus Walleij
@ 2023-06-14 12:21           ` Ulf Hansson
  2023-06-14 19:22             ` Linus Walleij
  0 siblings, 1 reply; 21+ messages in thread
From: Ulf Hansson @ 2023-06-14 12:21 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Wed, 14 Jun 2023 at 13:17, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> On Wed, Jun 14, 2023 at 12:05 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> > However, we use the spin_lock to protect some members in the struct
> > mmci_host. In this case, the mmci_cmd_irq() is using "host->cmd" to
> > understand whether there is an active command to manage. When the
> > command has been completed, we set host->cmd to NULL.
>
> Hm right...
>
> I'm leaning toward some catch-all like:
>
> if (!host->cmd)
>   state = MMCI_BUSY_DONE;
>
> as if there is no command going on then surely nothing is busy on the
> host controller.

Right, so at what point do you want to add this check?

Kind regards
Uffe

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

* Re: [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-14 12:21           ` Ulf Hansson
@ 2023-06-14 19:22             ` Linus Walleij
  2023-06-14 19:28               ` Linus Walleij
  0 siblings, 1 reply; 21+ messages in thread
From: Linus Walleij @ 2023-06-14 19:22 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Wed, Jun 14, 2023 at 2:22 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> On Wed, 14 Jun 2023 at 13:17, Linus Walleij <linus.walleij@linaro.org> wrote:
> >
> > On Wed, Jun 14, 2023 at 12:05 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> >
> > > However, we use the spin_lock to protect some members in the struct
> > > mmci_host. In this case, the mmci_cmd_irq() is using "host->cmd" to
> > > understand whether there is an active command to manage. When the
> > > command has been completed, we set host->cmd to NULL.
> >
> > Hm right...
> >
> > I'm leaning toward some catch-all like:
> >
> > if (!host->cmd)
> >   state = MMCI_BUSY_DONE;
> >
> > as if there is no command going on then surely nothing is busy on the
> > host controller.
>
> Right, so at what point do you want to add this check?

I have put it before the calls to the busy_complete() callback, in the
IRQ, where we are already in atomic context. If we are not processing
a command, we should not do any busy detection for sure.

Yours,
Linus Walleij

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

* Re: [PATCH v3 10/10] mmc: mmci: Add busydetect timeout
  2023-06-14 19:22             ` Linus Walleij
@ 2023-06-14 19:28               ` Linus Walleij
  0 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2023-06-14 19:28 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Yann Gautier, Stefan Hansson, Maxime Coquelin, Alexandre Torgue,
	linux-mmc, linux-stm32

On Wed, Jun 14, 2023 at 9:22 PM Linus Walleij <linus.walleij@linaro.org> wrote:
> On Wed, Jun 14, 2023 at 2:22 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > On Wed, 14 Jun 2023 at 13:17, Linus Walleij <linus.walleij@linaro.org> wrote:
> > >
> > > On Wed, Jun 14, 2023 at 12:05 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > >
> > > > However, we use the spin_lock to protect some members in the struct
> > > > mmci_host. In this case, the mmci_cmd_irq() is using "host->cmd" to
> > > > understand whether there is an active command to manage. When the
> > > > command has been completed, we set host->cmd to NULL.
> > >
> > > Hm right...
> > >
> > > I'm leaning toward some catch-all like:
> > >
> > > if (!host->cmd)
> > >   state = MMCI_BUSY_DONE;
> > >
> > > as if there is no command going on then surely nothing is busy on the
> > > host controller.
> >
> > Right, so at what point do you want to add this check?
>
> I have put it before the calls to the busy_complete() callback, in the
> IRQ, where we are already in atomic context. If we are not processing
> a command, we should not do any busy detection for sure.

No that's wrong. The mmci_cmd_irq() is actually passed the command as
parameter, so I just augment the busy_complete() prototype to pass this
along down, check out the patch (I talk to much).

Yours,
Linus Walleij

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

end of thread, other threads:[~2023-06-14 19:29 UTC | newest]

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).