pmaports/device/testing/linux-postmarketos-stericsson/0001-fix-busydetect-ux500.patch

1147 lines
39 KiB
Diff
Raw Normal View History

From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 1/12] mmc: mmci: Clear busy_status when starting command
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:22 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-1-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 2/12] mmc: mmci: Unwind big if() clause
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:23 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-2-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 3/12] mmc: mmci: Stash status while waiting for busy
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:24 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-3-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 4/12] mmc: mmci: Break out error check in busy detect
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:25 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-4-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 5/12] mmc: mmci: Make busy complete state machine
explicit
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:26 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-5-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 6/12] mmc: mmci: Retry the busy start condition
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:27 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-6-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 7/12] mmc: mmci: Use state machine state as exit
condition
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:28 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-7-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 8/12] mmc: mmci: Use a switch statement machine
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:29 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-8-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 9/12] mmc: mmci: Break out a helper function
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:30 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-9-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 10/12] mmc: mmci: mmci_card_busy() from state machine
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:31 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-10-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 11/12] mmc: mmci: Drop end IRQ from Ux500 busydetect
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:32 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-11-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 12/12] mmc: mmci: Add busydetect timeout
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sun, 09 Apr 2023 00:00:33 +0200
Message-Id: <20230405-pl180-busydetect-fix-v2-12-eeb10323b546@linaro.org>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
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