This patch implement hw_reset function for DesignWare
MMC controller. By adding this feature, mmc blk can
do some basic recovery.

>From Synopsys DesignWare Cores Mobile Storage Host Databook
(Section 7.4.4), we get the details:
1. Program CMD12 to end any transfer in process.
2. Wait for DTO, even if no response is sent back by the card.
3. Set the following resets:
        Software reset – BMOD[0] for IDMAC only
        DMA reset– CTRL[2]
        FIFO reset – CTRL[1] bits
4. Program the CARD_RESET register with a value of 0 for the bit
corresponding to the card number; This can be done at any time when
the card is connected to the controller. This programming asserts the
RST_n signal and resets the card.
5. Wait for minimum of 1 μs or cclk_in period, which ever is greater
6. After a minimum of 1 μs, the application should program a value of
0 into the CARD_RESET register for the bit corresponding to the card
number. This de-asserts the RST_n signal and takes the card out of reset.
7. The application can program a new CMD only after a minimum of 200 us
after the de-assertion of the RST_n signal, as per the MMC 4.41 standard.

HW reset producer will be call in mmc_init_card before mmc_go_idle. At that
time,dw mmc hasn't update clk for itself, so CMD12 is inappropriate and
unnecessary. Moreover, if mmc device runs into broken states, DRTO or RTO
generated by previous cmd w/ data will make mmc core issue stop already. Then
it will retry again and again, issue stop and card status again until the
cmd's retry number decrease to zero. That will finally trigger HW reset producer
if we declare MMC_CAP_HW_RESET. So there's no need to do step 1 and 2 for the
reasons we mentioned above.

This implementation can be easily tested by cutting off->On vmmc while doing 
data
accessing in background to simulate that case. And dw_mmc can generate timeout
interrupt and make mmc core trigger hw reset producer before re-init mmc card
to recover itself.

Signed-off-by: Shawn Lin <shawn....@rock-chips.com>

---

Changes in v2:
- remove unecessary mb
- reduce time cost for hw_reset
- combine SDMMC_CTRL_DMA_RESET and SDMMC_CTRL_FIFO_RESET

 drivers/mmc/host/dw_mmc.c | 25 +++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h |  4 ++++
 2 files changed, 29 insertions(+)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 7128351..98e75fc 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1477,6 +1477,30 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
        return present;
 }
 
+static void dw_mci_hw_reset(struct mmc_host *mmc)
+{
+       struct dw_mci_slot *slot = mmc_priv(mmc);
+       struct dw_mci *host = slot->host;
+
+       if (host->use_dma == TRANS_MODE_IDMAC)
+               dw_mci_idmac_reset(host);
+
+       if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET |
+                                    SDMMC_CTRL_FIFO_RESET))
+               return;
+
+       /*
+        * According to eMMC spec, card reset procedure:
+        * tRstW >= 1us:   RST_n pulse width
+        * tRSCA >= 200us: RST_n to Command time
+        * tRSTH >= 1us:   RST_n high period
+        */
+        mci_writel(slot->host, RST_N, SDMMC_RST_HWRESET);
+        usleep_range(1, 2);
+        mci_writel(slot->host, RST_N, SDMMC_RST_HWACTIVE);
+        usleep_range(200, 300);
+}
+
 static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
 {
        struct dw_mci_slot *slot = mmc_priv(mmc);
@@ -1563,6 +1587,7 @@ static const struct mmc_host_ops dw_mci_ops = {
        .set_ios                = dw_mci_set_ios,
        .get_ro                 = dw_mci_get_ro,
        .get_cd                 = dw_mci_get_cd,
+       .hw_reset               = dw_mci_hw_reset,
        .enable_sdio_irq        = dw_mci_enable_sdio_irq,
        .execute_tuning         = dw_mci_execute_tuning,
        .card_busy              = dw_mci_card_busy,
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index f695b58..684ee34 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -46,6 +46,7 @@
 #define SDMMC_VERID            0x06c
 #define SDMMC_HCON             0x070
 #define SDMMC_UHS_REG          0x074
+#define SDMMC_RST_N            0x078
 #define SDMMC_BMOD             0x080
 #define SDMMC_PLDMND           0x084
 #define SDMMC_DBADDR           0x088
@@ -169,6 +170,9 @@
 #define SDMMC_IDMAC_ENABLE             BIT(7)
 #define SDMMC_IDMAC_FB                 BIT(1)
 #define SDMMC_IDMAC_SWRESET            BIT(0)
+/* H/W reset */
+#define SDMMC_RST_HWACTIVE             0x1
+#define SDMMC_RST_HWRESET              0x0
 /* Version ID register define */
 #define SDMMC_GET_VERID(x)             ((x) & 0xFFFF)
 /* Card read threshold */
-- 
2.3.7


--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to