omap_hsmmc: improve interrupt synchronisation
[safe/jmp/linux-2.6] / drivers / mmc / host / omap_hsmmc.c
index c82ec7f..c9401ef 100644 (file)
 #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>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <plat/dma.h>
 #include <mach/hardware.h>
-#include <mach/board.h>
-#include <mach/mmc.h>
-#include <mach/cpu.h>
+#include <plat/board.h>
+#include <plat/mmc.h>
+#include <plat/cpu.h>
 
 /* OMAP HSMMC Host Controller Registers */
 #define OMAP_HSMMC_SYSCONFIG   0x0010
 #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;
@@ -142,10 +148,19 @@ struct mmc_omap_host {
        struct  clk             *fclk;
        struct  clk             *iclk;
        struct  clk             *dbclk;
-       struct  semaphore       sem;
+       /*
+        * vcc == configured supply
+        * vcc_aux == optional
+        *   -  MMC1, supply for DAT4..DAT7
+        *   -  MMC2/MMC2, external level shifter voltage supply, for
+        *      chip (SDIO, eMMC, etc) or transceiver (MMC2 only)
+        */
+       struct  regulator       *vcc;
+       struct  regulator       *vcc_aux;
        struct  work_struct     mmc_carddetect_work;
        void    __iomem         *base;
        resource_size_t         mapbase;
+       spinlock_t              irq_lock; /* Prevent races with irq handler */
        unsigned int            id;
        unsigned int            dma_len;
        unsigned int            dma_sg_idx;
@@ -158,19 +173,349 @@ 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;
+       int                     use_reg;
+       int                     req_in_progress;
 
        struct  omap_mmc_platform_data  *pdata;
 };
 
+static int omap_hsmmc_card_detect(struct device *dev, int slot)
+{
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+       /* NOTE: assumes card detect signal is active-low */
+       return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
+}
+
+static int omap_hsmmc_get_wp(struct device *dev, int slot)
+{
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+       /* NOTE: assumes write protect signal is active-high */
+       return gpio_get_value_cansleep(mmc->slots[0].gpio_wp);
+}
+
+static int omap_hsmmc_get_cover_state(struct device *dev, int slot)
+{
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+       /* NOTE: assumes card detect signal is active-low */
+       return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
+}
+
+#ifdef CONFIG_PM
+
+static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot)
+{
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+       disable_irq(mmc->slots[0].card_detect_irq);
+       return 0;
+}
+
+static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
+{
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+       enable_irq(mmc->slots[0].card_detect_irq);
+       return 0;
+}
+
+#else
+
+#define omap_hsmmc_suspend_cdirq       NULL
+#define omap_hsmmc_resume_cdirq                NULL
+
+#endif
+
+#ifdef CONFIG_REGULATOR
+
+static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on,
+                                 int vdd)
+{
+       struct omap_hsmmc_host *host =
+               platform_get_drvdata(to_platform_device(dev));
+       int ret;
+
+       if (mmc_slot(host).before_set_reg)
+               mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
+
+       if (power_on)
+               ret = mmc_regulator_set_ocr(host->vcc, vdd);
+       else
+               ret = mmc_regulator_set_ocr(host->vcc, 0);
+
+       if (mmc_slot(host).after_set_reg)
+               mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
+
+       return ret;
+}
+
+static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on,
+                                  int vdd)
+{
+       struct omap_hsmmc_host *host =
+               platform_get_drvdata(to_platform_device(dev));
+       int ret = 0;
+
+       /*
+        * If we don't see a Vcc regulator, assume it's a fixed
+        * voltage always-on regulator.
+        */
+       if (!host->vcc)
+               return 0;
+
+       if (mmc_slot(host).before_set_reg)
+               mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
+
+       /*
+        * Assume Vcc regulator is used only to power the card ... OMAP
+        * VDDS is used to power the pins, optionally with a transceiver to
+        * support cards using voltages other than VDDS (1.8V nominal).  When a
+        * transceiver is used, DAT3..7 are muxed as transceiver control pins.
+        *
+        * In some cases this regulator won't support enable/disable;
+        * e.g. it's a fixed rail for a WLAN chip.
+        *
+        * In other cases vcc_aux switches interface power.  Example, for
+        * eMMC cards it represents VccQ.  Sometimes transceivers or SDIO
+        * chips/cards need an interface voltage rail too.
+        */
+       if (power_on) {
+               ret = mmc_regulator_set_ocr(host->vcc, vdd);
+               /* Enable interface voltage rail, if needed */
+               if (ret == 0 && host->vcc_aux) {
+                       ret = regulator_enable(host->vcc_aux);
+                       if (ret < 0)
+                               ret = mmc_regulator_set_ocr(host->vcc, 0);
+               }
+       } else {
+               if (host->vcc_aux)
+                       ret = regulator_disable(host->vcc_aux);
+               if (ret == 0)
+                       ret = mmc_regulator_set_ocr(host->vcc, 0);
+       }
+
+       if (mmc_slot(host).after_set_reg)
+               mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
+
+       return ret;
+}
+
+static int omap_hsmmc_1_set_sleep(struct device *dev, int slot, int sleep,
+                                 int vdd, int cardsleep)
+{
+       struct omap_hsmmc_host *host =
+               platform_get_drvdata(to_platform_device(dev));
+       int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
+
+       return regulator_set_mode(host->vcc, mode);
+}
+
+static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep,
+                                  int vdd, int cardsleep)
+{
+       struct omap_hsmmc_host *host =
+               platform_get_drvdata(to_platform_device(dev));
+       int err, mode;
+
+       /*
+        * If we don't see a Vcc regulator, assume it's a fixed
+        * voltage always-on regulator.
+        */
+       if (!host->vcc)
+               return 0;
+
+       mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
+
+       if (!host->vcc_aux)
+               return regulator_set_mode(host->vcc, mode);
+
+       if (cardsleep) {
+               /* VCC can be turned off if card is asleep */
+               if (sleep)
+                       err = mmc_regulator_set_ocr(host->vcc, 0);
+               else
+                       err = mmc_regulator_set_ocr(host->vcc, vdd);
+       } else
+               err = regulator_set_mode(host->vcc, mode);
+       if (err)
+               return err;
+
+       if (!mmc_slot(host).vcc_aux_disable_is_sleep)
+               return regulator_set_mode(host->vcc_aux, mode);
+
+       if (sleep)
+               return regulator_disable(host->vcc_aux);
+       else
+               return regulator_enable(host->vcc_aux);
+}
+
+static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
+{
+       struct regulator *reg;
+       int ret = 0;
+
+       switch (host->id) {
+       case OMAP_MMC1_DEVID:
+               /* On-chip level shifting via PBIAS0/PBIAS1 */
+               mmc_slot(host).set_power = omap_hsmmc_1_set_power;
+               mmc_slot(host).set_sleep = omap_hsmmc_1_set_sleep;
+               break;
+       case OMAP_MMC2_DEVID:
+       case OMAP_MMC3_DEVID:
+               /* Off-chip level shifting, or none */
+               mmc_slot(host).set_power = omap_hsmmc_23_set_power;
+               mmc_slot(host).set_sleep = omap_hsmmc_23_set_sleep;
+               break;
+       default:
+               pr_err("MMC%d configuration not supported!\n", host->id);
+               return -EINVAL;
+       }
+
+       reg = regulator_get(host->dev, "vmmc");
+       if (IS_ERR(reg)) {
+               dev_dbg(host->dev, "vmmc regulator missing\n");
+               /*
+               * HACK: until fixed.c regulator is usable,
+               * we don't require a main regulator
+               * for MMC2 or MMC3
+               */
+               if (host->id == OMAP_MMC1_DEVID) {
+                       ret = PTR_ERR(reg);
+                       goto err;
+               }
+       } else {
+               host->vcc = reg;
+               mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg);
+
+               /* Allow an aux regulator */
+               reg = regulator_get(host->dev, "vmmc_aux");
+               host->vcc_aux = IS_ERR(reg) ? NULL : reg;
+
+               /*
+               * UGLY HACK:  workaround regulator framework bugs.
+               * When the bootloader leaves a supply active, it's
+               * initialized with zero usecount ... and we can't
+               * disable it without first enabling it.  Until the
+               * framework is fixed, we need a workaround like this
+               * (which is safe for MMC, but not in general).
+               */
+               if (regulator_is_enabled(host->vcc) > 0) {
+                       regulator_enable(host->vcc);
+                       regulator_disable(host->vcc);
+               }
+               if (host->vcc_aux) {
+                       if (regulator_is_enabled(reg) > 0) {
+                               regulator_enable(reg);
+                               regulator_disable(reg);
+                       }
+               }
+       }
+
+       return 0;
+
+err:
+       mmc_slot(host).set_power = NULL;
+       mmc_slot(host).set_sleep = NULL;
+       return ret;
+}
+
+static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
+{
+       regulator_put(host->vcc);
+       regulator_put(host->vcc_aux);
+       mmc_slot(host).set_power = NULL;
+       mmc_slot(host).set_sleep = NULL;
+}
+
+static inline int omap_hsmmc_have_reg(void)
+{
+       return 1;
+}
+
+#else
+
+static inline int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
+{
+       return -EINVAL;
+}
+
+static inline void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
+{
+}
+
+static inline int omap_hsmmc_have_reg(void)
+{
+       return 0;
+}
+
+#endif
+
+static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
+{
+       int ret;
+
+       if (gpio_is_valid(pdata->slots[0].switch_pin)) {
+               pdata->suspend = omap_hsmmc_suspend_cdirq;
+               pdata->resume = omap_hsmmc_resume_cdirq;
+               if (pdata->slots[0].cover)
+                       pdata->slots[0].get_cover_state =
+                                       omap_hsmmc_get_cover_state;
+               else
+                       pdata->slots[0].card_detect = omap_hsmmc_card_detect;
+               pdata->slots[0].card_detect_irq =
+                               gpio_to_irq(pdata->slots[0].switch_pin);
+               ret = gpio_request(pdata->slots[0].switch_pin, "mmc_cd");
+               if (ret)
+                       return ret;
+               ret = gpio_direction_input(pdata->slots[0].switch_pin);
+               if (ret)
+                       goto err_free_sp;
+       } else
+               pdata->slots[0].switch_pin = -EINVAL;
+
+       if (gpio_is_valid(pdata->slots[0].gpio_wp)) {
+               pdata->slots[0].get_ro = omap_hsmmc_get_wp;
+               ret = gpio_request(pdata->slots[0].gpio_wp, "mmc_wp");
+               if (ret)
+                       goto err_free_cd;
+               ret = gpio_direction_input(pdata->slots[0].gpio_wp);
+               if (ret)
+                       goto err_free_wp;
+       } else
+               pdata->slots[0].gpio_wp = -EINVAL;
+
+       return 0;
+
+err_free_wp:
+       gpio_free(pdata->slots[0].gpio_wp);
+err_free_cd:
+       if (gpio_is_valid(pdata->slots[0].switch_pin))
+err_free_sp:
+               gpio_free(pdata->slots[0].switch_pin);
+       return ret;
+}
+
+static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
+{
+       if (gpio_is_valid(pdata->slots[0].gpio_wp))
+               gpio_free(pdata->slots[0].gpio_wp);
+       if (gpio_is_valid(pdata->slots[0].switch_pin))
+               gpio_free(pdata->slots[0].switch_pin);
+}
+
 /*
  * 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);
@@ -178,13 +523,34 @@ static void omap_mmc_stop_clock(struct mmc_omap_host *host)
                dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
 }
 
+static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
+{
+       unsigned int irq_mask;
+
+       if (host->use_dma)
+               irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE);
+       else
+               irq_mask = INT_EN_MASK;
+
+       OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+       OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+       OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
+}
+
+static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
+{
+       OMAP_HSMMC_WRITE(host->base, ISE, 0);
+       OMAP_HSMMC_WRITE(host->base, IE, 0);
+       OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+}
+
 #ifdef CONFIG_PM
 
 /*
  * 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;
@@ -246,9 +612,7 @@ static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
                && time_before(jiffies, timeout))
                ;
 
-       OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
-       OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
-       OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+       omap_hsmmc_disable_irq(host);
 
        /* Do not initialize card-specific things if the power is off */
        if (host->power_mode == MMC_POWER_OFF)
@@ -312,7 +676,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;
@@ -327,12 +691,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)
 {
 }
 
@@ -342,12 +706,17 @@ 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, IE, INT_EN_MASK);
        OMAP_HSMMC_WRITE(host->base, CON,
                OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM);
        OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD);
@@ -358,51 +727,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;
@@ -411,17 +782,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
                mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
        host->cmd = cmd;
 
-       /*
-        * Clear status bits and enable interrupts
-        */
-       OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
-       OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
-
-       if (host->use_dma)
-               OMAP_HSMMC_WRITE(host->base, IE,
-                                INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE));
-       else
-               OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+       omap_hsmmc_enable_irq(host);
 
        host->response_busy = 0;
        if (cmd->flags & MMC_RSP_PRESENT) {
@@ -455,12 +816,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
        if (host->use_dma)
                cmdreg |= DMA_EN;
 
+       host->req_in_progress = 1;
+
        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;
@@ -468,44 +831,62 @@ mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
                return DMA_FROM_DEVICE;
 }
 
+static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
+{
+       int dma_ch;
+
+       spin_lock(&host->irq_lock);
+       host->req_in_progress = 0;
+       dma_ch = host->dma_ch;
+       spin_unlock(&host->irq_lock);
+
+       omap_hsmmc_disable_irq(host);
+       /* Do not complete the request if DMA is still in progress */
+       if (mrq->data && host->use_dma && dma_ch != -1)
+               return;
+       host->mrq = NULL;
+       mmc_request_done(host->mmc, mrq);
+}
+
 /*
  * 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;
 
-               host->mrq = NULL;
-               mmc_request_done(host->mmc, 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;
+               }
+
+               omap_hsmmc_request_done(host, mrq);
                return;
        }
 
        host->data = NULL;
 
-       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));
-
        if (!data->error)
                data->bytes_xfered += data->blocks * (data->blksz);
        else
                data->bytes_xfered = 0;
 
        if (!data->stop) {
-               host->mrq = NULL;
-               mmc_request_done(host->mmc, data->mrq);
+               omap_hsmmc_request_done(host, 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;
 
@@ -521,25 +902,28 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
                        cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
                }
        }
-       if ((host->data == NULL && !host->response_busy) || cmd->error) {
-               host->mrq = NULL;
-               mmc_request_done(host->mmc, cmd->mrq);
-       }
+       if ((host->data == NULL && !host->response_busy) || cmd->error)
+               omap_hsmmc_request_done(host, cmd->mrq);
 }
 
 /*
  * 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)
 {
+       int dma_ch;
+
        host->data->error = errno;
 
-       if (host->use_dma && host->dma_ch != -1) {
+       spin_lock(&host->irq_lock);
+       dma_ch = host->dma_ch;
+       host->dma_ch = -1;
+       spin_unlock(&host->irq_lock);
+
+       if (host->use_dma && 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_free_dma(host->dma_ch);
-               host->dma_ch = -1;
-               up(&host->sem);
+                       omap_hsmmc_get_dma_dir(host, host->data));
+               omap_free_dma(dma_ch);
        }
        host->data = NULL;
 }
@@ -548,10 +932,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", "---",
@@ -564,9 +948,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;
                }
 
@@ -581,8 +965,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 *
@@ -601,36 +985,33 @@ static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host,
                        __func__);
 }
 
-/*
- * MMC controller IRQ handler
- */
-static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
+static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
 {
-       struct mmc_omap_host *host = dev_id;
        struct mmc_data *data;
-       int end_cmd = 0, end_trans = 0, status;
-
-       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);
-               return IRQ_HANDLED;
+       int end_cmd = 0, end_trans = 0;
+
+       if (!host->req_in_progress) {
+               do {
+                       OMAP_HSMMC_WRITE(host->base, STAT, status);
+                       /* Flush posted write */
+                       status = OMAP_HSMMC_READ(host->base, STAT);
+               } while (status & INT_EN_MASK);
+               return;
        }
 
        data = host->data;
-       status = OMAP_HSMMC_READ(host->base, STAT);
        dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
 
        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;
@@ -639,9 +1020,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) ||
@@ -651,11 +1033,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;
                        }
                }
@@ -670,18 +1052,32 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        }
 
        OMAP_HSMMC_WRITE(host->base, STAT, status);
-       /* Flush posted write */
-       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);
+}
+
+/*
+ * MMC controller IRQ handler
+ */
+static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
+{
+       struct omap_hsmmc_host *host = dev_id;
+       int status;
+
+       status = OMAP_HSMMC_READ(host->base, STAT);
+       do {
+               omap_hsmmc_do_irq(host, status);
+               /* Flush posted write */
+               status = OMAP_HSMMC_READ(host->base, STAT);
+       } while (status & INT_EN_MASK);
 
        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;
 
@@ -701,7 +1097,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;
@@ -709,22 +1105,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);
@@ -732,7 +1130,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,
@@ -758,13 +1156,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;
 
@@ -773,27 +1195,25 @@ 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)
-               carddetect = slot->card_detect(slot->card_detect_irq);
-       else
+       if (slot->card_detect)
+               carddetect = slot->card_detect(host->dev, host->slot_id);
+       else {
+               omap_hsmmc_protect_card(host);
                carddetect = -ENOSYS;
+       }
 
-       if (carddetect) {
+       if (carddetect)
                mmc_detect_change(host->mmc, (HZ * 200) / 1000);
-       } else {
-               mmc_host_enable(host->mmc);
-               mmc_omap_reset_controller_fsm(host, SRD);
-               mmc_host_lazy_disable(host->mmc);
+       else
                mmc_detect_change(host->mmc, (HZ * 50) / 1000);
-       }
 }
 
 /*
  * 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;
@@ -802,7 +1222,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;
@@ -814,7 +1234,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)
 {
@@ -828,7 +1248,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);
        }
@@ -838,7 +1258,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);
@@ -847,40 +1267,56 @@ 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 *cb_data)
 {
-       struct mmc_omap_host *host = data;
+       struct omap_hsmmc_host *host = cb_data;
+       struct mmc_data *data = host->mrq->data;
+       int dma_ch, req_in_progress;
 
        if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
                dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
 
-       if (host->dma_ch < 0)
+       spin_lock(&host->irq_lock);
+       if (host->dma_ch < 0) {
+               spin_unlock(&host->irq_lock);
                return;
+       }
 
        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,
-                                          host->data->sg + host->dma_sg_idx);
+               omap_hsmmc_config_dma_params(host, data,
+                                          data->sg + host->dma_sg_idx);
+               spin_unlock(&host->irq_lock);
                return;
        }
 
-       omap_free_dma(host->dma_ch);
+       dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+               omap_hsmmc_get_dma_dir(host, data));
+
+       req_in_progress = host->req_in_progress;
+       dma_ch = host->dma_ch;
        host->dma_ch = -1;
-       /*
-        * DMA Callback: run in interrupt context.
-        * mutex_unlock will throw a kernel warning if used.
-        */
-       up(&host->sem);
+       spin_unlock(&host->irq_lock);
+
+       omap_free_dma(dma_ch);
+
+       /* If DMA has finished after TC, complete the request */
+       if (!req_in_progress) {
+               struct mmc_request *mrq = host->mrq;
+
+               host->mrq = NULL;
+               mmc_request_done(host->mmc, mrq);
+       }
 }
 
 /*
  * 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;
+       int dma_ch = 0, ret = 0, i;
        struct mmc_data *data = req->data;
 
        /* Sanity check: all the SG entries must be aligned by block size. */
@@ -897,26 +1333,10 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
                 */
                return -EINVAL;
 
-       /*
-        * If for some reason the DMA transfer is still active,
-        * we wait for timeout period and free the dma
-        */
-       if (host->dma_ch != -1) {
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(100);
-               if (down_trylock(&host->sem)) {
-                       omap_free_dma(host->dma_ch);
-                       host->dma_ch = -1;
-                       up(&host->sem);
-                       return err;
-               }
-       } else {
-               if (down_trylock(&host->sem))
-                       return err;
-       }
+       BUG_ON(host->dma_ch != -1);
 
-       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",
@@ -925,17 +1345,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;
@@ -946,8 +1367,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;
@@ -974,22 +1395,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;
@@ -1001,14 +1428,35 @@ 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;
 
+       BUG_ON(host->req_in_progress);
+       BUG_ON(host->dma_ch != -1);
+       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;
+               req->cmd->retries = 0;
+               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)
@@ -1018,14 +1466,13 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *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;
@@ -1084,8 +1531,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");
                }
        }
@@ -1101,7 +1548,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);
@@ -1135,25 +1582,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(host->dev, host->slot_id);
 }
 
 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;
 
@@ -1182,25 +1627,26 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
 
 /*
  * Dynamic power saving handling, FSM:
- *   ENABLED -> DISABLED -> OFF / REGSLEEP
- *     ^___________|          |
- *     |______________________|
+ *   ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF
+ *     ^___________|          |                      |
+ *     |______________________|______________________|
  *
  * ENABLED:   mmc host is fully functional
  * DISABLED:  fclk is off
- * OFF:       fclk is off,voltage regulator is off
- * REGSLEEP:  fclk is off,voltage regulator is asleep
+ * 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, REGSLEEP, 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;
 
@@ -1209,46 +1655,79 @@ 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 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 (mmc_slot(host).no_off)
+               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_power_save_host(host->mmc);
-               new_state = OFF;
-       } else {
-               if (mmc_slot(host).set_sleep)
-                       mmc_slot(host).set_sleep(host->dev, host->slot_id,
-                                                1, 0, 0);
-               new_state = REGSLEEP;
+            mmc_slot(host).get_cover_state(host->dev, host->slot_id)))
+               return OMAP_MMC_OFF_TIMEOUT;
+
+       return 0;
+}
+
+/* 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 (mmc_slot(host).no_off)
+               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;
        }
 
-       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).set_power(host->dev, host->slot_id, 0, 0);
+       host->vdd = 0;
+       host->power_mode = MMC_POWER_OFF;
 
-       clk_disable(host->fclk);
-       clk_disable(host->iclk);
-       clk_disable(host->dbclk);
+       dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n",
+               host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
 
-       host->dpm_state = new_state;
+       host->dpm_state = OFF;
 
        mmc_release_host(host->mmc);
 
@@ -1256,7 +1735,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;
 
@@ -1264,8 +1743,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");
@@ -1273,79 +1751,61 @@ 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)
 {
-       clk_enable(host->fclk);
-       clk_enable(host->iclk);
+       if (!mmc_try_claim_host(host->mmc))
+               return 0;
 
-       if (clk_enable(host->dbclk))
-               dev_dbg(mmc_dev(host->mmc),
-                       "Enabling debounce clk failed\n");
+       clk_enable(host->fclk);
+       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);
 
-       omap_mmc_restore_ctx(host);
-       omap_hsmmc_init(host);
-       mmc_power_restore_host(host->mmc);
+       dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n",
+               host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
 
        host->dpm_state = ENABLED;
 
-       dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
+       mmc_release_host(host->mmc);
 
        return 0;
 }
 
-/* Handler for [REGSLEEP -> ENABLED] transition */
-static int omap_mmc_regsleep_to_enabled(struct mmc_omap_host *host)
+/* Handler for [OFF -> ENABLED] transition */
+static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host)
 {
-       unsigned long timeout;
-
-       dev_dbg(mmc_dev(host->mmc), "REGSLEEP -> ENABLED\n");
-
        clk_enable(host->fclk);
-       clk_enable(host->iclk);
-
-       if (clk_enable(host->dbclk))
-               dev_dbg(mmc_dev(host->mmc),
-                       "Enabling debounce clk failed\n");
-
-       omap_mmc_restore_ctx(host);
-
-       /*
-        * We turned off interrupts and bus power.  Interrupts
-        * are turned on by 'mmc_omap_start_command()' so we
-        * just need to turn on the bus power here.
-        */
-       OMAP_HSMMC_WRITE(host->base, HCTL,
-                        OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
 
-       timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
-       while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP &&
-              time_before(jiffies, timeout))
-               ;
-
-       if (mmc_slot(host).set_sleep)
-               mmc_slot(host).set_sleep(host->dev, host->slot_id,
-                                        0, host->vdd, 0);
+       omap_hsmmc_context_restore(host);
+       omap_hsmmc_conf_bus_power(host);
+       mmc_power_restore_host(host->mmc);
 
        host->dpm_state = ENABLED;
 
+       dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
+
        return 0;
 }
 
 /*
  * 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_mmc_regsleep_to_enabled(host);
+               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;
@@ -1355,65 +1815,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 */
@@ -1421,15 +1884,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"
@@ -1441,8 +1903,7 @@ static int mmc_regs_show(struct seq_file *s, void *data)
                        host->dpm_state, mmc->nesting_cnt,
                        host->context_loss, context_loss);
 
-       if (host->suspended || host->dpm_state == OFF ||
-           host->dpm_state == REGSLEEP) {
+       if (host->suspended || host->dpm_state == OFF) {
                seq_printf(s, "host suspended, can't read registers\n");
                return 0;
        }
@@ -1472,19 +1933,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,
@@ -1493,19 +1954,19 @@ 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;
+       int ret, irq;
 
        if (pdata == NULL) {
                dev_err(&pdev->dev, "Platform Data is missing\n");
@@ -1527,10 +1988,14 @@ 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);
+       ret = omap_hsmmc_gpio_init(pdata);
+       if (ret)
+               goto err;
+
+       mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
        if (!mmc) {
                ret = -ENOMEM;
-               goto err;
+               goto err_alloc;
        }
 
        host            = mmc_priv(mmc);
@@ -1545,20 +2010,27 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        host->slot_id   = 0;
        host->mapbase   = res->start;
        host->base      = ioremap(host->mapbase, SZ_4K);
-       host->power_mode = -1;
+       host->power_mode = MMC_POWER_OFF;
 
        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;
+
+       /*
+        * If regulator_disable can only put vcc_aux to sleep then there is
+        * no off state.
+        */
+       if (mmc_slot(host).vcc_aux_disable_is_sleep)
+               mmc_slot(host).no_off = 1;
 
        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)) {
@@ -1574,7 +2046,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);
@@ -1594,18 +2066,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. */
@@ -1617,17 +2093,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) {
@@ -1643,32 +2120,48 @@ 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");
                goto err_irq;
        }
 
-       /* 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;
                }
        }
+
+       if (omap_hsmmc_have_reg() && !mmc_slot(host).set_power) {
+               ret = omap_hsmmc_reg_get(host);
+               if (ret)
+                       goto err_reg;
+               host->use_reg = 1;
+       }
+
        mmc->ocr_avail = mmc_slot(host).ocr_mask;
 
        /* 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);
@@ -1679,36 +2172,39 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                }
        }
 
-       OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
-       OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+       omap_hsmmc_disable_irq(host);
 
        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;
+                       goto err_slot_name;
        }
 
-       omap_mmc_debugfs(mmc);
+       omap_hsmmc_debugfs(mmc);
 
        return 0;
 
-err_cover_switch:
-       device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
 err_slot_name:
        mmc_remove_host(mmc);
-err_irq_cd:
        free_irq(mmc_slot(host).card_detect_irq, host);
+err_irq_cd:
+       if (host->use_reg)
+               omap_hsmmc_reg_put(host);
+err_reg:
+       if (host->pdata->cleanup)
+               host->pdata->cleanup(&pdev->dev);
 err_irq_cd_init:
        free_irq(host->irq, host);
 err_irq:
@@ -1716,29 +2212,31 @@ 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);
        }
-
 err1:
        iounmap(host->base);
+       platform_set_drvdata(pdev, NULL);
+       mmc_free_host(mmc);
+err_alloc:
+       omap_hsmmc_gpio_free(pdata);
 err:
-       dev_dbg(mmc_dev(host->mmc), "Probe Failed\n");
        release_mem_region(res->start, res->end - res->start + 1);
-       if (host)
-               mmc_free_host(mmc);
        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) {
                mmc_host_enable(host->mmc);
                mmc_remove_host(host->mmc);
+               if (host->use_reg)
+                       omap_hsmmc_reg_put(host);
                if (host->pdata->cleanup)
                        host->pdata->cleanup(&pdev->dev);
                free_irq(host->irq, host);
@@ -1750,13 +2248,14 @@ 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);
                }
 
                mmc_free_host(host->mmc);
                iounmap(host->base);
+               omap_hsmmc_gpio_free(pdev->dev.platform_data);
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1768,10 +2267,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;
@@ -1793,15 +2292,13 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
                mmc_host_enable(host->mmc);
                ret = mmc_suspend_host(host->mmc, state);
                if (ret == 0) {
-                       OMAP_HSMMC_WRITE(host->base, ISE, 0);
-                       OMAP_HSMMC_WRITE(host->base, IE, 0);
-
-
+                       omap_hsmmc_disable_irq(host);
                        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) {
@@ -1819,10 +2316,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;
@@ -1832,16 +2329,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);
@@ -1850,10 +2346,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);
        }
 
@@ -1866,34 +2365,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_probe(&omap_hsmmc_driver, omap_hsmmc_probe);
 }
 
-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");