The ux500 variant doesn't have a HW based timeout to use for busy-end IRQs. To avoid hanging and waiting for the card to stop signaling busy, let's schedule a delayed work, according to the corresponding cmd->busy_timeout for the command. If work gets to run, let's kick the IRQ handler to completed the currently running request/command. Reviewed-by: Linus Walleij <linus.walleij@xxxxxxxxxx> Tested-by: Linus Walleij <linus.walleij@xxxxxxxxxx> Signed-off-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx> --- drivers/mmc/host/mmci.c | 50 ++++++++++++++++++++++++++--- drivers/mmc/host/mmci.h | 3 +- drivers/mmc/host/mmci_stm32_sdmmc.c | 3 +- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 8a661ea1a2d1..61d761646462 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> @@ -682,7 +683,8 @@ static void ux500_busy_clear_mask_done(struct mmci_host *host) * | | * IRQ1 IRQ2 */ -static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk) +static bool ux500_busy_complete(struct mmci_host *host, struct mmc_command *cmd, + u32 status, u32 err_msk) { void __iomem *base = host->base; int retries = 10; @@ -726,6 +728,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_WAITING_FOR_START_IRQ; + schedule_delayed_work(&host->ux500_busy_timeout_work, + msecs_to_jiffies(cmd->busy_timeout)); goto out_ret_state; } retries--; @@ -753,6 +757,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"); + cancel_delayed_work(&host->ux500_busy_timeout_work); ux500_busy_clear_mask_done(host); } break; @@ -761,6 +766,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(&host->ux500_busy_timeout_work); ux500_busy_clear_mask_done(host); } else { dev_dbg(mmc_dev(host->mmc), @@ -1277,6 +1283,7 @@ static void mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) { void __iomem *base = host->base; + bool busy_resp = cmd->flags & MMC_RSP_BUSY; unsigned long long clks; dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n", @@ -1304,10 +1311,11 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) host->busy_status = 0; host->busy_state = MMCI_BUSY_DONE; - if (host->variant->busy_timeout && cmd->flags & MMC_RSP_BUSY) { - if (!cmd->busy_timeout) - cmd->busy_timeout = 10 * MSEC_PER_SEC; + /* Assign a default timeout if the core does not provide one */ + if (busy_resp && !cmd->busy_timeout) + cmd->busy_timeout = 10 * MSEC_PER_SEC; + if (busy_resp && host->variant->busy_timeout) { if (cmd->busy_timeout > host->mmc->max_busy_timeout) clks = (unsigned long long)host->mmc->max_busy_timeout * host->cclk; else @@ -1448,7 +1456,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, /* Handle busy detection on DAT0 if the variant supports it. */ if (busy_resp && host->variant->busy_detect) - if (!host->ops->busy_complete(host, status, err_msk)) + if (!host->ops->busy_complete(host, cmd, status, err_msk)) return; host->cmd = NULL; @@ -1495,6 +1503,34 @@ 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 ux500_busy_timeout_work(struct work_struct *work) +{ + struct mmci_host *host = container_of(work, struct mmci_host, + ux500_busy_timeout_work.work); + unsigned long flags; + u32 status; + + spin_lock_irqsave(&host->lock, flags); + + if (host->cmd) { + dev_dbg(mmc_dev(host->mmc), "timeout waiting for busy IRQ\n"); + + /* If we are still busy let's tag on a cmd-timeout error. */ + status = readl(host->base + MMCISTATUS); + if (status & host->variant->busy_detect_flag) + status |= MCI_CMDTIMEOUT; + + mmci_cmd_irq(host, host->cmd, status); + } + + spin_unlock_irqrestore(&host->lock, flags); +} + static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain) { return remain - (readl(host->base + MMCIFIFOCNT) << 2); @@ -2309,6 +2345,10 @@ static int mmci_probe(struct amba_device *dev, goto clk_disable; } + if (host->variant->busy_detect) + INIT_DELAYED_WORK(&host->ux500_busy_timeout_work, + ux500_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..69b2439842dd 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -393,7 +393,7 @@ struct mmci_host_ops { void (*dma_error)(struct mmci_host *host); void (*set_clkreg)(struct mmci_host *host, unsigned int desired); void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr); - bool (*busy_complete)(struct mmci_host *host, u32 status, u32 err_msk); + bool (*busy_complete)(struct mmci_host *host, struct mmc_command *cmd, u32 status, u32 err_msk); void (*pre_sig_volt_switch)(struct mmci_host *host); int (*post_sig_volt_switch)(struct mmci_host *host, struct mmc_ios *ios); }; @@ -451,6 +451,7 @@ struct mmci_host { void *dma_priv; s32 next_cookie; + struct delayed_work ux500_busy_timeout_work; }; #define dma_inprogress(host) ((host)->dma_in_progress) diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index 953d1be4e379..f07cfbeafe2e 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -372,7 +372,8 @@ static u32 sdmmc_get_dctrl_cfg(struct mmci_host *host) return datactrl; } -static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk) +static bool sdmmc_busy_complete(struct mmci_host *host, struct mmc_command *cmd, + u32 status, u32 err_msk) { void __iomem *base = host->base; u32 busy_d0, busy_d0end, mask, sdmmc_status; -- 2.34.1