]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mmc/host/omap_hsmmc.c
hsmmc: Fix bus voltage reset, clean-up for checkpatch
[linux-2.6-omap-h63xx.git] / drivers / mmc / host / omap_hsmmc.c
index 3d4a7d1b56284a11d993ee017fbeb7847da0c77e..d5c1e9d7e6bd45d1436b7099d8e1b2a459e164da 100644 (file)
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
 #include <linux/io.h>
-#include <asm/semaphore.h>
+#include <linux/semaphore.h>
 #include <asm/dma.h>
-#include <asm/hardware.h>
-#include <asm/arch/board.h>
-#include <asm/arch/mmc.h>
-#include <asm/arch/cpu.h>
+#include <mach/hardware.h>
+#include <mach/board.h>
+#include <mach/mmc.h>
+#include <mach/cpu.h>
 
 /* OMAP HSMMC Host Controller Registers */
 #define OMAP_HSMMC_SYSCONFIG   0x0010
 #define OMAP_HSMMC_ISE         0x0138
 #define OMAP_HSMMC_CAPA                0x0140
 
-#define VS18                   (1<<26)
-#define VS30                   (1<<25)
-#define SDVS18                 (0x5<<9)
-#define SDVS30                 (0x6<<9)
+#define VS18                   (1 << 26)
+#define VS30                   (1 << 25)
+#define SDVS18                 (0x5 << 9)
+#define SDVS30                 (0x6 << 9)
 #define SDVSCLR                        0xFFFFF1FF
 #define SDVSDET                        0x00000400
 #define AUTOIDLE               0x1
-#define SDBP                   (1<<8)
+#define SDBP                   (1 << 8)
 #define DTO                    0xe
 #define ICE                    0x1
 #define ICS                    0x2
-#define CEN                    (1<<2)
+#define CEN                    (1 << 2)
 #define CLKD_MASK              0x0000FFC0
+#define CLKD_SHIFT             6
+#define DTO_MASK               0x000F0000
+#define DTO_SHIFT              16
 #define INT_EN_MASK            0x307F0033
-#define INIT_STREAM            (1<<1)
-#define DP_SELECT              (1<<21)
-#define DDIR                   (1<<4)
+#define INIT_STREAM            (1 << 1)
+#define DP_SELECT              (1 << 21)
+#define DDIR                   (1 << 4)
 #define DMA_EN                 0x1
-#define MSBS                   1<<5
-#define BCE                    1<<1
-#define FOUR_BIT               1 << 1
+#define MSBS                   (1 << 5)
+#define BCE                    (1 << 1)
+#define FOUR_BIT               (1 << 1)
 #define CC                     0x1
 #define TC                     0x02
 #define OD                     0x1
 #define STAT_CLEAR             0xFFFFFFFF
 #define INIT_STREAM_CMD                0x00000000
 #define DUAL_VOLT_OCR_BIT      7
+#define SRC                    (1 << 25)
+#define SRD                    (1 << 26)
+
+/*
+ * FIXME: Most likely all the data using these _DEVID defines should come
+ * from the platform_data, or implemented in controller and slot specific
+ * functions.
+ */
+#define OMAP_MMC1_DEVID                0
+#define OMAP_MMC2_DEVID                1
 
-#define OMAP_MMC1_DEVID                1
-#define OMAP_MMC2_DEVID                2
 #define OMAP_MMC_DATADIR_NONE  0
 #define OMAP_MMC_DATADIR_READ  1
 #define OMAP_MMC_DATADIR_WRITE 2
 #define MMC_TIMEOUT_MS         20
 #define OMAP_MMC_MASTER_CLOCK  96000000
-#define DRIVER_NAME            "mmci-omap"
+#define DRIVER_NAME            "mmci-omap-hs"
+
 /*
- * slot_id is device id - 1, device id is a static value
- * of 1 to represent device 1 etc..
+ * One controller can have multiple slots, like on some omap boards using
+ * omap.c controller driver. Luckily this is not currently done on any known
+ * omap_hsmmc.c device.
  */
 #define mmc_slot(host)         (host->pdata->slots[host->slot_id])
 
 #define OMAP_HSMMC_WRITE(base, reg, val) \
        __raw_writel((val), (base) + OMAP_HSMMC_##reg)
 
+enum {OFF = 0, ON};
+#define IDLE_TIMEOUT (jiffies_to_msecs(10))
+
 struct mmc_omap_host {
        struct  device          *dev;
        struct  mmc_host        *mmc;
@@ -135,9 +151,52 @@ struct mmc_omap_host {
        int                     initstr;
        int                     slot_id;
        int                     dbclk_enabled;
+
+       struct timer_list       idle_timer;
+       spinlock_t              clk_lock;     /* for changing enabled state */
+       unsigned int            fclk_enabled:1;
+
        struct  omap_mmc_platform_data  *pdata;
 };
 
+static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&host->clk_lock, flags);
+       del_timer(&host->idle_timer);
+       if (host->fclk_enabled != state) {
+               if (state == ON) {
+                       ret = clk_enable(host->fclk);
+                       if (ret != 0)
+                               goto err_out;
+
+                       dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
+               } else {
+                       clk_disable(host->fclk);
+                       dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+               }
+               host->fclk_enabled = state;
+       }
+
+err_out:
+       spin_unlock_irqrestore(&host->clk_lock, flags);
+       return ret;
+}
+
+static void mmc_omap_idle_timer(unsigned long data)
+{
+       struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+       mmc_omap_fclk_state(host, OFF);
+}
+
+static void mmc_omap_fclk_lazy_disable(struct mmc_omap_host *host)
+{
+       mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+}
+
 /*
  * Stop clock to the card
  */
@@ -146,7 +205,7 @@ static void omap_mmc_stop_clock(struct mmc_omap_host *host)
        OMAP_HSMMC_WRITE(host->base, SYSCTL,
                OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
        if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0)
-               dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped");
+               dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
 }
 
 /*
@@ -172,6 +231,43 @@ static void send_init_stream(struct mmc_omap_host *host)
        enable_irq(host->irq);
 }
 
+static inline
+int mmc_omap_cover_is_closed(struct mmc_omap_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);
+       return r;
+}
+
+static ssize_t
+mmc_omap_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);
+
+       return sprintf(buf, "%s\n", mmc_omap_cover_is_closed(host) ? "closed" :
+                      "open");
+}
+
+static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+
+static ssize_t
+mmc_omap_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];
+
+       return sprintf(buf, "slot:%s\n", slot.name);
+}
+
+static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
+
 /*
  * Configure the response type and send the cmd.
  */
@@ -201,9 +297,10 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
 
        /*
         * Unlike OMAP1 controller, the cmdtype does not seem to be based on
-        * ac, bc, adtc, bcr. Only CMD12 needs a val of 0x3, rest 0x0.
+        * ac, bc, adtc, bcr. Only commands ending an open ended transfer need
+        * a val of 0x3, rest 0x0.
         */
-       if (cmd->opcode == 12)
+       if (cmd == host->mrq->stop)
                cmdtype = 0x3;
 
        cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
@@ -244,6 +341,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
 
        if (!data->stop) {
                host->mrq = NULL;
+               mmc_omap_fclk_lazy_disable(host);
                mmc_request_done(host->mmc, data->mrq);
                return;
        }
@@ -272,6 +370,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
        }
        if (host->data == NULL || cmd->error) {
                host->mrq = NULL;
+               mmc_omap_fclk_lazy_disable(host);
                mmc_request_done(host->mmc, cmd->mrq);
        }
 }
@@ -294,12 +393,44 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host)
        host->datadir = OMAP_MMC_DATADIR_NONE;
 }
 
+/*
+ * Readable error output
+ */
+#ifdef CONFIG_MMC_DEBUG
+static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
+{
+       /* --- means reserved bit without definition at documentation */
+       static const char *mmc_omap_status_bits[] = {
+               "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ",
+               "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
+               "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---",
+               "---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---"
+       };
+       char res[256];
+       char *buf = res;
+       int len, i;
+
+       len = sprintf(buf, "MMC IRQ 0x%x :", status);
+       buf += len;
+
+       for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
+               if (status & (1 << i)) {
+                       len = sprintf(buf, " %s", mmc_omap_status_bits[i]);
+                       buf += len;
+               }
+
+       dev_dbg(mmc_dev(host->mmc), "%s\n", res);
+}
+#endif  /* CONFIG_MMC_DEBUG */
+
+
 /*
  * MMC controller IRQ handler
  */
 static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
 {
        struct mmc_omap_host *host = dev_id;
+       struct mmc_data *data;
        int end_cmd = 0, end_trans = 0, status;
 
        if (host->cmd == NULL && host->data == NULL) {
@@ -308,17 +439,29 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                return IRQ_HANDLED;
        }
 
+       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);
+#endif
                if ((status & CMD_TIMEOUT) ||
                        (status & CMD_CRC)) {
                        if (host->cmd) {
-                               if (status & CMD_TIMEOUT)
+                               if (status & CMD_TIMEOUT) {
+                                       OMAP_HSMMC_WRITE(host->base, SYSCTL,
+                                               OMAP_HSMMC_READ(host->base,
+                                                               SYSCTL) | SRC);
+                                       while (OMAP_HSMMC_READ(host->base,
+                                                       SYSCTL) & SRC)
+                                               ;
+
                                        host->cmd->error = -ETIMEDOUT;
-                               else
+                               } else {
                                        host->cmd->error = -EILSEQ;
+                               }
                                end_cmd = 1;
                        }
                        if (host->data)
@@ -331,6 +474,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                                        mmc_dma_cleanup(host);
                                else
                                        host->data->error = -EILSEQ;
+                               OMAP_HSMMC_WRITE(host->base, SYSCTL,
+                                       OMAP_HSMMC_READ(host->base,
+                                                       SYSCTL) | SRD);
+                               while (OMAP_HSMMC_READ(host->base,
+                                               SYSCTL) & SRD)
+                                       ;
                                end_trans = 1;
                        }
                }
@@ -349,7 +498,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        if (end_cmd || (status & CC))
                mmc_omap_cmd_done(host, host->cmd);
        if (end_trans || (status & TC))
-               mmc_omap_xfer_done(host, host->data);
+               mmc_omap_xfer_done(host, data);
 
        return IRQ_HANDLED;
 }
@@ -363,7 +512,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
        int ret;
 
        /* Disable the clocks */
-       clk_disable(host->fclk);
+       mmc_omap_fclk_state(host, OFF);
        clk_disable(host->iclk);
        clk_disable(host->dbclk);
 
@@ -377,7 +526,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
        if (ret != 0)
                goto err;
 
-       clk_enable(host->fclk);
+       mmc_omap_fclk_state(host, ON);
        clk_enable(host->iclk);
        clk_enable(host->dbclk);
 
@@ -387,11 +536,18 @@ 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, mmc_omap_detect fn sets the VDD back to 3V.
+        * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
+        *
+        * Only MMC1 supports 3.0V.  MMC2 will not function if SDVS30 is
+        * set in HCTL.
         */
-       if (((1 << vdd) == MMC_VDD_32_33) || ((1 << vdd) == MMC_VDD_33_34))
-               reg_val |= SDVS30;
-       if ((1 << vdd) == MMC_VDD_165_195)
+       if (host->id == OMAP_MMC1_DEVID) {
+               if (((1 << vdd) == MMC_VDD_32_33) ||
+                   ((1 << vdd) == MMC_VDD_33_34))
+                       reg_val |= SDVS30;
+               else if ((1 << vdd) == MMC_VDD_165_195)
+                       reg_val |= SDVS18;
+       } else
                reg_val |= SDVS18;
 
        OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
@@ -401,7 +557,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
 
        return 0;
 err:
-       dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage \n");
+       dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n");
        return ret;
 }
 
@@ -410,33 +566,35 @@ err:
  */
 static void mmc_omap_detect(struct work_struct *work)
 {
-       u16 vdd = 0;
        struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
                                                mmc_carddetect_work);
 
+       sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
+       mmc_omap_fclk_state(host, ON);
        if (host->carddetect) {
-               if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-                       /*
-                        * Set the VDD back to 3V when the card is removed
-                        * before the set_ios fn turns off the power.
-                        */
-                       vdd = fls(host->mmc->ocr_avail) - 1;
-                       if (omap_mmc_switch_opcond(host, vdd) != 0)
-                               host->mmc->ios.vdd = vdd;
-               }
                mmc_detect_change(host->mmc, (HZ * 200) / 1000);
-       } else
+       } else {
+               OMAP_HSMMC_WRITE(host->base, SYSCTL,
+                       OMAP_HSMMC_READ(host->base, SYSCTL) | SRD);
+               while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD)
+                       ;
+
                mmc_detect_change(host->mmc, (HZ * 50) / 1000);
+       }
+       mmc_omap_fclk_lazy_disable(host);
 }
 
 /*
  * ISR for handling card insertion and removal
  */
-void omap_mmc_notify_card_detect(struct device *dev, int slot, int detected)
+static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
 {
-       struct mmc_omap_host *host = dev_get_drvdata(dev);
-       host->carddetect = detected;
+       struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;
+
+       host->carddetect = mmc_slot(host).card_detect(irq);
        schedule_work(&host->mmc_carddetect_work);
+
+       return IRQ_HANDLED;
 }
 
 /*
@@ -514,10 +672,16 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
 
        if (!(data->flags & MMC_DATA_WRITE)) {
                host->dma_dir = DMA_FROM_DEVICE;
-               sync_dev = OMAP24XX_DMA_MMC1_RX;
+               if (host->id == OMAP_MMC1_DEVID)
+                       sync_dev = OMAP24XX_DMA_MMC1_RX;
+               else
+                       sync_dev = OMAP24XX_DMA_MMC2_RX;
        } else {
                host->dma_dir = DMA_TO_DEVICE;
-               sync_dev = OMAP24XX_DMA_MMC1_TX;
+               if (host->id == OMAP_MMC1_DEVID)
+                       sync_dev = OMAP24XX_DMA_MMC1_TX;
+               else
+                       sync_dev = OMAP24XX_DMA_MMC2_TX;
        }
 
        ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
@@ -552,6 +716,42 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
        return 0;
 }
 
+static void set_data_timeout(struct mmc_omap_host *host,
+                            struct mmc_request *req)
+{
+       unsigned int timeout, cycle_ns;
+       uint32_t reg, clkd, dto = 0;
+
+       reg = OMAP_HSMMC_READ(host->base, SYSCTL);
+       clkd = (reg & CLKD_MASK) >> CLKD_SHIFT;
+       if (clkd == 0)
+               clkd = 1;
+
+       cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd);
+       timeout = req->data->timeout_ns / cycle_ns;
+       timeout += req->data->timeout_clks;
+       if (timeout) {
+               while ((timeout & 0x80000000) == 0) {
+                       dto += 1;
+                       timeout <<= 1;
+               }
+               dto = 31 - dto;
+               timeout <<= 1;
+               if (timeout && dto)
+                       dto += 1;
+               if (dto >= 13)
+                       dto -= 13;
+               else
+                       dto = 0;
+               if (dto > 14)
+                       dto = 14;
+       }
+
+       reg &= ~DTO_MASK;
+       reg |= dto << DTO_SHIFT;
+       OMAP_HSMMC_WRITE(host->base, SYSCTL, reg);
+}
+
 /*
  * Configure block length for MMC/SD cards and initiate the transfer.
  */
@@ -569,6 +769,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 
        OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
                                        | (req->data->blocks << 16));
+       set_data_timeout(host, req);
 
        host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
                        OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
@@ -592,11 +793,11 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
 
        WARN_ON(host->mrq != NULL);
        host->mrq = req;
+       mmc_omap_fclk_state(host, ON);
        mmc_omap_prepare_data(host, req);
        mmc_omap_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)
 {
@@ -605,9 +806,21 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        unsigned long regval;
        unsigned long timeout;
 
+       mmc_omap_fclk_state(host, ON);
+
        switch (ios->power_mode) {
        case MMC_POWER_OFF:
                mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+               /*
+                * Reset bus voltage to 3V if it got set to 1.8V earlier.
+                * REVISIT: If we are able to detect cards after unplugging
+                * a 1.8V card, this code should not be needed.
+                */
+               if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+                       int vdd = fls(host->mmc->ocr_avail) - 1;
+                       if (omap_mmc_switch_opcond(host, vdd) != 0)
+                               host->mmc->ios.vdd = vdd;
+               }
                break;
        case MMC_POWER_UP:
                mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
@@ -675,11 +888,36 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
                OMAP_HSMMC_WRITE(host->base, CON,
                                OMAP_HSMMC_READ(host->base, CON) | OD);
+
+       mmc_omap_fclk_lazy_disable(host);
 }
-/* NOTE: Read only switch not supported yet */
+
+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;
+
+       if (!pdata->slots[0].card_detect)
+               return -ENOSYS;
+       return pdata->slots[0].card_detect(pdata->slots[0].card_detect_irq);
+}
+
+static int omap_hsmmc_get_ro(struct mmc_host *mmc)
+{
+       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_mmc_platform_data *pdata = host->pdata;
+
+       if (!pdata->slots[0].get_ro)
+               return -ENOSYS;
+       return pdata->slots[0].get_ro(host->dev, 0);
+}
+
 static struct mmc_host_ops mmc_omap_ops = {
        .request = omap_mmc_request,
        .set_ios = omap_mmc_set_ios,
+       .get_cd = omap_hsmmc_get_cd,
+       .get_ro = omap_hsmmc_get_ro,
+       /* NYET -- enable_sdio_irq */
 };
 
 static int __init omap_mmc_probe(struct platform_device *pdev)
@@ -689,6 +927,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        struct mmc_omap_host *host = NULL;
        struct resource *res;
        int ret = 0, irq;
+       u32 hctl, capa;
 
        if (pdata == NULL) {
                dev_err(&pdev->dev, "Platform Data is missing\n");
@@ -719,13 +958,19 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        host            = mmc_priv(mmc);
        host->mmc       = mmc;
        host->pdata     = pdata;
+       host->dev       = &pdev->dev;
        host->use_dma   = 1;
+       host->dev->dma_mask = &pdata->dma_mask;
        host->dma_ch    = -1;
        host->irq       = irq;
        host->id        = pdev->id;
        host->slot_id   = 0;
        host->mapbase   = res->start;
        host->base      = ioremap(host->mapbase, SZ_4K);
+
+       platform_set_drvdata(pdev, host);
+       INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
+
        mmc->ops        = &mmc_omap_ops;
        mmc->f_min      = 400000;
        mmc->f_max      = 52000000;
@@ -736,23 +981,30 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        if (IS_ERR(host->iclk)) {
                ret = PTR_ERR(host->iclk);
                host->iclk = NULL;
-               goto err;
+               goto err1;
        }
        host->fclk = clk_get(&pdev->dev, "mmchs_fck");
        if (IS_ERR(host->fclk)) {
                ret = PTR_ERR(host->fclk);
                host->fclk = NULL;
                clk_put(host->iclk);
-               goto err;
+               goto err1;
        }
 
-       if (clk_enable(host->fclk) != 0)
-               goto err;
+       spin_lock_init(&host->clk_lock);
+       setup_timer(&host->idle_timer, mmc_omap_idle_timer,
+                   (unsigned long) host);
 
+       if (mmc_omap_fclk_state(host, ON) != 0) {
+               clk_put(host->iclk);
+               clk_put(host->fclk);
+               goto err1;
+       }
        if (clk_enable(host->iclk) != 0) {
-               clk_disable(host->fclk);
+               mmc_omap_fclk_state(host, OFF);
+               clk_put(host->iclk);
                clk_put(host->fclk);
-               goto err;
+               goto err1;
        }
 
        host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@@ -760,26 +1012,43 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
         * MMC can still work without debounce clock.
         */
        if (IS_ERR(host->dbclk))
-               dev_dbg(mmc_dev(host->mmc), "Failed to get debounce clock \n");
+               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");
+                                                       " clk failed\n");
                else
                        host->dbclk_enabled = 1;
 
+#ifdef CONFIG_MMC_BLOCK_BOUNCE
+       mmc->max_phys_segs = 1;
+       mmc->max_hw_segs = 1;
+#endif
+       mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
+       mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
+       mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+       mmc->max_seg_size = mmc->max_req_size;
+
        mmc->ocr_avail = mmc_slot(host).ocr_mask;
-       mmc->caps |= MMC_CAP_MULTIWRITE | MMC_CAP_MMC_HIGHSPEED |
-                               MMC_CAP_SD_HIGHSPEED;
+       mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
-       if (pdata->conf.wire4)
+       if (pdata->slots[host->slot_id].wires >= 4)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
+       /* Only MMC1 supports 3.0V */
+       if (host->id == OMAP_MMC1_DEVID) {
+               hctl = SDVS30;
+               capa = VS30 | VS18;
+       } else {
+               hctl = SDVS18;
+               capa = VS18;
+       }
+
        OMAP_HSMMC_WRITE(host->base, HCTL,
-                       OMAP_HSMMC_READ(host->base, HCTL) | SDVS30);
+                       OMAP_HSMMC_READ(host->base, HCTL) | hctl);
 
-       OMAP_HSMMC_WRITE(host->base, CAPA, OMAP_HSMMC_READ(host->base,
-                                                       CAPA) | VS30 | VS18);
+       OMAP_HSMMC_WRITE(host->base, CAPA,
+                       OMAP_HSMMC_READ(host->base, CAPA) | capa);
 
        /* Set the controller to AUTO IDLE mode */
        OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
@@ -790,38 +1059,66 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                        OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
 
        /* Request IRQ for MMC operations */
-       ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, pdev->name,
-                        host);
+       ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
+                       mmc_hostname(mmc), host);
        if (ret) {
-               dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ");
-               goto irq_err;
+               dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
+               goto err_irq;
        }
 
-       INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
        if (pdata->init != NULL) {
                if (pdata->init(&pdev->dev) != 0) {
-                       free_irq(host->irq, host);
-                       goto irq_err;
+                       dev_dbg(mmc_dev(host->mmc),
+                               "Unable to configure MMC IRQs\n");
+                       goto err_irq_cd_init;
+               }
+       }
+
+       /* Request IRQ for card detect */
+       if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) {
+               ret = request_irq(mmc_slot(host).card_detect_irq,
+                                 omap_mmc_cd_handler,
+                                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+                                         | IRQF_DISABLED,
+                                 mmc_hostname(mmc), host);
+               if (ret) {
+                       dev_dbg(mmc_dev(host->mmc),
+                               "Unable to grab MMC CD IRQ\n");
+                       goto err_irq_cd;
                }
        }
 
        OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
        OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
 
-       platform_set_drvdata(pdev, host);
        mmc_add_host(mmc);
 
-       return 0;
+       if (host->pdata->slots[host->slot_id].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 && mmc_slot(host).card_detect &&
+                       host->pdata->slots[host->slot_id].get_cover_state) {
+               ret = device_create_file(&mmc->class_dev,
+                                       &dev_attr_cover_switch);
+               if (ret < 0)
+                       goto err_cover_switch;
+       }
+       mmc_omap_fclk_lazy_disable(host);
 
-err:
-       dev_dbg(mmc_dev(host->mmc), "Probe Failed\n");
-       if (host)
-               mmc_free_host(mmc);
-       return ret;
+       return 0;
 
-irq_err:
-       dev_dbg(mmc_dev(host->mmc), "Unable to configure MMC IRQs");
-       clk_disable(host->fclk);
+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_init:
+       free_irq(host->irq, host);
+err_irq:
+       mmc_omap_fclk_state(host, OFF);
        clk_disable(host->iclk);
        clk_put(host->fclk);
        clk_put(host->iclk);
@@ -830,6 +1127,11 @@ irq_err:
                clk_put(host->dbclk);
        }
 
+err1:
+       iounmap(host->base);
+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;
@@ -838,14 +1140,18 @@ irq_err:
 static int omap_mmc_remove(struct platform_device *pdev)
 {
        struct mmc_omap_host *host = platform_get_drvdata(pdev);
+       struct resource *res;
 
-       platform_set_drvdata(pdev, NULL);
        if (host) {
-               host->pdata->cleanup(&pdev->dev);
+               mmc_remove_host(host->mmc);
+               if (host->pdata->cleanup)
+                       host->pdata->cleanup(&pdev->dev);
                free_irq(host->irq, host);
+               if (mmc_slot(host).card_detect_irq)
+                       free_irq(mmc_slot(host).card_detect_irq, host);
                flush_scheduled_work();
 
-               clk_disable(host->fclk);
+               mmc_omap_fclk_state(host, OFF);
                clk_disable(host->iclk);
                clk_put(host->fclk);
                clk_put(host->iclk);
@@ -855,8 +1161,14 @@ static int omap_mmc_remove(struct platform_device *pdev)
                }
 
                mmc_free_host(host->mmc);
+               iounmap(host->base);
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res)
+               release_mem_region(res->start, res->end - res->start + 1);
+       platform_set_drvdata(pdev, NULL);
+
        return 0;
 }
 
@@ -874,28 +1186,22 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
                if (ret == 0) {
                        host->suspended = 1;
 
+                       mmc_omap_fclk_state(host, ON);
                        OMAP_HSMMC_WRITE(host->base, ISE, 0);
                        OMAP_HSMMC_WRITE(host->base, IE, 0);
 
-                       ret = host->pdata->suspend(&pdev->dev, host->slot_id);
-                       if (ret)
-                               dev_dbg(mmc_dev(host->mmc),
-                                       "Unable to handle MMC board"
-                                       "level suspend\n");
-
-                       if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       & SDVSCLR);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDVS30);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDBP);
+                       if (host->pdata->suspend) {
+                               ret = host->pdata->suspend(&pdev->dev,
+                                                               host->slot_id);
+                               if (ret)
+                                       dev_dbg(mmc_dev(host->mmc),
+                                               "Unable to handle MMC board"
+                                               " level suspend\n");
                        }
 
-                       clk_disable(host->fclk);
+                       OMAP_HSMMC_WRITE(host->base, HCTL,
+                                        OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+                       mmc_omap_fclk_state(host, OFF);
                        clk_disable(host->iclk);
                        clk_disable(host->dbclk);
                }
@@ -914,14 +1220,13 @@ static int omap_mmc_resume(struct platform_device *pdev)
                return 0;
 
        if (host) {
-
-               ret = clk_enable(host->fclk);
-               if (ret)
+               int i;
+               if (mmc_omap_fclk_state(host, ON) != 0)
                        goto clk_en_err;
 
                ret = clk_enable(host->iclk);
                if (ret) {
-                       clk_disable(host->fclk);
+                       mmc_omap_fclk_state(host, OFF);
                        clk_put(host->fclk);
                        goto clk_en_err;
                }
@@ -930,15 +1235,26 @@ static int omap_mmc_resume(struct platform_device *pdev)
                        dev_dbg(mmc_dev(host->mmc),
                                        "Enabling debounce clk failed\n");
 
-               ret = host->pdata->resume(&pdev->dev, host->slot_id);
-               if (ret)
-                       dev_dbg(mmc_dev(host->mmc),
+               OMAP_HSMMC_WRITE(host->base, HCTL,
+                                OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+               for (i = 0; i < 100; i++)
+                       if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
+                               break;
+
+               if (host->pdata->resume) {
+                       ret = host->pdata->resume(&pdev->dev, host->slot_id);
+                       if (ret)
+                               dev_dbg(mmc_dev(host->mmc),
                                        "Unmask interrupt failed\n");
+               }
 
                /* Notify the core to resume the host */
                ret = mmc_resume_host(host->mmc);
                if (ret == 0)
                        host->suspended = 0;
+
+               mmc_omap_fclk_lazy_disable(host);
        }
 
        return ret;
@@ -961,6 +1277,7 @@ static struct platform_driver omap_mmc_driver = {
        .resume         = omap_mmc_resume,
        .driver         = {
                .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
        },
 };
 
@@ -981,5 +1298,5 @@ module_exit(omap_mmc_cleanup);
 
 MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS(DRIVER_NAME);
+MODULE_ALIAS("platform:" DRIVER_NAME);
 MODULE_AUTHOR("Texas Instruments Inc");