[PATCH v3] mmc: dw_mmc: add hw_reset support
From: Shawn Lin
Date: Wed Jan 13 2016 - 20:12:59 EST
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@xxxxxxxxxxxxxx>
---
Changes in v3:
- reset for each slot
- simply the commit msg
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 | 29 +++++++++++++++++++++++++++++
drivers/mmc/host/dw_mmc.h | 3 +++
2 files changed, 32 insertions(+)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 7128351..fddbcb6 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1477,6 +1477,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(slot->host, RST_N, reset);
+ usleep_range(1, 2);
+ reset |= SDMMC_RST_HWACTIVE << slot->id;
+ mci_writel(slot->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);
@@ -1563,6 +1591,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..a14b7fc 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,8 @@
#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 */
--
2.3.7