Re: [PATCH] mmc: mmci: Add support for SW busy-end timeouts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 20 Jun 2023 at 11:27, Yann Gautier <yann.gautier@xxxxxxxxxxx> wrote:
>
> On 6/20/23 11:11, Ulf Hansson wrote:
> > 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);
>
> Hi Ulf,
>
> STM32 variants also have busy_detect = true.
> Could that be an issue to initialize this work, which seem dedicated to
> ux500?

The work will not be used for the STM32 variants
(sdmmc_variant_init()), so it's not a problem as it's just an
initialization.

> I haven't tested the patch yet. Will do that soon.

Looking forward to your feedback, thanks!

Kind regards
Uffe

>
>
> Yann
>
> > +
> >       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;
>



[Index of Archives]     [Linux Memonry Technology]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux