[PATCHv2] mmc: dw_mmc: change to use recommended reset procedure

Sonny Rao sonnyrao at chromium.org
Tue May 13 00:16:16 PDT 2014


On Mon, May 12, 2014 at 10:02 PM, Seungwon Jeon <tgih.jun at samsung.com> wrote:
> As I mentioned in previous version, you put all reset stuff in existing fifo_reset function.
> Although databook mentions ciu_reset case for SBE error, it's not obvious when ciu_reset is needed in other error cases.
> If you intend to add some robust error handing, it would be better to make another function.

The document I have actually doesn't mention error cases, it describes
this procedure as "Controller/DMA/FIFO Reset Usage" so I believe it is
saying this is the correct procedure in all cases, and based on our
testing it seems to work.  I understand the skepticism, as I shared it
initially when I saw this, but based on our experience, this is
correct and it doesn't need to live in a separate function.

> Please check my comments below.
>
> On Tue, May 13, 2014, Sonny Rao wrote:
>> This patch changes the fifo reset code to follow the reset procedure
>> outlined in the documentation of Synopsys  Mobile storage host databook
>> 7.2.13.
>>
>> v2: Add Generic DMA support
>>     per the documentation, move interrupt clear before wait
>>     make the test for DMA host->use_dma rather than host->using_dma
>>     add proper return values (although it appears no caller checks)
>>
>> Signed-off-by: Sonny Rao <sonnyrao at chromium.org>
>> Signed-off-by: Yuvaraj Kumar C D <yuvaraj.cd at samsung.com>
>> ---
>>  drivers/mmc/host/dw_mmc.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++-
>>  drivers/mmc/host/dw_mmc.h |  1 +
>>  2 files changed, 55 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>> index 55cd110..aff57e1 100644
>> --- a/drivers/mmc/host/dw_mmc.c
>> +++ b/drivers/mmc/host/dw_mmc.c
>> @@ -2325,6 +2325,7 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
>>
>>  static inline bool dw_mci_fifo_reset(struct dw_mci *host)
>>  {
>> +     u32 flags = SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET;
>>       /*
>>        * Reseting generates a block interrupt, hence setting
>>        * the scatter-gather pointer to NULL.
>> @@ -2334,7 +2335,59 @@ static inline bool dw_mci_fifo_reset(struct dw_mci *host)
>>               host->sg = NULL;
>>       }
>>
>> -     return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET);
>> +     /*
>> +      * The recommended method for resetting is to always reset the
>> +      * controller and the fifo, but differs slightly depending on the mode.
>> +      * The Generic DMA mode (non IDMAC) also needs to reset DMA where IDMAC
>> +      * mode resets IDMAC at the end.
>> +      *
>> +      */
>> +#ifndef CONFIG_MMC_DW_IDMAC
> Is it added for generic DMA?
> IDMAC should be considered for dma_reseet as well.
> Please check databook.
>

Yeah it's a little unclear.  In the "7.2.13 Controller/DMA/FIFO Reset
Usage" part of the document they mention It is set for what they call
"generic" DMA, which I think is when there is an external DMA
controller, and the part below that it says for "DW-DMA/Non-DW-DMA"
that controller_reset and fifo_reset should be set.   I believe this
"DW-DMA" case refers to what is called IDMAC.  So, I think it's not
required for this case, but I admit I'm not sure why they also say
"Non-DW-DMA". If you know of a good way to differentiate the "Generic
DMA" case  I can implement it.  It wasn't clear to me if the driver
even supported this "generic" dma case, but it sounds like it might,
so I added this code.  In practice, on the Exynos Systems we have,
which are using IDMAC, the reset procedure seems to work okay without
the dma_reset bit set, but I cannot test this "generic dma" case.

The other places in the doc where I see them mention the dma_reset bit
are "7.2.21 Transmission and Reception with Internal DMAC (IDMAC)"
and the description of the CTRL register, and in "7.1
Software/Hardware Restrictions" where they say it will terminate any
pending transfer.

>> +     if (host->use_dma)
>> +             flags |= SDMMC_CTRL_DMA_RESET;
>> +#endif
>> +     if (dw_mci_ctrl_reset(host, flags)) {
>> +             /*
>> +              * In all cases we clear the RAWINTS register to clear any
>> +              * interrupts.
>> +              */
> I think interrupt masking is needed before reset because we will not handle it anymore.
> And Is there any reason to move interrupt clear here compared with previous version?

Yeah I moved it to match the description in the document more closely.
 The documents mentioned doing the interrupt clear after setting the
reset bits, and before waiting for the dma_req bit in the status
register to clear.  We've been running it with the interrupt clear
below the loop, for a while, and I just tested it with the clear above
the wait to make sure it still works properly and I was able to pass
the tuning process with some errors, so I believe this works fine too,
and more closely matches the description in the doc that I have.

>> +             mci_writel(host, RINTSTS, 0xFFFFFFFF);
>> +
>> +             /* if using dma we wait for dma_req to clear */
>> +             if (host->use_dma) {
>> +                     unsigned long timeout = jiffies + msecs_to_jiffies(500);
>> +                     u32 status;
>> +                     do {
>> +                             status = mci_readl(host, STATUS);
>> +                             if (!(status & SDMMC_STATUS_DMA_REQ))
>> +                                     break;
>> +                             cpu_relax();
> What did you intend here?
> If you intent busy-wait, how about using sleep instead?

Yes it is a busy-wait.  There is similar code in dw_mci_ctrl_reset,
where that function is polling without sleeps, so I was trying to
match that.  The cpu_relax() is something I saw in other busy-waits in
the kernel, but I'm happy to take it out if you'd like.

>> +                     } while (time_before(jiffies, timeout));
>> +
>> +                     if (status & SDMMC_STATUS_DMA_REQ) {
>> +                             dev_err(host->dev,
>> +                                     "%s: Timeout waiting for dma_req to "
>> +                                     "clear during reset", __func__);
>> +                             return false;
>> +                     }
>> +
>> +                     /* when using DMA next we reset the fifo again */
>> +                     dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET);
>> +             }
>> +     } else {
>> +             dev_err(host->dev, "%s: Reset bits didn't clear", __func__);
> If ciu_reset is cleared, clk update below is needed?

I'm honestly not sure what happens if the reset bits don't clear.  The
docs say controller_reset and dma_reset will auto clear after a number
of clocks, but fifo_reset will clear "after completion of the reset
operation"  So in this particular error case I'm not sure if it's
possible to recover properly or not and might hang, so I thought it
best to just return the error immediately.

> Thanks,
> Seungwon Jeon
>
>> +             return false;
>> +     }
>> +
>> +#ifdef CONFIG_MMC_DW_IDMAC
>> +     /* It is also recommended that we reset and reprogram idmac */
>> +     dw_mci_idmac_reset(host);
>> +#endif
>> +
>> +     /* After a CTRL reset we need to have CIU set clock registers  */
>> +     mci_send_cmd(host->cur_slot, SDMMC_CMD_UPD_CLK, 0);
>> +
>> +     return true;
>>  }
>>
>>  static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host)
>> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
>> index 6bf24ab..2505804 100644
>> --- a/drivers/mmc/host/dw_mmc.h
>> +++ b/drivers/mmc/host/dw_mmc.h
>> @@ -129,6 +129,7 @@
>>  #define SDMMC_CMD_INDX(n)            ((n) & 0x1F)
>>  /* Status register defines */
>>  #define SDMMC_GET_FCNT(x)            (((x)>>17) & 0x1FFF)
>> +#define SDMMC_STATUS_DMA_REQ         BIT(31)
>>  /* FIFOTH register defines */
>>  #define SDMMC_SET_FIFOTH(m, r, t)    (((m) & 0x7) << 28 | \
>>                                        ((r) & 0xFFF) << 16 | \
>> --
>> 1.9.1.423.g4596e3a
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



More information about the linux-arm-kernel mailing list