mmc: OMAP HS-MMC: convert to dev_pm_ops
[safe/jmp/linux-2.6] / drivers / mmc / host / omap_hsmmc.c
index 14c58ca..d25b19b 100644 (file)
 #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
@@ -144,12 +148,19 @@ struct omap_hsmmc_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 long           flags;
        unsigned int            id;
        unsigned int            dma_len;
        unsigned int            dma_sg_idx;
@@ -169,10 +180,338 @@ struct omap_hsmmc_host {
        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
  */
@@ -184,6 +523,27 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_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
 
 /*
@@ -252,9 +612,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_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)
@@ -357,6 +715,8 @@ static void send_init_stream(struct omap_hsmmc_host *host)
                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);
@@ -422,17 +782,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_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) {
@@ -466,13 +816,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_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);
+       host->req_in_progress = 1;
 
        OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
        OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
@@ -487,6 +831,23 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_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
  */
@@ -503,25 +864,19 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
                        return;
                }
 
-               host->mrq = NULL;
-               mmc_request_done(host->mmc, mrq);
+               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,
-                       omap_hsmmc_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;
        }
        omap_hsmmc_start_command(host, data->stop, NULL);
@@ -547,10 +902,8 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_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);
 }
 
 /*
@@ -558,14 +911,19 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
  */
 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,
                        omap_hsmmc_get_dma_dir(host, host->data));
-               omap_free_dma(host->dma_ch);
-               host->dma_ch = -1;
-               up(&host->sem);
+               omap_free_dma(dma_ch);
        }
        host->data = NULL;
 }
@@ -627,28 +985,21 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
                        __func__);
 }
 
-/*
- * MMC controller IRQ handler
- */
-static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
+static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
 {
-       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;
+       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) {
@@ -701,15 +1052,27 @@ static irqreturn_t omap_hsmmc_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))
                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);
+/*
+ * 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;
 }
@@ -833,21 +1196,16 @@ static void omap_hsmmc_detect(struct work_struct *work)
        sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
 
        if (slot->card_detect)
-               carddetect = slot->card_detect(slot->card_detect_irq);
+               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);
-               omap_hsmmc_reset_controller_fsm(host, SRD);
-               mmc_host_lazy_disable(host->mmc);
-
+       else
                mmc_detect_change(host->mmc, (HZ * 50) / 1000);
-       }
 }
 
 /*
@@ -909,31 +1267,47 @@ static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
 /*
  * DMA call back function
  */
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data)
+static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
 {
-       struct omap_hsmmc_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. */
-               omap_hsmmc_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);
+       }
 }
 
 /*
@@ -942,7 +1316,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data)
 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. */
@@ -959,23 +1333,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
                 */
                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(omap_hsmmc_get_dma_sync_dev(host, data),
                               "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
@@ -997,7 +1355,8 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 }
 
 static void set_data_timeout(struct omap_hsmmc_host *host,
-                            struct mmc_request *req)
+                            unsigned int timeout_ns,
+                            unsigned int timeout_clks)
 {
        unsigned int timeout, cycle_ns;
        uint32_t reg, clkd, dto = 0;
@@ -1008,8 +1367,8 @@ static void set_data_timeout(struct omap_hsmmc_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;
@@ -1043,12 +1402,18 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
 
        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 = omap_hsmmc_start_dma_transfer(host, req);
@@ -1068,37 +1433,27 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
        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;
-       }
+       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 = omap_hsmmc_prepare_data(host, req);
@@ -1107,8 +1462,6 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
                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;
        }
@@ -1233,7 +1586,7 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc)
 
        if (!mmc_slot(host).card_detect)
                return -ENOSYS;
-       return mmc_slot(host).card_detect(mmc_slot(host).card_detect_irq);
+       return mmc_slot(host).card_detect(host->dev, host->slot_id);
 }
 
 static int omap_hsmmc_get_ro(struct mmc_host *mmc)
@@ -1302,7 +1655,7 @@ static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host)
        if (host->power_mode == MMC_POWER_OFF)
                return 0;
 
-       return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT);
+       return OMAP_MMC_SLEEP_TIMEOUT;
 }
 
 /* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
@@ -1338,11 +1691,14 @@ static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *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)))
-               return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+               return OMAP_MMC_OFF_TIMEOUT;
 
        return 0;
 }
@@ -1353,6 +1709,9 @@ 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 &&
@@ -1607,7 +1966,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
        struct mmc_host *mmc;
        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");
@@ -1629,10 +1988,14 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
        if (res == NULL)
                return -EBUSY;
 
+       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);
@@ -1647,7 +2010,7 @@ static int __init omap_hsmmc_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, omap_hsmmc_detect);
@@ -1657,10 +2020,16 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
        else
                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");
@@ -1751,6 +2120,14 @@ static int __init omap_hsmmc_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;
@@ -1764,7 +2141,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
-       /* initialize power supplies, gpios, etc */
        if (pdata->init != NULL) {
                if (pdata->init(&pdev->dev) != 0) {
                        dev_dbg(mmc_dev(host->mmc),
@@ -1772,6 +2148,14 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
                        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 */
@@ -1788,8 +2172,7 @@ static int __init omap_hsmmc_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);
 
@@ -1806,19 +2189,22 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
                ret = device_create_file(&mmc->class_dev,
                                        &dev_attr_cover_switch);
                if (ret < 0)
-                       goto err_cover_switch;
+                       goto err_slot_name;
        }
 
        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:
@@ -1830,14 +2216,14 @@ err_irq:
                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;
 }
 
@@ -1849,6 +2235,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
        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);
@@ -1867,6 +2255,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 
                mmc_free_host(host->mmc);
                iounmap(host->base);
+               omap_hsmmc_gpio_free(pdev->dev.platform_data);
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1878,10 +2267,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
+static int omap_hsmmc_suspend(struct device *dev)
 {
        int ret = 0;
+       struct platform_device *pdev = to_platform_device(dev);
        struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
+       pm_message_t state = PMSG_SUSPEND; /* unused by MMC core */
 
        if (host && host->suspended)
                return 0;
@@ -1903,10 +2294,7 @@ static int omap_hsmmc_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);
                        mmc_host_disable(host->mmc);
@@ -1930,9 +2318,10 @@ static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
 }
 
 /* Routine to resume the MMC device */
-static int omap_hsmmc_resume(struct platform_device *pdev)
+static int omap_hsmmc_resume(struct device *dev)
 {
        int ret = 0;
+       struct platform_device *pdev = to_platform_device(dev);
        struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
 
        if (host && !host->suspended)
@@ -1983,20 +2372,24 @@ clk_en_err:
 #define omap_hsmmc_resume              NULL
 #endif
 
-static struct platform_driver omap_hsmmc_driver = {
-       .remove         = omap_hsmmc_remove,
+static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
        .suspend        = omap_hsmmc_suspend,
        .resume         = omap_hsmmc_resume,
+};
+
+static struct platform_driver omap_hsmmc_driver = {
+       .remove         = omap_hsmmc_remove,
        .driver         = {
                .name = DRIVER_NAME,
                .owner = THIS_MODULE,
+               .pm = &omap_hsmmc_dev_pm_ops,
        },
 };
 
 static int __init omap_hsmmc_init(void)
 {
        /* Register the MMC driver */
-       return platform_driver_register(&omap_hsmmc_driver);
+       return platform_driver_probe(&omap_hsmmc_driver, omap_hsmmc_probe);
 }
 
 static void __exit omap_hsmmc_cleanup(void)