]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/mmc/host/omap_hsmmc.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[mv-sheeva.git] / drivers / mmc / host / omap_hsmmc.c
index 016914c2c4e715918db9f2287c7b6233bbfc85af..4487cc0979112ea71e412f2c722ad45282cd3328 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/timer.h>
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/core.h>
 #include <linux/io.h>
 #include <linux/semaphore.h>
 #include <mach/dma.h>
 #define OMAP_MMC1_DEVID                0
 #define OMAP_MMC2_DEVID                1
 #define OMAP_MMC3_DEVID                2
+#define OMAP_MMC4_DEVID                3
+#define OMAP_MMC5_DEVID                4
 
 #define MMC_TIMEOUT_MS         20
 #define OMAP_MMC_MASTER_CLOCK  96000000
 
 /* Timeouts for entering power saving states on inactivity, msec */
 #define OMAP_MMC_DISABLED_TIMEOUT      100
-#define OMAP_MMC_OFF_TIMEOUT           1000
+#define OMAP_MMC_SLEEP_TIMEOUT         1000
+#define OMAP_MMC_OFF_TIMEOUT           8000
 
 /*
  * One controller can have multiple slots, like on some omap boards using
 #define OMAP_HSMMC_WRITE(base, reg, val) \
        __raw_writel((val), (base) + OMAP_HSMMC_##reg)
 
-struct mmc_omap_host {
+struct omap_hsmmc_host {
        struct  device          *dev;
        struct  mmc_host        *mmc;
        struct  mmc_request     *mrq;
@@ -146,6 +150,8 @@ struct mmc_omap_host {
        struct  work_struct     mmc_carddetect_work;
        void    __iomem         *base;
        resource_size_t         mapbase;
+       spinlock_t              irq_lock; /* Prevent races with irq handler */
+       unsigned long           flags;
        unsigned int            id;
        unsigned int            dma_len;
        unsigned int            dma_sg_idx;
@@ -158,10 +164,13 @@ struct mmc_omap_host {
        int                     use_dma, dma_ch;
        int                     dma_line_tx, dma_line_rx;
        int                     slot_id;
-       int                     dbclk_enabled;
+       int                     got_dbclk;
        int                     response_busy;
        int                     context_loss;
        int                     dpm_state;
+       int                     vdd;
+       int                     protect_card;
+       int                     reqs_blocked;
 
        struct  omap_mmc_platform_data  *pdata;
 };
@@ -169,7 +178,7 @@ struct mmc_omap_host {
 /*
  * Stop clock to the card
  */
-static void omap_mmc_stop_clock(struct mmc_omap_host *host)
+static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
 {
        OMAP_HSMMC_WRITE(host->base, SYSCTL,
                OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
@@ -183,7 +192,7 @@ static void omap_mmc_stop_clock(struct mmc_omap_host *host)
  * Restore the MMC host context, if it was lost as result of a
  * power state change.
  */
-static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 {
        struct mmc_ios *ios = &host->mmc->ios;
        struct omap_mmc_platform_data *pdata = host->pdata;
@@ -311,7 +320,7 @@ out:
 /*
  * Save the MMC host context (store the number of power state changes so far).
  */
-static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
 {
        struct omap_mmc_platform_data *pdata = host->pdata;
        int context_loss;
@@ -326,12 +335,12 @@ static void omap_mmc_save_ctx(struct mmc_omap_host *host)
 
 #else
 
-static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 {
        return 0;
 }
 
-static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
 {
 }
 
@@ -341,11 +350,14 @@ static void omap_mmc_save_ctx(struct mmc_omap_host *host)
  * Send init stream sequence to card
  * before sending IDLE command
  */
-static void send_init_stream(struct mmc_omap_host *host)
+static void send_init_stream(struct omap_hsmmc_host *host)
 {
        int reg = 0;
        unsigned long timeout;
 
+       if (host->protect_card)
+               return;
+
        disable_irq(host->irq);
        OMAP_HSMMC_WRITE(host->base, CON,
                OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM);
@@ -357,51 +369,53 @@ static void send_init_stream(struct mmc_omap_host *host)
 
        OMAP_HSMMC_WRITE(host->base, CON,
                OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM);
+
+       OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+       OMAP_HSMMC_READ(host->base, STAT);
+
        enable_irq(host->irq);
 }
 
 static inline
-int mmc_omap_cover_is_closed(struct mmc_omap_host *host)
+int omap_hsmmc_cover_is_closed(struct omap_hsmmc_host *host)
 {
        int r = 1;
 
-       if (host->pdata->slots[host->slot_id].get_cover_state)
-               r = host->pdata->slots[host->slot_id].get_cover_state(host->dev,
-                       host->slot_id);
+       if (mmc_slot(host).get_cover_state)
+               r = mmc_slot(host).get_cover_state(host->dev, host->slot_id);
        return r;
 }
 
 static ssize_t
-mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
+omap_hsmmc_show_cover_switch(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
        struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
-       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
 
-       return sprintf(buf, "%s\n", mmc_omap_cover_is_closed(host) ? "closed" :
-                      "open");
+       return sprintf(buf, "%s\n",
+                       omap_hsmmc_cover_is_closed(host) ? "closed" : "open");
 }
 
-static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+static DEVICE_ATTR(cover_switch, S_IRUGO, omap_hsmmc_show_cover_switch, NULL);
 
 static ssize_t
-mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
+omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr,
                        char *buf)
 {
        struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
-       struct mmc_omap_host *host = mmc_priv(mmc);
-       struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id];
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
 
-       return sprintf(buf, "%s\n", slot.name);
+       return sprintf(buf, "%s\n", mmc_slot(host).name);
 }
 
-static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
+static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
 
 /*
  * Configure the response type and send the cmd.
  */
 static void
-mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
+omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
        struct mmc_data *data)
 {
        int cmdreg = 0, resptype = 0, cmdtype = 0;
@@ -454,12 +468,20 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
        if (host->use_dma)
                cmdreg |= DMA_EN;
 
+       /*
+        * In an interrupt context (i.e. STOP command), the spinlock is unlocked
+        * by the interrupt handler, otherwise (i.e. for a new request) it is
+        * unlocked here.
+        */
+       if (!in_interrupt())
+               spin_unlock_irqrestore(&host->irq_lock, host->flags);
+
        OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
        OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
 }
 
 static int
-mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
+omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
 {
        if (data->flags & MMC_DATA_WRITE)
                return DMA_TO_DEVICE;
@@ -471,11 +493,18 @@ mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
  * Notify the transfer complete to MMC core
  */
 static void
-mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
+omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
 {
        if (!data) {
                struct mmc_request *mrq = host->mrq;
 
+               /* TC before CC from CMD6 - don't know why, but it happens */
+               if (host->cmd && host->cmd->opcode == 6 &&
+                   host->response_busy) {
+                       host->response_busy = 0;
+                       return;
+               }
+
                host->mrq = NULL;
                mmc_request_done(host->mmc, mrq);
                return;
@@ -485,7 +514,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
 
        if (host->use_dma && host->dma_ch != -1)
                dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
-                       mmc_omap_get_dma_dir(host, data));
+                       omap_hsmmc_get_dma_dir(host, data));
 
        if (!data->error)
                data->bytes_xfered += data->blocks * (data->blksz);
@@ -497,14 +526,14 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
                mmc_request_done(host->mmc, data->mrq);
                return;
        }
-       mmc_omap_start_command(host, data->stop, NULL);
+       omap_hsmmc_start_command(host, data->stop, NULL);
 }
 
 /*
  * Notify the core about command completion
  */
 static void
-mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
+omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
 {
        host->cmd = NULL;
 
@@ -529,13 +558,13 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
 /*
  * DMA clean up for command errors
  */
-static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
+static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 {
        host->data->error = errno;
 
        if (host->use_dma && host->dma_ch != -1) {
                dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
-                       mmc_omap_get_dma_dir(host, host->data));
+                       omap_hsmmc_get_dma_dir(host, host->data));
                omap_free_dma(host->dma_ch);
                host->dma_ch = -1;
                up(&host->sem);
@@ -547,10 +576,10 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
  * Readable error output
  */
 #ifdef CONFIG_MMC_DEBUG
-static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
+static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
 {
        /* --- means reserved bit without definition at documentation */
-       static const char *mmc_omap_status_bits[] = {
+       static const char *omap_hsmmc_status_bits[] = {
                "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ",
                "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
                "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---",
@@ -563,9 +592,9 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
        len = sprintf(buf, "MMC IRQ 0x%x :", status);
        buf += len;
 
-       for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
+       for (i = 0; i < ARRAY_SIZE(omap_hsmmc_status_bits); i++)
                if (status & (1 << i)) {
-                       len = sprintf(buf, " %s", mmc_omap_status_bits[i]);
+                       len = sprintf(buf, " %s", omap_hsmmc_status_bits[i]);
                        buf += len;
                }
 
@@ -580,8 +609,8 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
  *  SRC or SRD bit of SYSCTL register
  * Can be called from interrupt context
  */
-static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host,
-               unsigned long bit)
+static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
+                                                  unsigned long bit)
 {
        unsigned long i = 0;
        unsigned long limit = (loops_per_jiffy *
@@ -603,17 +632,20 @@ static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host,
 /*
  * MMC controller IRQ handler
  */
-static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
+static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
 {
-       struct mmc_omap_host *host = dev_id;
+       struct omap_hsmmc_host *host = dev_id;
        struct mmc_data *data;
        int end_cmd = 0, end_trans = 0, status;
 
+       spin_lock(&host->irq_lock);
+
        if (host->mrq == NULL) {
                OMAP_HSMMC_WRITE(host->base, STAT,
                        OMAP_HSMMC_READ(host->base, STAT));
                /* Flush posted write */
                OMAP_HSMMC_READ(host->base, STAT);
+               spin_unlock(&host->irq_lock);
                return IRQ_HANDLED;
        }
 
@@ -623,13 +655,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
 
        if (status & ERR) {
 #ifdef CONFIG_MMC_DEBUG
-               mmc_omap_report_irq(host, status);
+               omap_hsmmc_report_irq(host, status);
 #endif
                if ((status & CMD_TIMEOUT) ||
                        (status & CMD_CRC)) {
                        if (host->cmd) {
                                if (status & CMD_TIMEOUT) {
-                                       mmc_omap_reset_controller_fsm(host, SRC);
+                                       omap_hsmmc_reset_controller_fsm(host,
+                                                                       SRC);
                                        host->cmd->error = -ETIMEDOUT;
                                } else {
                                        host->cmd->error = -EILSEQ;
@@ -638,9 +671,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                        }
                        if (host->data || host->response_busy) {
                                if (host->data)
-                                       mmc_dma_cleanup(host, -ETIMEDOUT);
+                                       omap_hsmmc_dma_cleanup(host,
+                                                               -ETIMEDOUT);
                                host->response_busy = 0;
-                               mmc_omap_reset_controller_fsm(host, SRD);
+                               omap_hsmmc_reset_controller_fsm(host, SRD);
                        }
                }
                if ((status & DATA_TIMEOUT) ||
@@ -650,11 +684,11 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                                                -ETIMEDOUT : -EILSEQ;
 
                                if (host->data)
-                                       mmc_dma_cleanup(host, err);
+                                       omap_hsmmc_dma_cleanup(host, err);
                                else
                                        host->mrq->cmd->error = err;
                                host->response_busy = 0;
-                               mmc_omap_reset_controller_fsm(host, SRD);
+                               omap_hsmmc_reset_controller_fsm(host, SRD);
                                end_trans = 1;
                        }
                }
@@ -673,14 +707,16 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        OMAP_HSMMC_READ(host->base, STAT);
 
        if (end_cmd || ((status & CC) && host->cmd))
-               mmc_omap_cmd_done(host, host->cmd);
-       if (end_trans || (status & TC))
-               mmc_omap_xfer_done(host, data);
+               omap_hsmmc_cmd_done(host, host->cmd);
+       if ((end_trans || (status & TC)) && host->mrq)
+               omap_hsmmc_xfer_done(host, data);
+
+       spin_unlock(&host->irq_lock);
 
        return IRQ_HANDLED;
 }
 
-static void set_sd_bus_power(struct mmc_omap_host *host)
+static void set_sd_bus_power(struct omap_hsmmc_host *host)
 {
        unsigned long i;
 
@@ -700,7 +736,7 @@ static void set_sd_bus_power(struct mmc_omap_host *host)
  * The MMC2 transceiver controls are used instead of DAT4..DAT7.
  * Some chips, like eMMC ones, use internal transceivers.
  */
-static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
+static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
 {
        u32 reg_val = 0;
        int ret;
@@ -708,22 +744,24 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
        /* Disable the clocks */
        clk_disable(host->fclk);
        clk_disable(host->iclk);
-       clk_disable(host->dbclk);
+       if (host->got_dbclk)
+               clk_disable(host->dbclk);
 
        /* Turn the power off */
        ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
-       if (ret != 0)
-               goto err;
 
        /* Turn the power ON with given VDD 1.8 or 3.0v */
-       ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
+       if (!ret)
+               ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
+                                              vdd);
+       clk_enable(host->iclk);
+       clk_enable(host->fclk);
+       if (host->got_dbclk)
+               clk_enable(host->dbclk);
+
        if (ret != 0)
                goto err;
 
-       clk_enable(host->fclk);
-       clk_enable(host->iclk);
-       clk_enable(host->dbclk);
-
        OMAP_HSMMC_WRITE(host->base, HCTL,
                OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
        reg_val = OMAP_HSMMC_READ(host->base, HCTL);
@@ -731,7 +769,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
        /*
         * If a MMC dual voltage card is detected, the set_ios fn calls
         * this fn with VDD bit set for 1.8V. Upon card removal from the
-        * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
+        * slot, omap_hsmmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
         *
         * Cope with a bit of slop in the range ... per data sheets:
         *  - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max,
@@ -757,13 +795,37 @@ err:
        return ret;
 }
 
+/* Protect the card while the cover is open */
+static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
+{
+       if (!mmc_slot(host).get_cover_state)
+               return;
+
+       host->reqs_blocked = 0;
+       if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
+               if (host->protect_card) {
+                       printk(KERN_INFO "%s: cover is closed, "
+                                        "card is now accessible\n",
+                                        mmc_hostname(host->mmc));
+                       host->protect_card = 0;
+               }
+       } else {
+               if (!host->protect_card) {
+                       printk(KERN_INFO "%s: cover is open, "
+                                        "card is now inaccessible\n",
+                                        mmc_hostname(host->mmc));
+                       host->protect_card = 1;
+               }
+       }
+}
+
 /*
  * Work Item to notify the core about card insertion/removal
  */
-static void mmc_omap_detect(struct work_struct *work)
+static void omap_hsmmc_detect(struct work_struct *work)
 {
-       struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
-                                               mmc_carddetect_work);
+       struct omap_hsmmc_host *host =
+               container_of(work, struct omap_hsmmc_host, mmc_carddetect_work);
        struct omap_mmc_slot_data *slot = &mmc_slot(host);
        int carddetect;
 
@@ -772,17 +834,20 @@ static void mmc_omap_detect(struct work_struct *work)
 
        sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
 
-       if (mmc_slot(host).card_detect)
+       if (slot->card_detect)
                carddetect = slot->card_detect(slot->card_detect_irq);
-       else
+       else {
+               omap_hsmmc_protect_card(host);
                carddetect = -ENOSYS;
+       }
 
        if (carddetect) {
                mmc_detect_change(host->mmc, (HZ * 200) / 1000);
        } else {
                mmc_host_enable(host->mmc);
-               mmc_omap_reset_controller_fsm(host, SRD);
+               omap_hsmmc_reset_controller_fsm(host, SRD);
                mmc_host_lazy_disable(host->mmc);
+
                mmc_detect_change(host->mmc, (HZ * 50) / 1000);
        }
 }
@@ -790,9 +855,9 @@ static void mmc_omap_detect(struct work_struct *work)
 /*
  * ISR for handling card insertion and removal
  */
-static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
+static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id)
 {
-       struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;
+       struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id;
 
        if (host->suspended)
                return IRQ_HANDLED;
@@ -801,7 +866,7 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
+static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
                                     struct mmc_data *data)
 {
        int sync_dev;
@@ -813,7 +878,7 @@ static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
        return sync_dev;
 }
 
-static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
+static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
                                       struct mmc_data *data,
                                       struct scatterlist *sgl)
 {
@@ -827,7 +892,7 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
                        sg_dma_address(sgl), 0, 0);
        } else {
                omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-                                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
                omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
                        sg_dma_address(sgl), 0, 0);
        }
@@ -837,7 +902,7 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
 
        omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
                        blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
-                       mmc_omap_get_dma_sync_dev(host, data),
+                       omap_hsmmc_get_dma_sync_dev(host, data),
                        !(data->flags & MMC_DATA_WRITE));
 
        omap_start_dma(dma_ch);
@@ -846,9 +911,9 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
 /*
  * DMA call back function
  */
-static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
+static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data)
 {
-       struct mmc_omap_host *host = data;
+       struct omap_hsmmc_host *host = data;
 
        if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
                dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
@@ -859,7 +924,7 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
        host->dma_sg_idx++;
        if (host->dma_sg_idx < host->dma_len) {
                /* Fire up the next transfer. */
-               mmc_omap_config_dma_params(host, host->data,
+               omap_hsmmc_config_dma_params(host, host->data,
                                           host->data->sg + host->dma_sg_idx);
                return;
        }
@@ -876,8 +941,8 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
 /*
  * Routine to configure and start DMA for the MMC card
  */
-static int
-mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
+static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
+                                       struct mmc_request *req)
 {
        int dma_ch = 0, ret = 0, err = 1, i;
        struct mmc_data *data = req->data;
@@ -914,8 +979,8 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
                        return err;
        }
 
-       ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
-                              mmc_omap_dma_cb,host, &dma_ch);
+       ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
+                              "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
        if (ret != 0) {
                dev_err(mmc_dev(host->mmc),
                        "%s: omap_request_dma() failed with %d\n",
@@ -924,17 +989,18 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
        }
 
        host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-                       data->sg_len, mmc_omap_get_dma_dir(host, data));
+                       data->sg_len, omap_hsmmc_get_dma_dir(host, data));
        host->dma_ch = dma_ch;
        host->dma_sg_idx = 0;
 
-       mmc_omap_config_dma_params(host, data, data->sg);
+       omap_hsmmc_config_dma_params(host, data, data->sg);
 
        return 0;
 }
 
-static void set_data_timeout(struct mmc_omap_host *host,
-                            struct mmc_request *req)
+static void set_data_timeout(struct omap_hsmmc_host *host,
+                            unsigned int timeout_ns,
+                            unsigned int timeout_clks)
 {
        unsigned int timeout, cycle_ns;
        uint32_t reg, clkd, dto = 0;
@@ -945,8 +1011,8 @@ static void set_data_timeout(struct mmc_omap_host *host,
                clkd = 1;
 
        cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd);
-       timeout = req->data->timeout_ns / cycle_ns;
-       timeout += req->data->timeout_clks;
+       timeout = timeout_ns / cycle_ns;
+       timeout += timeout_clks;
        if (timeout) {
                while ((timeout & 0x80000000) == 0) {
                        dto += 1;
@@ -973,22 +1039,28 @@ static void set_data_timeout(struct mmc_omap_host *host,
  * Configure block length for MMC/SD cards and initiate the transfer.
  */
 static int
-mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
+omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
 {
        int ret;
        host->data = req->data;
 
        if (req->data == NULL) {
                OMAP_HSMMC_WRITE(host->base, BLK, 0);
+               /*
+                * Set an arbitrary 100ms data timeout for commands with
+                * busy signal.
+                */
+               if (req->cmd->flags & MMC_RSP_BUSY)
+                       set_data_timeout(host, 100000000U, 0);
                return 0;
        }
 
        OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
                                        | (req->data->blocks << 16));
-       set_data_timeout(host, req);
+       set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);
 
        if (host->use_dma) {
-               ret = mmc_omap_start_dma_transfer(host, req);
+               ret = omap_hsmmc_start_dma_transfer(host, req);
                if (ret != 0) {
                        dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
                        return ret;
@@ -1000,31 +1072,63 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 /*
  * Request function. for read/write operation
  */
-static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
+static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
        int err;
 
+       /*
+        * Prevent races with the interrupt handler because of unexpected
+        * interrupts, but not if we are already in interrupt context i.e.
+        * retries.
+        */
+       if (!in_interrupt()) {
+               spin_lock_irqsave(&host->irq_lock, host->flags);
+               /*
+                * Protect the card from I/O if there is a possibility
+                * it can be removed.
+                */
+               if (host->protect_card) {
+                       if (host->reqs_blocked < 3) {
+                               /*
+                                * Ensure the controller is left in a consistent
+                                * state by resetting the command and data state
+                                * machines.
+                                */
+                               omap_hsmmc_reset_controller_fsm(host, SRD);
+                               omap_hsmmc_reset_controller_fsm(host, SRC);
+                               host->reqs_blocked += 1;
+                       }
+                       req->cmd->error = -EBADF;
+                       if (req->data)
+                               req->data->error = -EBADF;
+                       spin_unlock_irqrestore(&host->irq_lock, host->flags);
+                       mmc_request_done(mmc, req);
+                       return;
+               } else if (host->reqs_blocked)
+                       host->reqs_blocked = 0;
+       }
        WARN_ON(host->mrq != NULL);
        host->mrq = req;
-       err = mmc_omap_prepare_data(host, req);
+       err = omap_hsmmc_prepare_data(host, req);
        if (err) {
                req->cmd->error = err;
                if (req->data)
                        req->data->error = err;
                host->mrq = NULL;
+               if (!in_interrupt())
+                       spin_unlock_irqrestore(&host->irq_lock, host->flags);
                mmc_request_done(mmc, req);
                return;
        }
 
-       mmc_omap_start_command(host, req->cmd, req->data);
+       omap_hsmmc_start_command(host, req->cmd, req->data);
 }
 
-
 /* Routine to configure clock values. Exposed API to core */
-static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
        u16 dsor = 0;
        unsigned long regval;
        unsigned long timeout;
@@ -1038,10 +1142,12 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                case MMC_POWER_OFF:
                        mmc_slot(host).set_power(host->dev, host->slot_id,
                                                 0, 0);
+                       host->vdd = 0;
                        break;
                case MMC_POWER_UP:
                        mmc_slot(host).set_power(host->dev, host->slot_id,
                                                 1, ios->vdd);
+                       host->vdd = ios->vdd;
                        break;
                case MMC_POWER_ON:
                        do_send_init_stream = 1;
@@ -1081,8 +1187,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                                 * MMC_POWER_UP upon recalculating the voltage.
                                 * vdd 1.8v.
                                 */
-                               if (omap_mmc_switch_opcond(host, ios->vdd) != 0)
-                                       dev_dbg(mmc_dev(host->mmc),
+                       if (omap_hsmmc_switch_opcond(host, ios->vdd) != 0)
+                               dev_dbg(mmc_dev(host->mmc),
                                                "Switch operation failed\n");
                }
        }
@@ -1098,7 +1204,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                if (dsor > 250)
                        dsor = 250;
        }
-       omap_mmc_stop_clock(host);
+       omap_hsmmc_stop_clock(host);
        regval = OMAP_HSMMC_READ(host->base, SYSCTL);
        regval = regval & ~(CLKD_MASK);
        regval = regval | (dsor << 6) | (DTO << 16);
@@ -1132,25 +1238,23 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 static int omap_hsmmc_get_cd(struct mmc_host *mmc)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
-       struct omap_mmc_platform_data *pdata = host->pdata;
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
 
-       if (!pdata->slots[0].card_detect)
+       if (!mmc_slot(host).card_detect)
                return -ENOSYS;
-       return pdata->slots[0].card_detect(pdata->slots[0].card_detect_irq);
+       return mmc_slot(host).card_detect(mmc_slot(host).card_detect_irq);
 }
 
 static int omap_hsmmc_get_ro(struct mmc_host *mmc)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
-       struct omap_mmc_platform_data *pdata = host->pdata;
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
 
-       if (!pdata->slots[0].get_ro)
+       if (!mmc_slot(host).get_ro)
                return -ENOSYS;
-       return pdata->slots[0].get_ro(host->dev, 0);
+       return mmc_slot(host).get_ro(host->dev, 0);
 }
 
-static void omap_hsmmc_init(struct mmc_omap_host *host)
+static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
 {
        u32 hctl, capa, value;
 
@@ -1179,24 +1283,26 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
 
 /*
  * Dynamic power saving handling, FSM:
- *   ENABLED -> DISABLED -> OFF
- *     ^___________|          |
- *     |______________________|
+ *   ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF
+ *     ^___________|          |                      |
+ *     |______________________|______________________|
  *
  * ENABLED:   mmc host is fully functional
  * DISABLED:  fclk is off
- * OFF:       fclk is off,voltage regulator is off
+ * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep
+ * REGSLEEP:  fclk is off, voltage regulator is asleep
+ * OFF:       fclk is off, voltage regulator is off
  *
  * Transition handlers return the timeout for the next state transition
  * or negative error.
  */
 
-enum {ENABLED = 0, DISABLED, OFF};
+enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};
 
 /* Handler for [ENABLED -> DISABLED] transition */
-static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
+static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host)
 {
-       omap_mmc_save_ctx(host);
+       omap_hsmmc_context_save(host);
        clk_disable(host->fclk);
        host->dpm_state = DISABLED;
 
@@ -1205,42 +1311,73 @@ static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
        if (host->power_mode == MMC_POWER_OFF)
                return 0;
 
-       return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+       return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT);
 }
 
-/* Handler for [DISABLED -> OFF] transition */
-static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
+/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
+static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host)
 {
-       int new_state;
-
-       dev_dbg(mmc_dev(host->mmc), "DISABLED -> OFF\n");
+       int err, new_state;
 
        if (!mmc_try_claim_host(host->mmc))
                return 0;
 
        clk_enable(host->fclk);
+       omap_hsmmc_context_restore(host);
+       if (mmc_card_can_sleep(host->mmc)) {
+               err = mmc_card_sleep(host->mmc);
+               if (err < 0) {
+                       clk_disable(host->fclk);
+                       mmc_release_host(host->mmc);
+                       return err;
+               }
+               new_state = CARDSLEEP;
+       } else {
+               new_state = REGSLEEP;
+       }
+       if (mmc_slot(host).set_sleep)
+               mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
+                                        new_state == CARDSLEEP);
+       /* FIXME: turn off bus power and perhaps interrupts too */
+       clk_disable(host->fclk);
+       host->dpm_state = new_state;
+
+       mmc_release_host(host->mmc);
 
-       omap_mmc_restore_ctx(host);
+       dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n",
+               host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
 
        if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
            mmc_slot(host).card_detect ||
            (mmc_slot(host).get_cover_state &&
-            mmc_slot(host).get_cover_state(host->dev, host->slot_id))) {
-               mmc_power_save_host(host->mmc);
-               new_state = OFF;
-       } else
-               new_state = DISABLED;
-
-       OMAP_HSMMC_WRITE(host->base, ISE, 0);
-       OMAP_HSMMC_WRITE(host->base, IE, 0);
-       OMAP_HSMMC_WRITE(host->base, HCTL,
-                OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+            mmc_slot(host).get_cover_state(host->dev, host->slot_id)))
+               return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
 
-       clk_disable(host->fclk);
-       clk_disable(host->iclk);
-       clk_disable(host->dbclk);
+       return 0;
+}
 
-       host->dpm_state = new_state;
+/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */
+static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host)
+{
+       if (!mmc_try_claim_host(host->mmc))
+               return 0;
+
+       if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+             mmc_slot(host).card_detect ||
+             (mmc_slot(host).get_cover_state &&
+              mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) {
+               mmc_release_host(host->mmc);
+               return 0;
+       }
+
+       mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+       host->vdd = 0;
+       host->power_mode = MMC_POWER_OFF;
+
+       dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n",
+               host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
+
+       host->dpm_state = OFF;
 
        mmc_release_host(host->mmc);
 
@@ -1248,7 +1385,7 @@ static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
 }
 
 /* Handler for [DISABLED -> ENABLED] transition */
-static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
+static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host)
 {
        int err;
 
@@ -1256,8 +1393,7 @@ static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
        if (err < 0)
                return err;
 
-       omap_mmc_restore_ctx(host);
-
+       omap_hsmmc_context_restore(host);
        host->dpm_state = ENABLED;
 
        dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n");
@@ -1265,18 +1401,37 @@ static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
        return 0;
 }
 
-/* Handler for [OFF -> ENABLED] transition */
-static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
+/* Handler for [SLEEP -> ENABLED] transition */
+static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host)
 {
+       if (!mmc_try_claim_host(host->mmc))
+               return 0;
+
        clk_enable(host->fclk);
-       clk_enable(host->iclk);
+       omap_hsmmc_context_restore(host);
+       if (mmc_slot(host).set_sleep)
+               mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
+                        host->vdd, host->dpm_state == CARDSLEEP);
+       if (mmc_card_can_sleep(host->mmc))
+               mmc_card_awake(host->mmc);
+
+       dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n",
+               host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
+
+       host->dpm_state = ENABLED;
 
-       if (clk_enable(host->dbclk))
-               dev_dbg(mmc_dev(host->mmc),
-                       "Enabling debounce clk failed\n");
+       mmc_release_host(host->mmc);
+
+       return 0;
+}
+
+/* Handler for [OFF -> ENABLED] transition */
+static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host)
+{
+       clk_enable(host->fclk);
 
-       omap_mmc_restore_ctx(host);
-       omap_hsmmc_init(host);
+       omap_hsmmc_context_restore(host);
+       omap_hsmmc_conf_bus_power(host);
        mmc_power_restore_host(host->mmc);
 
        host->dpm_state = ENABLED;
@@ -1289,15 +1444,18 @@ static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
 /*
  * Bring MMC host to ENABLED from any other PM state.
  */
-static int omap_mmc_enable(struct mmc_host *mmc)
+static int omap_hsmmc_enable(struct mmc_host *mmc)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
 
        switch (host->dpm_state) {
        case DISABLED:
-               return omap_mmc_disabled_to_enabled(host);
+               return omap_hsmmc_disabled_to_enabled(host);
+       case CARDSLEEP:
+       case REGSLEEP:
+               return omap_hsmmc_sleep_to_enabled(host);
        case OFF:
-               return omap_mmc_off_to_enabled(host);
+               return omap_hsmmc_off_to_enabled(host);
        default:
                dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
                return -EINVAL;
@@ -1307,65 +1465,68 @@ static int omap_mmc_enable(struct mmc_host *mmc)
 /*
  * Bring MMC host in PM state (one level deeper).
  */
-static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
+static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
 
        switch (host->dpm_state) {
        case ENABLED: {
                int delay;
 
-               delay = omap_mmc_enabled_to_disabled(host);
+               delay = omap_hsmmc_enabled_to_disabled(host);
                if (lazy || delay < 0)
                        return delay;
                return 0;
        }
        case DISABLED:
-               return omap_mmc_disabled_to_off(host);
+               return omap_hsmmc_disabled_to_sleep(host);
+       case CARDSLEEP:
+       case REGSLEEP:
+               return omap_hsmmc_sleep_to_off(host);
        default:
                dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
                return -EINVAL;
        }
 }
 
-static int omap_mmc_enable_fclk(struct mmc_host *mmc)
+static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
        int err;
 
        err = clk_enable(host->fclk);
        if (err)
                return err;
        dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
-       omap_mmc_restore_ctx(host);
+       omap_hsmmc_context_restore(host);
        return 0;
 }
 
-static int omap_mmc_disable_fclk(struct mmc_host *mmc, int lazy)
+static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
 {
-       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
 
-       omap_mmc_save_ctx(host);
+       omap_hsmmc_context_save(host);
        clk_disable(host->fclk);
        dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
        return 0;
 }
 
-static const struct mmc_host_ops mmc_omap_ops = {
-       .enable = omap_mmc_enable_fclk,
-       .disable = omap_mmc_disable_fclk,
-       .request = omap_mmc_request,
-       .set_ios = omap_mmc_set_ios,
+static const struct mmc_host_ops omap_hsmmc_ops = {
+       .enable = omap_hsmmc_enable_fclk,
+       .disable = omap_hsmmc_disable_fclk,
+       .request = omap_hsmmc_request,
+       .set_ios = omap_hsmmc_set_ios,
        .get_cd = omap_hsmmc_get_cd,
        .get_ro = omap_hsmmc_get_ro,
        /* NYET -- enable_sdio_irq */
 };
 
-static const struct mmc_host_ops mmc_omap_ps_ops = {
-       .enable = omap_mmc_enable,
-       .disable = omap_mmc_disable,
-       .request = omap_mmc_request,
-       .set_ios = omap_mmc_set_ios,
+static const struct mmc_host_ops omap_hsmmc_ps_ops = {
+       .enable = omap_hsmmc_enable,
+       .disable = omap_hsmmc_disable,
+       .request = omap_hsmmc_request,
+       .set_ios = omap_hsmmc_set_ios,
        .get_cd = omap_hsmmc_get_cd,
        .get_ro = omap_hsmmc_get_ro,
        /* NYET -- enable_sdio_irq */
@@ -1373,15 +1534,14 @@ static const struct mmc_host_ops mmc_omap_ps_ops = {
 
 #ifdef CONFIG_DEBUG_FS
 
-static int mmc_regs_show(struct seq_file *s, void *data)
+static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
 {
        struct mmc_host *mmc = s->private;
-       struct mmc_omap_host *host = mmc_priv(mmc);
-       struct omap_mmc_platform_data *pdata = host->pdata;
+       struct omap_hsmmc_host *host = mmc_priv(mmc);
        int context_loss = 0;
 
-       if (pdata->get_context_loss_count)
-               context_loss = pdata->get_context_loss_count(host->dev);
+       if (host->pdata->get_context_loss_count)
+               context_loss = host->pdata->get_context_loss_count(host->dev);
 
        seq_printf(s, "mmc%d:\n"
                        " enabled:\t%d\n"
@@ -1423,19 +1583,19 @@ static int mmc_regs_show(struct seq_file *s, void *data)
        return 0;
 }
 
-static int mmc_regs_open(struct inode *inode, struct file *file)
+static int omap_hsmmc_regs_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, mmc_regs_show, inode->i_private);
+       return single_open(file, omap_hsmmc_regs_show, inode->i_private);
 }
 
 static const struct file_operations mmc_regs_fops = {
-       .open           = mmc_regs_open,
+       .open           = omap_hsmmc_regs_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
        .release        = single_release,
 };
 
-static void omap_mmc_debugfs(struct mmc_host *mmc)
+static void omap_hsmmc_debugfs(struct mmc_host *mmc)
 {
        if (mmc->debugfs_root)
                debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root,
@@ -1444,17 +1604,17 @@ static void omap_mmc_debugfs(struct mmc_host *mmc)
 
 #else
 
-static void omap_mmc_debugfs(struct mmc_host *mmc)
+static void omap_hsmmc_debugfs(struct mmc_host *mmc)
 {
 }
 
 #endif
 
-static int __init omap_mmc_probe(struct platform_device *pdev)
+static int __init omap_hsmmc_probe(struct platform_device *pdev)
 {
        struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
        struct mmc_host *mmc;
-       struct mmc_omap_host *host = NULL;
+       struct omap_hsmmc_host *host = NULL;
        struct resource *res;
        int ret = 0, irq;
 
@@ -1478,7 +1638,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        if (res == NULL)
                return -EBUSY;
 
-       mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev);
+       mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
        if (!mmc) {
                ret = -ENOMEM;
                goto err;
@@ -1499,17 +1659,18 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        host->power_mode = -1;
 
        platform_set_drvdata(pdev, host);
-       INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
+       INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
 
-       if (pdata->slots[host->slot_id].power_saving)
-               mmc->ops        = &mmc_omap_ps_ops;
+       if (mmc_slot(host).power_saving)
+               mmc->ops        = &omap_hsmmc_ps_ops;
        else
-               mmc->ops        = &mmc_omap_ops;
+               mmc->ops        = &omap_hsmmc_ops;
 
        mmc->f_min      = 400000;
        mmc->f_max      = 52000000;
 
        sema_init(&host->sem, 1);
+       spin_lock_init(&host->irq_lock);
 
        host->iclk = clk_get(&pdev->dev, "ick");
        if (IS_ERR(host->iclk)) {
@@ -1525,7 +1686,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                goto err1;
        }
 
-       omap_mmc_save_ctx(host);
+       omap_hsmmc_context_save(host);
 
        mmc->caps |= MMC_CAP_DISABLE;
        mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT);
@@ -1545,18 +1706,22 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                goto err1;
        }
 
-       host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
-       /*
-        * MMC can still work without debounce clock.
-        */
-       if (IS_ERR(host->dbclk))
-               dev_warn(mmc_dev(host->mmc), "Failed to get debounce clock\n");
-       else
-               if (clk_enable(host->dbclk) != 0)
-                       dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
-                                                       " clk failed\n");
+       if (cpu_is_omap2430()) {
+               host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
+               /*
+                * MMC can still work without debounce clock.
+                */
+               if (IS_ERR(host->dbclk))
+                       dev_warn(mmc_dev(host->mmc),
+                               "Failed to get debounce clock\n");
                else
-                       host->dbclk_enabled = 1;
+                       host->got_dbclk = 1;
+
+               if (host->got_dbclk)
+                       if (clk_enable(host->dbclk) != 0)
+                               dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
+                                                       " clk failed\n");
+       }
 
        /* Since we do only SG emulation, we can have as many segs
         * as we want. */
@@ -1568,17 +1733,18 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
        mmc->max_seg_size = mmc->max_req_size;
 
-       mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+       mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+                    MMC_CAP_WAIT_WHILE_BUSY;
 
-       if (pdata->slots[host->slot_id].wires >= 8)
+       if (mmc_slot(host).wires >= 8)
                mmc->caps |= MMC_CAP_8_BIT_DATA;
-       else if (pdata->slots[host->slot_id].wires >= 4)
+       else if (mmc_slot(host).wires >= 4)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
-       if (pdata->slots[host->slot_id].nonremovable)
+       if (mmc_slot(host).nonremovable)
                mmc->caps |= MMC_CAP_NONREMOVABLE;
 
-       omap_hsmmc_init(host);
+       omap_hsmmc_conf_bus_power(host);
 
        /* Select DMA lines */
        switch (host->id) {
@@ -1594,13 +1760,21 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                host->dma_line_tx = OMAP34XX_DMA_MMC3_TX;
                host->dma_line_rx = OMAP34XX_DMA_MMC3_RX;
                break;
+       case OMAP_MMC4_DEVID:
+               host->dma_line_tx = OMAP44XX_DMA_MMC4_TX;
+               host->dma_line_rx = OMAP44XX_DMA_MMC4_RX;
+               break;
+       case OMAP_MMC5_DEVID:
+               host->dma_line_tx = OMAP44XX_DMA_MMC5_TX;
+               host->dma_line_rx = OMAP44XX_DMA_MMC5_RX;
+               break;
        default:
                dev_err(mmc_dev(host->mmc), "Invalid MMC id\n");
                goto err_irq;
        }
 
        /* Request IRQ for MMC operations */
-       ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
+       ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED,
                        mmc_hostname(mmc), host);
        if (ret) {
                dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
@@ -1610,7 +1784,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        /* initialize power supplies, gpios, etc */
        if (pdata->init != NULL) {
                if (pdata->init(&pdev->dev) != 0) {
-                       dev_dbg(mmc_dev(host->mmc), "late init error\n");
+                       dev_dbg(mmc_dev(host->mmc),
+                               "Unable to configure MMC IRQs\n");
                        goto err_irq_cd_init;
                }
        }
@@ -1619,7 +1794,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        /* Request IRQ for card detect */
        if ((mmc_slot(host).card_detect_irq)) {
                ret = request_irq(mmc_slot(host).card_detect_irq,
-                                 omap_mmc_cd_handler,
+                                 omap_hsmmc_cd_handler,
                                  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
                                          | IRQF_DISABLED,
                                  mmc_hostname(mmc), host);
@@ -1635,22 +1810,23 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
 
        mmc_host_lazy_disable(host->mmc);
 
+       omap_hsmmc_protect_card(host);
+
        mmc_add_host(mmc);
 
-       if (host->pdata->slots[host->slot_id].name != NULL) {
+       if (mmc_slot(host).name != NULL) {
                ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name);
                if (ret < 0)
                        goto err_slot_name;
        }
-       if (mmc_slot(host).card_detect_irq &&
-           host->pdata->slots[host->slot_id].get_cover_state) {
+       if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) {
                ret = device_create_file(&mmc->class_dev,
                                        &dev_attr_cover_switch);
                if (ret < 0)
                        goto err_cover_switch;
        }
 
-       omap_mmc_debugfs(mmc);
+       omap_hsmmc_debugfs(mmc);
 
        return 0;
 
@@ -1667,7 +1843,7 @@ err_irq:
        clk_disable(host->iclk);
        clk_put(host->fclk);
        clk_put(host->iclk);
-       if (host->dbclk_enabled) {
+       if (host->got_dbclk) {
                clk_disable(host->dbclk);
                clk_put(host->dbclk);
        }
@@ -1682,9 +1858,9 @@ err:
        return ret;
 }
 
-static int omap_mmc_remove(struct platform_device *pdev)
+static int omap_hsmmc_remove(struct platform_device *pdev)
 {
-       struct mmc_omap_host *host = platform_get_drvdata(pdev);
+       struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
        struct resource *res;
 
        if (host) {
@@ -1701,7 +1877,7 @@ static int omap_mmc_remove(struct platform_device *pdev)
                clk_disable(host->iclk);
                clk_put(host->fclk);
                clk_put(host->iclk);
-               if (host->dbclk_enabled) {
+               if (host->got_dbclk) {
                        clk_disable(host->dbclk);
                        clk_put(host->dbclk);
                }
@@ -1719,10 +1895,10 @@ static int omap_mmc_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
+static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
 {
        int ret = 0;
-       struct mmc_omap_host *host = platform_get_drvdata(pdev);
+       struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
 
        if (host && host->suspended)
                return 0;
@@ -1749,10 +1925,11 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
 
 
                        OMAP_HSMMC_WRITE(host->base, HCTL,
-                                        OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+                               OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
                        mmc_host_disable(host->mmc);
                        clk_disable(host->iclk);
-                       clk_disable(host->dbclk);
+                       if (host->got_dbclk)
+                               clk_disable(host->dbclk);
                } else {
                        host->suspended = 0;
                        if (host->pdata->resume) {
@@ -1770,10 +1947,10 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
 }
 
 /* Routine to resume the MMC device */
-static int omap_mmc_resume(struct platform_device *pdev)
+static int omap_hsmmc_resume(struct platform_device *pdev)
 {
        int ret = 0;
-       struct mmc_omap_host *host = platform_get_drvdata(pdev);
+       struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
 
        if (host && !host->suspended)
                return 0;
@@ -1783,16 +1960,15 @@ static int omap_mmc_resume(struct platform_device *pdev)
                if (ret)
                        goto clk_en_err;
 
-               if (clk_enable(host->dbclk) != 0)
-                       dev_dbg(mmc_dev(host->mmc),
-                                       "Enabling debounce clk failed\n");
-
                if (mmc_host_enable(host->mmc) != 0) {
                        clk_disable(host->iclk);
                        goto clk_en_err;
                }
 
-               omap_hsmmc_init(host);
+               if (host->got_dbclk)
+                       clk_enable(host->dbclk);
+
+               omap_hsmmc_conf_bus_power(host);
 
                if (host->pdata->resume) {
                        ret = host->pdata->resume(&pdev->dev, host->slot_id);
@@ -1801,10 +1977,13 @@ static int omap_mmc_resume(struct platform_device *pdev)
                                        "Unmask interrupt failed\n");
                }
 
+               omap_hsmmc_protect_card(host);
+
                /* Notify the core to resume the host */
                ret = mmc_resume_host(host->mmc);
                if (ret == 0)
                        host->suspended = 0;
+
                mmc_host_lazy_disable(host->mmc);
        }
 
@@ -1817,34 +1996,34 @@ clk_en_err:
 }
 
 #else
-#define omap_mmc_suspend       NULL
-#define omap_mmc_resume                NULL
+#define omap_hsmmc_suspend     NULL
+#define omap_hsmmc_resume              NULL
 #endif
 
-static struct platform_driver omap_mmc_driver = {
-       .remove         = omap_mmc_remove,
-       .suspend        = omap_mmc_suspend,
-       .resume         = omap_mmc_resume,
+static struct platform_driver omap_hsmmc_driver = {
+       .remove         = omap_hsmmc_remove,
+       .suspend        = omap_hsmmc_suspend,
+       .resume         = omap_hsmmc_resume,
        .driver         = {
                .name = DRIVER_NAME,
                .owner = THIS_MODULE,
        },
 };
 
-static int __init omap_mmc_init(void)
+static int __init omap_hsmmc_init(void)
 {
        /* Register the MMC driver */
-       return platform_driver_probe(&omap_mmc_driver, omap_mmc_probe);
+       return platform_driver_register(&omap_hsmmc_driver);
 }
 
-static void __exit omap_mmc_cleanup(void)
+static void __exit omap_hsmmc_cleanup(void)
 {
        /* Unregister MMC driver */
-       platform_driver_unregister(&omap_mmc_driver);
+       platform_driver_unregister(&omap_hsmmc_driver);
 }
 
-module_init(omap_mmc_init);
-module_exit(omap_mmc_cleanup);
+module_init(omap_hsmmc_init);
+module_exit(omap_hsmmc_cleanup);
 
 MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver");
 MODULE_LICENSE("GPL");