]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/mmc/host/mxcmmc.c
Merge tag 'mmc-merge-for-3.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / drivers / mmc / host / mxcmmc.c
index 7b1161de01d60b0b68e26f2b7e6d9e41f79de82e..565c2e4fac75cecd8890ec2d9885ffb50cc23060 100644 (file)
@@ -44,6 +44,7 @@
 #include <mach/hardware.h>
 
 #define DRIVER_NAME "mxc-mmc"
+#define MXCMCI_TIMEOUT_MS 10000
 
 #define MMC_REG_STR_STP_CLK            0x00
 #define MMC_REG_STATUS                 0x04
@@ -150,6 +151,8 @@ struct mxcmci_host {
        int                     dmareq;
        struct dma_slave_config dma_slave_config;
        struct imx_dma_data     dma_data;
+
+       struct timer_list       watchdog;
 };
 
 static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
@@ -271,9 +274,32 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
        dmaengine_submit(host->desc);
        dma_async_issue_pending(host->dma);
 
+       mod_timer(&host->watchdog, jiffies + msecs_to_jiffies(MXCMCI_TIMEOUT_MS));
+
        return 0;
 }
 
+static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat);
+static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat);
+
+static void mxcmci_dma_callback(void *data)
+{
+       struct mxcmci_host *host = data;
+       u32 stat;
+
+       del_timer(&host->watchdog);
+
+       stat = readl(host->base + MMC_REG_STATUS);
+       writel(stat & ~STATUS_DATA_TRANS_DONE, host->base + MMC_REG_STATUS);
+
+       dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
+
+       if (stat & STATUS_READ_OP_DONE)
+               writel(STATUS_READ_OP_DONE, host->base + MMC_REG_STATUS);
+
+       mxcmci_data_done(host, stat);
+}
+
 static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
                unsigned int cmdat)
 {
@@ -305,8 +331,14 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
 
        int_cntr = INT_END_CMD_RES_EN;
 
-       if (mxcmci_use_dma(host))
-               int_cntr |= INT_READ_OP_EN | INT_WRITE_OP_DONE_EN;
+       if (mxcmci_use_dma(host)) {
+               if (host->dma_dir == DMA_FROM_DEVICE) {
+                       host->desc->callback = mxcmci_dma_callback;
+                       host->desc->callback_param = host;
+               } else {
+                       int_cntr |= INT_WRITE_OP_DONE_EN;
+               }
+       }
 
        spin_lock_irqsave(&host->lock, flags);
        if (host->use_sdio)
@@ -345,11 +377,9 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
        struct mmc_data *data = host->data;
        int data_error;
 
-       if (mxcmci_use_dma(host)) {
-               dmaengine_terminate_all(host->dma);
+       if (mxcmci_use_dma(host))
                dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
                                host->dma_dir);
-       }
 
        if (stat & STATUS_ERR_MASK) {
                dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",
@@ -624,8 +654,10 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
                mxcmci_cmd_done(host, stat);
 
        if (mxcmci_use_dma(host) &&
-                 (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE)))
+                 (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) {
+               del_timer(&host->watchdog);
                mxcmci_data_done(host, stat);
+       }
 
        if (host->default_irq_mask &&
                  (stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
@@ -836,6 +868,34 @@ static bool filter(struct dma_chan *chan, void *param)
        return true;
 }
 
+static void mxcmci_watchdog(unsigned long data)
+{
+       struct mmc_host *mmc = (struct mmc_host *)data;
+       struct mxcmci_host *host = mmc_priv(mmc);
+       struct mmc_request *req = host->req;
+       unsigned int stat = readl(host->base + MMC_REG_STATUS);
+
+       if (host->dma_dir == DMA_FROM_DEVICE) {
+               dmaengine_terminate_all(host->dma);
+               dev_err(mmc_dev(host->mmc),
+                       "%s: read time out (status = 0x%08x)\n",
+                       __func__, stat);
+       } else {
+               dev_err(mmc_dev(host->mmc),
+                       "%s: write time out (status = 0x%08x)\n",
+                       __func__, stat);
+               mxcmci_softreset(host);
+       }
+
+       /* Mark transfer as erroneus and inform the upper layers */
+
+       host->data->error = -ETIMEDOUT;
+       host->req = NULL;
+       host->cmd = NULL;
+       host->data = NULL;
+       mmc_request_done(host->mmc, req);
+}
+
 static const struct mmc_host_ops mxcmci_ops = {
        .request                = mxcmci_request,
        .set_ios                = mxcmci_set_ios,
@@ -968,6 +1028,10 @@ static int mxcmci_probe(struct platform_device *pdev)
 
        mmc_add_host(mmc);
 
+       init_timer(&host->watchdog);
+       host->watchdog.function = &mxcmci_watchdog;
+       host->watchdog.data = (unsigned long)mmc;
+
        return 0;
 
 out_free_irq: