summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorJaehoon Chung <jh80.chung@samsung.com>2013-09-10 17:43:03 +0900
committerMyungJoo Ham <myungjoo.ham@samsung.com>2013-11-15 13:52:20 +0900
commite07f849566a05d85e31b69fa00cf0cf0f407316b (patch)
tree7409913fa3296e2cf75087c9f87714ff34baf051 /drivers/mmc
parent8e6b648a3c013b4a9f11d67d339504ee6bffba4b (diff)
downloadlinux-3.10-e07f849566a05d85e31b69fa00cf0cf0f407316b.tar.gz
linux-3.10-e07f849566a05d85e31b69fa00cf0cf0f407316b.tar.bz2
linux-3.10-e07f849566a05d85e31b69fa00cf0cf0f407316b.zip
mmc: dw-mmc: check whether card is busy or not, before clock disabled.
Before disable the clock, must check whether card is busy or not. (Refer to DesignWare TRM) Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/dw_mmc.c39
1 files changed, 39 insertions, 0 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 564c9fbd5c7..c6deb2bd49d 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -310,6 +310,27 @@ static void dw_mci_stop_dma(struct dw_mci *host)
}
}
+static bool dw_mci_wait_reset(struct device *dev, struct dw_mci *host,
+ unsigned int reset_val)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(500);
+ unsigned int ctrl;
+
+ ctrl = mci_readl(host, CTRL);
+ ctrl |= reset_val;
+ mci_writel(host, CTRL, ctrl);
+
+ /* wait till resets clear */
+ do {
+ if (!(mci_readl(host, CTRL) & reset_val))
+ return true;
+ } while (time_before(jiffies, timeout));
+
+ dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+ return false;
+}
+
static int dw_mci_get_dma_dir(struct mmc_data *data)
{
if (data->flags & MMC_DATA_WRITE)
@@ -634,6 +655,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
struct dw_mci *host = slot->host;
u32 div;
u32 clk_en_a;
+ int timeout = 1000;
if (slot->clock != host->current_speed || force_clkinit) {
div = host->bus_hz / slot->clock;
@@ -651,6 +673,23 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
" div = %d)\n", slot->id, host->bus_hz, slot->clock,
div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+ /*
+ * Before disable the clock,
+ * must check whether the card is busy or not.
+ */
+ do {
+ if (!(mci_readl(host, STATUS) & BIT(9)))
+ break;
+ if (timeout-- < 0) {
+ dev_err(host->dev, "Can't disable clock"
+ "because Card is busy!!\n");
+ return;
+ }
+ host->cur_slot = slot;
+ dw_mci_wait_reset(host->dev, host, SDMMC_CTRL_RESET);
+
+ } while (1);
+
/* disable clock */
mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0);