Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / mmc / host / sh_mobile_sdhi.c
index b365429..774f643 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/mfd/tmio.h>
 #include <linux/sh_dma.h>
+#include <linux/delay.h>
 
 #include "tmio_mmc.h"
 
@@ -55,6 +56,39 @@ static int sh_mobile_sdhi_get_cd(struct platform_device *pdev)
                return -ENOSYS;
 }
 
+static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
+{
+       int timeout = 1000;
+
+       while (--timeout && !(sd_ctrl_read16(host, CTL_STATUS2) & (1 << 13)))
+               udelay(1);
+
+       if (!timeout) {
+               dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
+{
+       switch (addr)
+       {
+       case CTL_SD_CMD:
+       case CTL_STOP_INTERNAL_ACTION:
+       case CTL_XFER_BLK_COUNT:
+       case CTL_SD_CARD_CLK_CTL:
+       case CTL_SD_XFER_LEN:
+       case CTL_SD_MEM_CARD_OPT:
+       case CTL_TRANSACTION_CTL:
+       case CTL_DMA_ENABLE:
+               return sh_mobile_sdhi_wait_idle(host);
+       }
+
+       return 0;
+}
+
 static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
 {
        struct sh_mobile_sdhi *priv;
@@ -86,13 +120,15 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
        mmc_data->hclk = clk_get_rate(priv->clk);
        mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
        mmc_data->get_cd = sh_mobile_sdhi_get_cd;
+       if (mmc_data->flags & TMIO_MMC_HAS_IDLE_WAIT)
+               mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
        mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
        if (p) {
                mmc_data->flags = p->tmio_flags;
                mmc_data->ocr_mask = p->tmio_ocr_mask;
                mmc_data->capabilities |= p->tmio_caps;
 
-               if (p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) {
+               if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
                        priv->param_tx.slave_id = p->dma_slave_tx;
                        priv->param_rx.slave_id = p->dma_slave_rx;
                        priv->dma_priv.chan_priv_tx = &priv->param_tx;
@@ -165,13 +201,14 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
 
        p->pdata = NULL;
 
+       tmio_mmc_host_remove(host);
+
        for (i = 0; i < 3; i++) {
                irq = platform_get_irq(pdev, i);
                if (irq >= 0)
                        free_irq(irq, host);
        }
 
-       tmio_mmc_host_remove(host);
        clk_disable(priv->clk);
        clk_put(priv->clk);
        kfree(priv);