From 935a665e15dcda3b0b986829f32e2724db1f20fc Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 14 Jan 2016 09:08:02 +0800 Subject: [PATCH] mmc: dw_mmc: add hw_reset support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- 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 7d6bd9d32b48..6ad9ebf1faef 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -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, diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 6fc84b61dc8c..68d5da2dfd19 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 */