mmc: dw_mmc: add hw_reset support
authorShawn Lin <shawn.lin@rock-chips.com>
Thu, 14 Jan 2016 01:08:02 +0000 (09:08 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 29 Feb 2016 10:03:09 +0000 (11:03 +0100)
This patch implement hw_reset function for DesignWare
MMC controller. By adding this feature, mmc blk can
do some basic recovery.

Set the following resets:
software reset – BMOD[0] for IDMAC only
DMA reset - CTRL[2]
FIFO reset - CTRL[1] bits

Program the CARD_RESET register with a value of 0 for the bit
corresponding to the card number; This programming asserts the
RST_n signal and resets the card. After a minimum of 1 ?s, de-asserts the
RST_n signal and takes the card out of reset. The application can program
a new CMD only after a minimum of 200 us

This implementation can be easily tested by cutting off->On vmmc
while doing data accessing in background to simulate that case.

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h

index 7d6bd9d..6ad9ebf 100644 (file)
@@ -1474,6 +1474,34 @@ 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;
+       int reset;
+
+       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
+        */
+       reset = mci_readl(host, RST_N);
+       reset &= ~(SDMMC_RST_HWACTIVE << slot->id);
+       mci_writel(host, RST_N, reset);
+       usleep_range(1, 2);
+       reset |= SDMMC_RST_HWACTIVE << slot->id;
+       mci_writel(host, RST_N, reset);
+       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);
@@ -1560,6 +1588,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,
index 6fc84b6..68d5da2 100644 (file)
@@ -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
 #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
 /* Version ID register define */
 #define SDMMC_GET_VERID(x)             ((x) & 0xFFFF)
 /* Card read threshold */