Merge branch 'msm-mmc_sdcc' of git://codeaurora.org/quic/kernel/dwalker/linux-msm
[sfrench/cifs-2.6.git] / drivers / mmc / host / msm_sdcc.c
index 04ae884383f690d6fdc41cae87da020afbbbfad9..61f1d27fed3fe7b1b510c7cfc7d49883b8ad581a 100644 (file)
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2007 Google Inc,
  *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *  Copyright (C) 2009, Code Aurora Forum. All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -26,6 +27,7 @@
 #include <linux/log2.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
 #include <linux/clk.h>
 #include <linux/scatterlist.h>
 #include <linux/platform_device.h>
@@ -47,6 +49,8 @@
 
 #define DRIVER_NAME "msm-sdcc"
 
+#define BUSCLK_PWRSAVE 1
+#define BUSCLK_TIMEOUT (HZ)
 static unsigned int msmsdcc_fmin = 144000;
 static unsigned int msmsdcc_fmax = 50000000;
 static unsigned int msmsdcc_4bit = 1;
@@ -57,6 +61,67 @@ static unsigned int msmsdcc_sdioirq;
 #define PIO_SPINMAX 30
 #define CMD_SPINMAX 20
 
+
+static inline void
+msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr)
+{
+       WARN_ON(!host->clks_on);
+
+       BUG_ON(host->curr.mrq);
+
+       if (deferr) {
+               mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT);
+       } else {
+               del_timer_sync(&host->busclk_timer);
+               /* Need to check clks_on again in case the busclk
+                * timer fired
+                */
+               if (host->clks_on) {
+                       clk_disable(host->clk);
+                       clk_disable(host->pclk);
+                       host->clks_on = 0;
+               }
+       }
+}
+
+static inline int
+msmsdcc_enable_clocks(struct msmsdcc_host *host)
+{
+       int rc;
+
+       del_timer_sync(&host->busclk_timer);
+
+       if (!host->clks_on) {
+               rc = clk_enable(host->pclk);
+               if (rc)
+                       return rc;
+               rc = clk_enable(host->clk);
+               if (rc) {
+                       clk_disable(host->pclk);
+                       return rc;
+               }
+               udelay(1 + ((3 * USEC_PER_SEC) /
+                      (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
+               host->clks_on = 1;
+       }
+       return 0;
+}
+
+static inline unsigned int
+msmsdcc_readl(struct msmsdcc_host *host, unsigned int reg)
+{
+       return readl(host->base + reg);
+}
+
+static inline void
+msmsdcc_writel(struct msmsdcc_host *host, u32 data, unsigned int reg)
+{
+       writel(data, host->base + reg);
+       /* 3 clk delay required! */
+       udelay(1 + ((3 * USEC_PER_SEC) /
+              (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
+}
+
 static void
 msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
                      u32 c);
@@ -64,8 +129,6 @@ msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
 static void
 msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
 {
-       writel(0, host->base + MMCICOMMAND);
-
        BUG_ON(host->curr.data);
 
        host->curr.mrq = NULL;
@@ -76,6 +139,9 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
        if (mrq->cmd->error == -ETIMEDOUT)
                mdelay(5);
 
+#if BUSCLK_PWRSAVE
+       msmsdcc_disable_clocks(host, 1);
+#endif
        /*
         * Need to drop the host lock here; mmc_request_done may call
         * back into the driver...
@@ -88,7 +154,6 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
 static void
 msmsdcc_stop_data(struct msmsdcc_host *host)
 {
-       writel(0, host->base + MMCIDATACTRL);
        host->curr.data = NULL;
        host->curr.got_dataend = host->curr.got_datablkend = 0;
 }
@@ -109,6 +174,31 @@ uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
        return 0;
 }
 
+static inline void
+msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c) {
+       msmsdcc_writel(host, arg, MMCIARGUMENT);
+       msmsdcc_writel(host, c, MMCICOMMAND);
+}
+
+static void
+msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
+{
+       struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data;
+
+       msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER);
+       msmsdcc_writel(host, (unsigned int)host->curr.xfer_size,
+                      MMCIDATALENGTH);
+       msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1);
+       msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL);
+
+       if (host->cmd_cmd) {
+               msmsdcc_start_command_exec(host,
+                                          (u32) host->cmd_cmd->arg,
+                                          (u32) host->cmd_c);
+       }
+       host->dma.active = 1;
+}
+
 static void
 msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
                          unsigned int result,
@@ -121,8 +211,11 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
        struct mmc_request      *mrq;
 
        spin_lock_irqsave(&host->lock, flags);
+       host->dma.active = 0;
+
        mrq = host->curr.mrq;
        BUG_ON(!mrq);
+       WARN_ON(!mrq->data);
 
        if (!(result & DMOV_RSLT_VALID)) {
                pr_err("msmsdcc: Invalid DataMover result\n");
@@ -146,7 +239,6 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
                if (!mrq->data->error)
                        mrq->data->error = -EIO;
        }
-       host->dma.busy = 0;
        dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents,
                     host->dma.dir);
 
@@ -159,6 +251,7 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
        }
 
        host->dma.sg = NULL;
+       host->dma.busy = 0;
 
        if ((host->curr.got_dataend && host->curr.got_datablkend)
             || mrq->data->error) {
@@ -172,12 +265,14 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
                if (!mrq->data->error)
                        host->curr.data_xfered = host->curr.xfer_size;
                if (!mrq->data->stop || mrq->cmd->error) {
-                       writel(0, host->base + MMCICOMMAND);
                        host->curr.mrq = NULL;
                        host->curr.cmd = NULL;
                        mrq->data->bytes_xfered = host->curr.data_xfered;
 
                        spin_unlock_irqrestore(&host->lock, flags);
+#if BUSCLK_PWRSAVE
+                       msmsdcc_disable_clocks(host, 1);
+#endif
                        mmc_request_done(host->mmc, mrq);
                        return;
                } else
@@ -218,6 +313,8 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
        host->dma.sg = data->sg;
        host->dma.num_ents = data->sg_len;
 
+       BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */
+
        nc = host->dma.nc;
 
        switch (host->pdev_id) {
@@ -246,22 +343,15 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
 
        host->curr.user_pages = 0;
 
-       n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
-                      host->dma.num_ents, host->dma.dir);
-
-       if (n != host->dma.num_ents) {
-               pr_err("%s: Unable to map in all sg elements\n",
-                      mmc_hostname(host->mmc));
-               host->dma.sg = NULL;
-               host->dma.num_ents = 0;
-               return -ENOMEM;
-       }
-
        box = &nc->cmd[0];
        for (i = 0; i < host->dma.num_ents; i++) {
                box->cmd = CMD_MODE_BOX;
 
-               if (i == (host->dma.num_ents - 1))
+       /* Initialize sg dma address */
+       sg->dma_address = page_to_dma(mmc_dev(host->mmc), sg_page(sg))
+                               + sg->offset;
+
+       if (i == (host->dma.num_ents - 1))
                        box->cmd |= CMD_LC;
                rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
                        (sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
@@ -300,15 +390,70 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
                               DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
        host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
 
+       n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
+                       host->dma.num_ents, host->dma.dir);
+/* dsb inside dma_map_sg will write nc out to mem as well */
+
+       if (n != host->dma.num_ents) {
+               printk(KERN_ERR "%s: Unable to map in all sg elements\n",
+                       mmc_hostname(host->mmc));
+               host->dma.sg = NULL;
+               host->dma.num_ents = 0;
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int
+snoop_cccr_abort(struct mmc_command *cmd)
+{
+       if ((cmd->opcode == 52) &&
+           (cmd->arg & 0x80000000) &&
+           (((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT))
+               return 1;
        return 0;
 }
 
 static void
-msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
+msmsdcc_start_command_deferred(struct msmsdcc_host *host,
+                               struct mmc_command *cmd, u32 *c)
+{
+       *c |= (cmd->opcode | MCI_CPSM_ENABLE);
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               if (cmd->flags & MMC_RSP_136)
+                       *c |= MCI_CPSM_LONGRSP;
+               *c |= MCI_CPSM_RESPONSE;
+       }
+
+       if (/*interrupt*/0)
+               *c |= MCI_CPSM_INTERRUPT;
+
+       if ((((cmd->opcode == 17) || (cmd->opcode == 18))  ||
+            ((cmd->opcode == 24) || (cmd->opcode == 25))) ||
+             (cmd->opcode == 53))
+               *c |= MCI_CSPM_DATCMD;
+
+       if (cmd == cmd->mrq->stop)
+               *c |= MCI_CSPM_MCIABORT;
+
+       if (snoop_cccr_abort(cmd))
+               *c |= MCI_CSPM_MCIABORT;
+
+       if (host->curr.cmd != NULL) {
+               printk(KERN_ERR "%s: Overlapping command requests\n",
+                       mmc_hostname(host->mmc));
+       }
+       host->curr.cmd = cmd;
+}
+
+static void
+msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
+                       struct mmc_command *cmd, u32 c)
 {
        unsigned int datactrl, timeout;
        unsigned long long clks;
-       void __iomem *base = host->base;
        unsigned int pio_irqmask = 0;
 
        host->curr.data = data;
@@ -320,13 +465,6 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
 
        memset(&host->pio, 0, sizeof(host->pio));
 
-       clks = (unsigned long long)data->timeout_ns * host->clk_rate;
-       do_div(clks, NSEC_PER_SEC);
-       timeout = data->timeout_clks + (unsigned int)clks;
-       writel(timeout, base + MMCIDATATIMER);
-
-       writel(host->curr.xfer_size, base + MMCIDATALENGTH);
-
        datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
 
        if (!msmsdcc_config_dma(host, data))
@@ -347,47 +485,51 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
        if (data->flags & MMC_DATA_READ)
                datactrl |= MCI_DPSM_DIRECTION;
 
-       writel(pio_irqmask, base + MMCIMASK1);
-       writel(datactrl, base + MMCIDATACTRL);
+       clks = (unsigned long long)data->timeout_ns * host->clk_rate;
+       do_div(clks, NSEC_PER_SEC);
+       timeout = data->timeout_clks + (unsigned int)clks*2 ;
 
        if (datactrl & MCI_DPSM_DMAENABLE) {
+               /* Save parameters for the exec function */
+               host->cmd_timeout = timeout;
+               host->cmd_pio_irqmask = pio_irqmask;
+               host->cmd_datactrl = datactrl;
+               host->cmd_cmd = cmd;
+
+               host->dma.hdr.execute_func = msmsdcc_dma_exec_func;
+               host->dma.hdr.data = (void *)host;
                host->dma.busy = 1;
+
+               if (cmd) {
+                       msmsdcc_start_command_deferred(host, cmd, &c);
+                       host->cmd_c = c;
+               }
                msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr);
+       } else {
+               msmsdcc_writel(host, timeout, MMCIDATATIMER);
+
+               msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
+
+               msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
+               msmsdcc_writel(host, datactrl, MMCIDATACTRL);
+
+               if (cmd) {
+                       /* Daisy-chain the command if requested */
+                       msmsdcc_start_command(host, cmd, c);
+               }
        }
 }
 
 static void
 msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
 {
-       void __iomem *base = host->base;
-
-       if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
-               writel(0, base + MMCICOMMAND);
-               udelay(2 + ((5 * 1000000) / host->clk_rate));
-       }
-
-       c |= cmd->opcode | MCI_CPSM_ENABLE;
-
-       if (cmd->flags & MMC_RSP_PRESENT) {
-               if (cmd->flags & MMC_RSP_136)
-                       c |= MCI_CPSM_LONGRSP;
-               c |= MCI_CPSM_RESPONSE;
-       }
-
-       if (cmd->opcode == 17 || cmd->opcode == 18 ||
-           cmd->opcode == 24 || cmd->opcode == 25 ||
-           cmd->opcode == 53)
-               c |= MCI_CSPM_DATCMD;
-
        if (cmd == cmd->mrq->stop)
                c |= MCI_CSPM_MCIABORT;
 
-       host->curr.cmd = cmd;
-
        host->stats.cmds++;
 
-       writel(cmd->arg, base + MMCIARGUMENT);
-       writel(c, base + MMCICOMMAND);
+       msmsdcc_start_command_deferred(host, cmd, &c);
+       msmsdcc_start_command_exec(host, cmd->arg, c);
 }
 
 static void
@@ -421,13 +563,11 @@ msmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data,
 static int
 msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
 {
-       void __iomem    *base = host->base;
        uint32_t        *ptr = (uint32_t *) buffer;
        int             count = 0;
 
-       while (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) {
-
-               *ptr = readl(base + MMCIFIFO + (count % MCI_FIFOSIZE));
+       while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) {
+               *ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE));
                ptr++;
                count += sizeof(uint32_t);
 
@@ -459,7 +599,7 @@ msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
                if (remain == 0)
                        break;
 
-               status = readl(base + MMCISTATUS);
+               status = msmsdcc_readl(host, MMCISTATUS);
        } while (status & MCI_TXFIFOHALFEMPTY);
 
        return ptr - buffer;
@@ -469,7 +609,7 @@ static int
 msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
 {
        while (maxspin) {
-               if ((readl(host->base + MMCISTATUS) & mask))
+               if ((msmsdcc_readl(host, MMCISTATUS) & mask))
                        return 0;
                udelay(1);
                --maxspin;
@@ -477,14 +617,13 @@ msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
        return -ETIMEDOUT;
 }
 
-static int
+static irqreturn_t
 msmsdcc_pio_irq(int irq, void *dev_id)
 {
        struct msmsdcc_host     *host = dev_id;
-       void __iomem            *base = host->base;
        uint32_t                status;
 
-       status = readl(base + MMCISTATUS);
+       status = msmsdcc_readl(host, MMCISTATUS);
 
        do {
                unsigned long flags;
@@ -539,14 +678,14 @@ msmsdcc_pio_irq(int irq, void *dev_id)
                        host->pio.sg_off = 0;
                }
 
-               status = readl(base + MMCISTATUS);
+               status = msmsdcc_readl(host, MMCISTATUS);
        } while (1);
 
        if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
-               writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
+               msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);
 
        if (!host->curr.xfer_remain)
-               writel(0, base + MMCIMASK1);
+               msmsdcc_writel(host, 0, MMCIMASK1);
 
        return IRQ_HANDLED;
 }
@@ -554,15 +693,13 @@ msmsdcc_pio_irq(int irq, void *dev_id)
 static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
 {
        struct mmc_command *cmd = host->curr.cmd;
-       void __iomem       *base = host->base;
 
        host->curr.cmd = NULL;
-       cmd->resp[0] = readl(base + MMCIRESPONSE0);
-       cmd->resp[1] = readl(base + MMCIRESPONSE1);
-       cmd->resp[2] = readl(base + MMCIRESPONSE2);
-       cmd->resp[3] = readl(base + MMCIRESPONSE3);
+       cmd->resp[0] = msmsdcc_readl(host, MMCIRESPONSE0);
+       cmd->resp[1] = msmsdcc_readl(host, MMCIRESPONSE1);
+       cmd->resp[2] = msmsdcc_readl(host, MMCIRESPONSE2);
+       cmd->resp[3] = msmsdcc_readl(host, MMCIRESPONSE3);
 
-       del_timer(&host->command_timer);
        if (status & MCI_CMDTIMEOUT) {
                cmd->error = -ETIMEDOUT;
        } else if (status & MCI_CMDCRCFAIL &&
@@ -580,8 +717,10 @@ static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
                        msmsdcc_request_end(host, cmd->mrq);
                } else /* host->data == NULL */
                        msmsdcc_request_end(host, cmd->mrq);
-       } else if (!(cmd->data->flags & MMC_DATA_READ))
-               msmsdcc_start_data(host, cmd->data);
+       } else if (cmd->data)
+               if (!(cmd->data->flags & MMC_DATA_READ))
+                       msmsdcc_start_data(host, cmd->data,
+                                               NULL, 0);
 }
 
 static void
@@ -590,6 +729,11 @@ msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
 {
        struct mmc_data *data = host->curr.data;
 
+       if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
+                     MCI_CMDTIMEOUT) && host->curr.cmd) {
+               msmsdcc_do_cmdirq(host, status);
+       }
+
        if (!data)
                return;
 
@@ -602,7 +746,8 @@ msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
                        msm_dmov_stop_cmd(host->dma.channel,
                                          &host->dma.hdr, 0);
                else {
-                       msmsdcc_stop_data(host);
+                       if (host->curr.data)
+                               msmsdcc_stop_data(host);
                        if (!data->stop)
                                msmsdcc_request_end(host, data->mrq);
                        else
@@ -657,17 +802,18 @@ msmsdcc_irq(int irq, void *dev_id)
        spin_lock(&host->lock);
 
        do {
-               status = readl(base + MMCISTATUS);
+               status = msmsdcc_readl(host, MMCISTATUS);
+               status &= (msmsdcc_readl(host, MMCIMASK0) |
+                                             MCI_DATABLOCKENDMASK);
+               msmsdcc_writel(host, status, MMCICLEAR);
 
-               status &= (readl(base + MMCIMASK0) | MCI_DATABLOCKENDMASK);
-               writel(status, base + MMCICLEAR);
+               if (status & MCI_SDIOINTR)
+                       status &= ~MCI_SDIOINTR;
 
-               msmsdcc_handle_irq_data(host, status, base);
+               if (!status)
+                       break;
 
-               if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
-                             MCI_CMDTIMEOUT) && host->curr.cmd) {
-                       msmsdcc_do_cmdirq(host, status);
-               }
+               msmsdcc_handle_irq_data(host, status, base);
 
                if (status & MCI_SDIOINTOPER) {
                        cardint = 1;
@@ -714,24 +860,27 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
                return;
        }
 
+       msmsdcc_enable_clocks(host);
+
        host->curr.mrq = mrq;
 
        if (mrq->data && mrq->data->flags & MMC_DATA_READ)
-               msmsdcc_start_data(host, mrq->data);
-
-       msmsdcc_start_command(host, mrq->cmd, 0);
+               /* Queue/read data, daisy-chain command when data starts */
+               msmsdcc_start_data(host, mrq->data, mrq->cmd, 0);
+       else
+               msmsdcc_start_command(host, mrq->cmd, 0);
 
        if (host->cmdpoll && !msmsdcc_spin_on_status(host,
                                MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
                                CMD_SPINMAX)) {
-               uint32_t status = readl(host->base + MMCISTATUS);
+               uint32_t status = msmsdcc_readl(host, MMCISTATUS);
                msmsdcc_do_cmdirq(host, status);
-               writel(MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
-                      host->base + MMCICLEAR);
+               msmsdcc_writel(host,
+                              MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
+                              MMCICLEAR);
                host->stats.cmdpoll_hits++;
        } else {
                host->stats.cmdpoll_misses++;
-               mod_timer(&host->command_timer, jiffies + HZ);
        }
        spin_unlock_irqrestore(&host->lock, flags);
 }
@@ -742,14 +891,13 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        struct msmsdcc_host *host = mmc_priv(mmc);
        u32 clk = 0, pwr = 0;
        int rc;
+       unsigned long flags;
 
-       if (ios->clock) {
+       spin_lock_irqsave(&host->lock, flags);
 
-               if (!host->clks_on) {
-                       clk_enable(host->pclk);
-                       clk_enable(host->clk);
-                       host->clks_on = 1;
-               }
+       msmsdcc_enable_clocks(host);
+
+       if (ios->clock) {
                if (ios->clock != host->clk_rate) {
                        rc = clk_set_rate(host->clk, ios->clock);
                        if (rc < 0)
@@ -787,18 +935,16 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
                pwr |= MCI_OD;
 
-       writel(clk, host->base + MMCICLOCK);
+       msmsdcc_writel(host, clk, MMCICLOCK);
 
        if (host->pwr != pwr) {
                host->pwr = pwr;
-               writel(pwr, host->base + MMCIPOWER);
-       }
-
-       if (!(clk & MCI_CLK_ENABLE) && host->clks_on) {
-               clk_disable(host->clk);
-               clk_disable(host->pclk);
-               host->clks_on = 0;
+               msmsdcc_writel(host, pwr, MMCIPOWER);
        }
+#if BUSCLK_PWRSAVE
+       msmsdcc_disable_clocks(host, 1);
+#endif
+       spin_unlock_irqrestore(&host->lock, flags);
 }
 
 static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
@@ -809,13 +955,13 @@ static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 
        spin_lock_irqsave(&host->lock, flags);
        if (msmsdcc_sdioirq == 1) {
-               status = readl(host->base + MMCIMASK0);
+               status = msmsdcc_readl(host, MMCIMASK0);
                if (enable)
                        status |= MCI_SDIOINTOPERMASK;
                else
                        status &= ~MCI_SDIOINTOPERMASK;
                host->saved_irq0mask = status;
-               writel(status, host->base + MMCIMASK0);
+               msmsdcc_writel(host, status, MMCIMASK0);
        }
        spin_unlock_irqrestore(&host->lock, flags);
 }
@@ -875,42 +1021,13 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id)
        msmsdcc_check_status((unsigned long) host);
 }
 
-/*
- * called when a command expires.
- * Dump some debugging, and then error
- * out the transaction.
- */
 static void
-msmsdcc_command_expired(unsigned long _data)
+msmsdcc_busclk_expired(unsigned long _data)
 {
        struct msmsdcc_host     *host = (struct msmsdcc_host *) _data;
-       struct mmc_request      *mrq;
-       unsigned long           flags;
-
-       spin_lock_irqsave(&host->lock, flags);
-       mrq = host->curr.mrq;
-
-       if (!mrq) {
-               pr_info("%s: Command expiry misfire\n",
-                       mmc_hostname(host->mmc));
-               spin_unlock_irqrestore(&host->lock, flags);
-               return;
-       }
-
-       pr_err("%s: Command timeout (%p %p %p %p)\n",
-              mmc_hostname(host->mmc), mrq, mrq->cmd,
-              mrq->data, host->dma.sg);
-
-       mrq->cmd->error = -ETIMEDOUT;
-       msmsdcc_stop_data(host);
 
-       writel(0, host->base + MMCICOMMAND);
-
-       host->curr.mrq = NULL;
-       host->curr.cmd = NULL;
-
-       spin_unlock_irqrestore(&host->lock, flags);
-       mmc_request_done(host->mmc, mrq);
+       if (host->clks_on)
+               msmsdcc_disable_clocks(host, 0);
 }
 
 static int
@@ -1012,6 +1129,7 @@ msmsdcc_probe(struct platform_device *pdev)
        host->pdev_id = pdev->id;
        host->plat = plat;
        host->mmc = mmc;
+       host->curr.cmd = NULL;
 
        host->cmdpoll = 1;
 
@@ -1027,36 +1145,35 @@ msmsdcc_probe(struct platform_device *pdev)
        host->dmares = dmares;
        spin_lock_init(&host->lock);
 
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+       if (plat->embedded_sdio)
+               mmc_set_embedded_sdio_data(mmc,
+                                          &plat->embedded_sdio->cis,
+                                          &plat->embedded_sdio->cccr,
+                                          plat->embedded_sdio->funcs,
+                                          plat->embedded_sdio->num_funcs);
+#endif
+
        /*
         * Setup DMA
         */
        msmsdcc_init_dma(host);
 
-       /*
-        * Setup main peripheral bus clock
-        */
+       /* Get our clocks */
        host->pclk = clk_get(&pdev->dev, "sdc_pclk");
        if (IS_ERR(host->pclk)) {
                ret = PTR_ERR(host->pclk);
                goto host_free;
        }
 
-       ret = clk_enable(host->pclk);
-       if (ret)
-               goto pclk_put;
-
-       host->pclk_rate = clk_get_rate(host->pclk);
-
-       /*
-        * Setup SDC MMC clock
-        */
        host->clk = clk_get(&pdev->dev, "sdc_clk");
        if (IS_ERR(host->clk)) {
                ret = PTR_ERR(host->clk);
-               goto pclk_disable;
+               goto pclk_put;
        }
 
-       ret = clk_enable(host->clk);
+       /* Enable clocks */
+       ret = msmsdcc_enable_clocks(host);
        if (ret)
                goto clk_put;
 
@@ -1066,10 +1183,9 @@ msmsdcc_probe(struct platform_device *pdev)
                goto clk_disable;
        }
 
+       host->pclk_rate = clk_get_rate(host->pclk);
        host->clk_rate = clk_get_rate(host->clk);
 
-       host->clks_on = 1;
-
        /*
         * Setup MMC host structure
         */
@@ -1092,10 +1208,10 @@ msmsdcc_probe(struct platform_device *pdev)
        mmc->max_req_size = 33554432;   /* MCI_DATA_LENGTH is 25 bits */
        mmc->max_seg_size = mmc->max_req_size;
 
-       writel(0, host->base + MMCIMASK0);
-       writel(0x5e007ff, host->base + MMCICLEAR); /* Add: 1 << 25 */
+       msmsdcc_writel(host, 0, MMCIMASK0);
+       msmsdcc_writel(host, 0x5e007ff, MMCICLEAR);
 
-       writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+       msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
        host->saved_irq0mask = MCI_IRQENABLE;
 
        /*
@@ -1137,13 +1253,9 @@ msmsdcc_probe(struct platform_device *pdev)
                host->eject = !host->oldstat;
        }
 
-       /*
-        * Setup a command timer. We currently need this due to
-        * some 'strange' timeout / error handling situations.
-        */
-       init_timer(&host->command_timer);
-       host->command_timer.data = (unsigned long) host;
-       host->command_timer.function = msmsdcc_command_expired;
+       init_timer(&host->busclk_timer);
+       host->busclk_timer.data = (unsigned long) host;
+       host->busclk_timer.function = msmsdcc_busclk_expired;
 
        ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
                          DRIVER_NAME " (cmd)", host);
@@ -1181,6 +1293,9 @@ msmsdcc_probe(struct platform_device *pdev)
        if (host->timer.function)
                pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc));
 
+#if BUSCLK_PWRSAVE
+       msmsdcc_disable_clocks(host, 1);
+#endif
        return 0;
  cmd_irq_free:
        free_irq(cmd_irqres->start, host);
@@ -1188,11 +1303,9 @@ msmsdcc_probe(struct platform_device *pdev)
        if (host->stat_irq)
                free_irq(host->stat_irq, host);
  clk_disable:
-       clk_disable(host->clk);
+       msmsdcc_disable_clocks(host, 0);
  clk_put:
        clk_put(host->clk);
- pclk_disable:
-       clk_disable(host->pclk);
  pclk_put:
        clk_put(host->pclk);
  host_free:
@@ -1215,15 +1328,10 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
 
                if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
                        rc = mmc_suspend_host(mmc, state);
-               if (!rc) {
-                       writel(0, host->base + MMCIMASK0);
-
-                       if (host->clks_on) {
-                               clk_disable(host->clk);
-                               clk_disable(host->pclk);
-                               host->clks_on = 0;
-                       }
-               }
+               if (!rc)
+                       msmsdcc_writel(host, 0, MMCIMASK0);
+               if (host->clks_on)
+                       msmsdcc_disable_clocks(host, 0);
        }
        return rc;
 }
@@ -1232,27 +1340,21 @@ static int
 msmsdcc_resume(struct platform_device *dev)
 {
        struct mmc_host *mmc = mmc_get_drvdata(dev);
-       unsigned long flags;
 
        if (mmc) {
                struct msmsdcc_host *host = mmc_priv(mmc);
 
-               spin_lock_irqsave(&host->lock, flags);
+               msmsdcc_enable_clocks(host);
 
-               if (!host->clks_on) {
-                       clk_enable(host->pclk);
-                       clk_enable(host->clk);
-                       host->clks_on = 1;
-               }
-
-               writel(host->saved_irq0mask, host->base + MMCIMASK0);
-
-               spin_unlock_irqrestore(&host->lock, flags);
+               msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
 
                if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
                        mmc_resume_host(mmc);
                if (host->stat_irq)
                        enable_irq(host->stat_irq);
+#if BUSCLK_PWRSAVE
+               msmsdcc_disable_clocks(host, 1);
+#endif
        }
        return 0;
 }