omap: fix a load of "warning: symbol 'xxx' was not declared. Should it be static?"
[pandora-kernel.git] / drivers / mmc / host / omap.c
index 59eac72..1b9fc3c 100644 (file)
 
 #include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/mach-types.h>
 
-#include <asm/arch/board.h>
-#include <asm/arch/mmc.h>
-#include <asm/arch/gpio.h>
-#include <asm/arch/dma.h>
-#include <asm/arch/mux.h>
-#include <asm/arch/fpga.h>
+#include <mach/board.h>
+#include <mach/mmc.h>
+#include <mach/gpio.h>
+#include <mach/dma.h>
+#include <mach/mux.h>
+#include <mach/fpga.h>
 
 #define        OMAP_MMC_REG_CMD        0x00
 #define        OMAP_MMC_REG_ARGL       0x04
@@ -94,7 +93,7 @@
 
 /* Specifies how often in millisecs to poll for card status changes
  * when the cover switch is open */
-#define OMAP_MMC_SWITCH_POLL_DELAY     500
+#define OMAP_MMC_COVER_POLL_DELAY      500
 
 struct mmc_omap_host;
 
@@ -106,6 +105,10 @@ struct mmc_omap_slot {
        unsigned int            fclk_freq;
        unsigned                powered:1;
 
+       struct tasklet_struct   cover_tasklet;
+       struct timer_list       cover_timer;
+       unsigned                cover_open;
+
        struct mmc_request      *mrq;
        struct mmc_omap_host    *host;
        struct mmc_host         *mmc;
@@ -130,6 +133,15 @@ struct mmc_omap_host {
        unsigned char           bus_mode;
        unsigned char           hw_bus_mode;
 
+       struct work_struct      cmd_abort_work;
+       unsigned                abort:1;
+       struct timer_list       cmd_abort_timer;
+
+       struct work_struct      slot_release_work;
+       struct mmc_omap_slot    *next_slot;
+       struct work_struct      send_stop_work;
+       struct mmc_data         *stop_data;
+
        unsigned int            sg_len;
        int                     sg_idx;
        u16 *                   buffer;
@@ -153,9 +165,38 @@ struct mmc_omap_host {
        wait_queue_head_t       slot_wq;
        int                     nr_slots;
 
+       struct timer_list       clk_timer;
+       spinlock_t              clk_lock;     /* for changing enabled state */
+       unsigned int            fclk_enabled:1;
+
        struct omap_mmc_platform_data *pdata;
 };
 
+static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
+{
+       unsigned long tick_ns;
+
+       if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) {
+               tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq;
+               ndelay(8 * tick_ns);
+       }
+}
+
+static void mmc_omap_fclk_enable(struct mmc_omap_host *host, unsigned int enable)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->clk_lock, flags);
+       if (host->fclk_enabled != enable) {
+               host->fclk_enabled = enable;
+               if (enable)
+                       clk_enable(host->fclk);
+               else
+                       clk_disable(host->fclk);
+       }
+       spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
 static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
 {
        struct mmc_omap_host *host = slot->host;
@@ -172,52 +213,82 @@ static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
        host->mmc = slot->mmc;
        spin_unlock_irqrestore(&host->slot_lock, flags);
 no_claim:
-       clk_enable(host->fclk);
+       del_timer(&host->clk_timer);
+       if (host->current_slot != slot || !claimed)
+               mmc_omap_fclk_offdelay(host->current_slot);
+
        if (host->current_slot != slot) {
+               OMAP_MMC_WRITE(host, CON, slot->saved_con & 0xFC00);
                if (host->pdata->switch_slot != NULL)
                        host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id);
                host->current_slot = slot;
        }
 
-       /* Doing the dummy read here seems to work around some bug
-        * at least in OMAP24xx silicon where the command would not
-        * start after writing the CMD register. Sigh. */
-       OMAP_MMC_READ(host, CON);
+       if (claimed) {
+               mmc_omap_fclk_enable(host, 1);
 
-       OMAP_MMC_WRITE(host, CON, slot->saved_con);
+               /* Doing the dummy read here seems to work around some bug
+                * at least in OMAP24xx silicon where the command would not
+                * start after writing the CMD register. Sigh. */
+               OMAP_MMC_READ(host, CON);
+
+               OMAP_MMC_WRITE(host, CON, slot->saved_con);
+       } else
+               mmc_omap_fclk_enable(host, 0);
 }
 
 static void mmc_omap_start_request(struct mmc_omap_host *host,
                                   struct mmc_request *req);
 
-static void mmc_omap_release_slot(struct mmc_omap_slot *slot)
+static void mmc_omap_slot_release_work(struct work_struct *work)
+{
+       struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
+                                                 slot_release_work);
+       struct mmc_omap_slot *next_slot = host->next_slot;
+       struct mmc_request *rq;
+
+       host->next_slot = NULL;
+       mmc_omap_select_slot(next_slot, 1);
+
+       rq = next_slot->mrq;
+       next_slot->mrq = NULL;
+       mmc_omap_start_request(host, rq);
+}
+
+static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
 {
        struct mmc_omap_host *host = slot->host;
        unsigned long flags;
        int i;
 
        BUG_ON(slot == NULL || host->mmc == NULL);
-       clk_disable(host->fclk);
+
+       if (clk_enabled)
+               /* Keeps clock running for at least 8 cycles on valid freq */
+               mod_timer(&host->clk_timer, jiffies  + HZ/10);
+       else {
+               del_timer(&host->clk_timer);
+               mmc_omap_fclk_offdelay(slot);
+               mmc_omap_fclk_enable(host, 0);
+       }
 
        spin_lock_irqsave(&host->slot_lock, flags);
        /* Check for any pending requests */
        for (i = 0; i < host->nr_slots; i++) {
                struct mmc_omap_slot *new_slot;
-               struct mmc_request *rq;
 
                if (host->slots[i] == NULL || host->slots[i]->mrq == NULL)
                        continue;
 
+               BUG_ON(host->next_slot != NULL);
                new_slot = host->slots[i];
                /* The current slot should not have a request in queue */
                BUG_ON(new_slot == host->current_slot);
 
+               host->next_slot = new_slot;
                host->mmc = new_slot->mmc;
                spin_unlock_irqrestore(&host->slot_lock, flags);
-               mmc_omap_select_slot(new_slot, 1);
-               rq = new_slot->mrq;
-               new_slot->mrq = NULL;
-               mmc_omap_start_request(host, rq);
+               schedule_work(&host->slot_release_work);
                return;
        }
 
@@ -226,6 +297,28 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot)
        spin_unlock_irqrestore(&host->slot_lock, flags);
 }
 
+static inline
+int mmc_omap_cover_is_open(struct mmc_omap_slot *slot)
+{
+       if (slot->pdata->get_cover_state)
+               return slot->pdata->get_cover_state(mmc_dev(slot->mmc),
+                                                   slot->id);
+       return 0;
+}
+
+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_slot *slot = mmc_priv(mmc);
+
+       return sprintf(buf, "%s\n", mmc_omap_cover_is_open(slot) ? "open" :
+                      "closed");
+}
+
+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)
@@ -291,6 +384,8 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
        if (host->data && !(host->data->flags & MMC_DATA_WRITE))
                cmdreg |= 1 << 15;
 
+       mod_timer(&host->cmd_abort_timer, jiffies + HZ/2);
+
        OMAP_MMC_WRITE(host, CTO, 200);
        OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
        OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
@@ -303,27 +398,47 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
        OMAP_MMC_WRITE(host, CMD, cmdreg);
 }
 
+static void
+mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
+                    int abort)
+{
+       enum dma_data_direction dma_data_dir;
+
+       BUG_ON(host->dma_ch < 0);
+       if (data->error)
+               omap_stop_dma(host->dma_ch);
+       /* Release DMA channel lazily */
+       mod_timer(&host->dma_timer, jiffies + HZ);
+       if (data->flags & MMC_DATA_WRITE)
+               dma_data_dir = DMA_TO_DEVICE;
+       else
+               dma_data_dir = DMA_FROM_DEVICE;
+       dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
+                    dma_data_dir);
+}
+
+static void mmc_omap_send_stop_work(struct work_struct *work)
+{
+       struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
+                                                 send_stop_work);
+       struct mmc_omap_slot *slot = host->current_slot;
+       struct mmc_data *data = host->stop_data;
+       unsigned long tick_ns;
+
+       tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq;
+       ndelay(8*tick_ns);
+
+       mmc_omap_start_command(host, data->stop);
+}
+
 static void
 mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
 {
-       if (host->dma_in_use) {
-               enum dma_data_direction dma_data_dir;
-
-               BUG_ON(host->dma_ch < 0);
-               if (data->error)
-                       omap_stop_dma(host->dma_ch);
-               /* Release DMA channel lazily */
-               mod_timer(&host->dma_timer, jiffies + HZ);
-               if (data->flags & MMC_DATA_WRITE)
-                       dma_data_dir = DMA_TO_DEVICE;
-               else
-                       dma_data_dir = DMA_FROM_DEVICE;
-               dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
-                            dma_data_dir);
-       }
+       if (host->dma_in_use)
+               mmc_omap_release_dma(host, data, data->error);
+
        host->data = NULL;
        host->sg_len = 0;
-       clk_disable(host->fclk);
 
        /* NOTE:  MMC layer will sometimes poll-wait CMD13 next, issuing
         * dozens of requests until the card finishes writing data.
@@ -331,12 +446,58 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
         */
 
        if (!data->stop) {
+               struct mmc_host *mmc;
+
                host->mrq = NULL;
-               mmc_request_done(host->mmc, data->mrq);
+               mmc = host->mmc;
+               mmc_omap_release_slot(host->current_slot, 1);
+               mmc_request_done(mmc, data->mrq);
                return;
        }
 
-       mmc_omap_start_command(host, data->stop);
+       host->stop_data = data;
+       schedule_work(&host->send_stop_work);
+}
+
+static void
+mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)
+{
+       struct mmc_omap_slot *slot = host->current_slot;
+       unsigned int restarts, passes, timeout;
+       u16 stat = 0;
+
+       /* Sending abort takes 80 clocks. Have some extra and round up */
+       timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
+       restarts = 0;
+       while (restarts < maxloops) {
+               OMAP_MMC_WRITE(host, STAT, 0xFFFF);
+               OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
+
+               passes = 0;
+               while (passes < timeout) {
+                       stat = OMAP_MMC_READ(host, STAT);
+                       if (stat & OMAP_MMC_STAT_END_OF_CMD)
+                               goto out;
+                       udelay(1);
+                       passes++;
+               }
+
+               restarts++;
+       }
+out:
+       OMAP_MMC_WRITE(host, STAT, stat);
+}
+
+static void
+mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
+{
+       if (host->dma_in_use)
+               mmc_omap_release_dma(host, data, 1);
+
+       host->data = NULL;
+       host->sg_len = 0;
+
+       mmc_omap_send_abort(host, 10000);
 }
 
 static void
@@ -392,6 +553,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
 {
        host->cmd = NULL;
 
+       del_timer(&host->cmd_abort_timer);
+
        if (cmd->flags & MMC_RSP_PRESENT) {
                if (cmd->flags & MMC_RSP_136) {
                        /* response type 2 */
@@ -416,10 +579,66 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
        }
 
        if (host->data == NULL || cmd->error) {
+               struct mmc_host *mmc;
+
+               if (host->data != NULL)
+                       mmc_omap_abort_xfer(host, host->data);
+               host->mrq = NULL;
+               mmc = host->mmc;
+               mmc_omap_release_slot(host->current_slot, 1);
+               mmc_request_done(mmc, cmd->mrq);
+       }
+}
+
+/*
+ * Abort stuck command. Can occur when card is removed while it is being
+ * read.
+ */
+static void mmc_omap_abort_command(struct work_struct *work)
+{
+       struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
+                                                 cmd_abort_work);
+       BUG_ON(!host->cmd);
+
+       dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
+               host->cmd->opcode);
+
+       if (host->cmd->error == 0)
+               host->cmd->error = -ETIMEDOUT;
+
+       if (host->data == NULL) {
+               struct mmc_command *cmd;
+               struct mmc_host    *mmc;
+
+               cmd = host->cmd;
+               host->cmd = NULL;
+               mmc_omap_send_abort(host, 10000);
+
                host->mrq = NULL;
-               clk_disable(host->fclk);
-               mmc_request_done(host->mmc, cmd->mrq);
+               mmc = host->mmc;
+               mmc_omap_release_slot(host->current_slot, 1);
+               mmc_request_done(mmc, cmd->mrq);
+       } else
+               mmc_omap_cmd_done(host, host->cmd);
+
+       host->abort = 0;
+       enable_irq(host->irq);
+}
+
+static void
+mmc_omap_cmd_timer(unsigned long data)
+{
+       struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->slot_lock, flags);
+       if (host->cmd != NULL && !host->abort) {
+               OMAP_MMC_WRITE(host, IE, 0);
+               disable_irq(host->irq);
+               host->abort = 1;
+               schedule_work(&host->cmd_abort_work);
        }
+       spin_unlock_irqrestore(&host->slot_lock, flags);
 }
 
 /* PIO only */
@@ -435,6 +654,14 @@ mmc_omap_sg_to_buf(struct mmc_omap_host *host)
                host->buffer_bytes_left = host->total_bytes_left;
 }
 
+static void
+mmc_omap_clk_timer(unsigned long data)
+{
+       struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+       mmc_omap_fclk_enable(host, 0);
+}
+
 /* PIO only */
 static void
 mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
@@ -483,11 +710,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        u16 status;
        int end_command;
        int end_transfer;
-       int transfer_error;
+       int transfer_error, cmd_error;
 
        if (host->cmd == NULL && host->data == NULL) {
                status = OMAP_MMC_READ(host, STAT);
-               dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status);
+               dev_info(mmc_dev(host->slots[0]->mmc),
+                        "Spurious IRQ 0x%04x\n", status);
                if (status != 0) {
                        OMAP_MMC_WRITE(host, STAT, status);
                        OMAP_MMC_WRITE(host, IE, 0);
@@ -498,12 +726,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        end_command = 0;
        end_transfer = 0;
        transfer_error = 0;
+       cmd_error = 0;
 
        while ((status = OMAP_MMC_READ(host, STAT)) != 0) {
+               int cmd;
+
                OMAP_MMC_WRITE(host, STAT, status);
+               if (host->cmd != NULL)
+                       cmd = host->cmd->opcode;
+               else
+                       cmd = -1;
 #ifdef CONFIG_MMC_DEBUG
                dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ",
-                       status, host->cmd != NULL ? host->cmd->opcode : -1);
+                       status, cmd);
                mmc_omap_report_irq(status);
                printk("\n");
 #endif
@@ -515,12 +750,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                                mmc_omap_xfer_data(host, 1);
                }
 
-               if (status & OMAP_MMC_STAT_END_OF_DATA) {
+               if (status & OMAP_MMC_STAT_END_OF_DATA)
                        end_transfer = 1;
-               }
 
                if (status & OMAP_MMC_STAT_DATA_TOUT) {
-                       dev_dbg(mmc_dev(host->mmc), "data timeout\n");
+                       dev_dbg(mmc_dev(host->mmc), "data timeout (CMD%d)\n",
+                               cmd);
                        if (host->data) {
                                host->data->error = -ETIMEDOUT;
                                transfer_error = 1;
@@ -544,11 +779,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                        if (host->cmd) {
                                struct mmc_omap_slot *slot =
                                        host->current_slot;
-                               dev_err(mmc_dev(host->mmc),
-                                       "command timeout, CMD %d\n",
-                                       host->cmd->opcode);
+                               if (slot == NULL ||
+                                   !mmc_omap_cover_is_open(slot))
+                                       dev_err(mmc_dev(host->mmc),
+                                               "command timeout (CMD%d)\n",
+                                               cmd);
                                host->cmd->error = -ETIMEDOUT;
                                end_command = 1;
+                               cmd_error = 1;
                        }
                }
 
@@ -556,9 +794,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                        if (host->cmd) {
                                dev_err(mmc_dev(host->mmc),
                                        "command CRC error (CMD%d, arg 0x%08x)\n",
-                                       host->cmd->opcode, host->cmd->arg);
+                                       cmd, host->cmd->arg);
                                host->cmd->error = -EILSEQ;
                                end_command = 1;
+                               cmd_error = 1;
                        } else
                                dev_err(mmc_dev(host->mmc),
                                        "command CRC error without cmd?\n");
@@ -567,13 +806,13 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                if (status & OMAP_MMC_STAT_CARD_ERR) {
                        dev_dbg(mmc_dev(host->mmc),
                                "ignoring card status error (CMD%d)\n",
-                               host->cmd->opcode);
+                               cmd);
                        end_command = 1;
                }
 
                /*
                 * NOTE: On 1610 the END_OF_CMD may come too early when
-                * starting a write 
+                * starting a write
                 */
                if ((status & OMAP_MMC_STAT_END_OF_CMD) &&
                    (!(status & OMAP_MMC_STAT_A_EMPTY))) {
@@ -581,17 +820,74 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                }
        }
 
-       if (end_command) {
+       if (cmd_error && host->data) {
+               del_timer(&host->cmd_abort_timer);
+               host->abort = 1;
+               OMAP_MMC_WRITE(host, IE, 0);
+               disable_irq(host->irq);
+               schedule_work(&host->cmd_abort_work);
+               return IRQ_HANDLED;
+       }
+
+       if (end_command)
                mmc_omap_cmd_done(host, host->cmd);
+       if (host->data != NULL) {
+               if (transfer_error)
+                       mmc_omap_xfer_done(host, host->data);
+               else if (end_transfer)
+                       mmc_omap_end_of_data(host, host->data);
        }
-       if (transfer_error)
-               mmc_omap_xfer_done(host, host->data);
-       else if (end_transfer)
-               mmc_omap_end_of_data(host, host->data);
 
        return IRQ_HANDLED;
 }
 
+void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
+{
+       int cover_open;
+       struct mmc_omap_host *host = dev_get_drvdata(dev);
+       struct mmc_omap_slot *slot = host->slots[num];
+
+       BUG_ON(num >= host->nr_slots);
+
+       /* Other subsystems can call in here before we're initialised. */
+       if (host->nr_slots == 0 || !host->slots[num])
+               return;
+
+       cover_open = mmc_omap_cover_is_open(slot);
+       if (cover_open != slot->cover_open) {
+               slot->cover_open = cover_open;
+               sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
+       }
+
+       tasklet_hi_schedule(&slot->cover_tasklet);
+}
+
+static void mmc_omap_cover_timer(unsigned long arg)
+{
+       struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
+       tasklet_schedule(&slot->cover_tasklet);
+}
+
+static void mmc_omap_cover_handler(unsigned long param)
+{
+       struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
+       int cover_open = mmc_omap_cover_is_open(slot);
+
+       mmc_detect_change(slot->mmc, 0);
+       if (!cover_open)
+               return;
+
+       /*
+        * If no card is inserted, we postpone polling until
+        * the cover has been closed.
+        */
+       if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card))
+               return;
+
+       mod_timer(&slot->cover_timer,
+                 jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
+}
+
 /* Prepare to transfer the next segment of a scatterlist */
 static void
 mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
@@ -706,7 +1002,7 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
 
 static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data)
 {
-       const char *dev_name;
+       const char *dma_dev_name;
        int sync_dev, dma_ch, is_read, r;
 
        is_read = !(data->flags & MMC_DATA_WRITE);
@@ -721,21 +1017,21 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
        if (is_read) {
                if (host->id == 1) {
                        sync_dev = OMAP_DMA_MMC_RX;
-                       dev_name = "MMC1 read";
+                       dma_dev_name = "MMC1 read";
                } else {
                        sync_dev = OMAP_DMA_MMC2_RX;
-                       dev_name = "MMC2 read";
+                       dma_dev_name = "MMC2 read";
                }
        } else {
                if (host->id == 1) {
                        sync_dev = OMAP_DMA_MMC_TX;
-                       dev_name = "MMC1 write";
+                       dma_dev_name = "MMC1 write";
                } else {
                        sync_dev = OMAP_DMA_MMC2_TX;
-                       dev_name = "MMC2 write";
+                       dma_dev_name = "MMC2 write";
                }
        }
-       r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb,
+       r = omap_request_dma(sync_dev, dma_dev_name, mmc_omap_dma_cb,
                             host, &dma_ch);
        if (r != 0) {
                dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r);
@@ -760,13 +1056,12 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_reques
 
 static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req)
 {
-       int timeout;
+       unsigned int timeout, cycle_ns;
        u16 reg;
 
-       /* Convert ns to clock cycles by assuming 20MHz frequency
-        * 1 cycle at 20MHz = 500 ns
-        */
-       timeout = req->data->timeout_clks + req->data->timeout_ns / 500;
+       cycle_ns = 1000000000 / host->current_slot->fclk_freq;
+       timeout = req->data->timeout_ns / cycle_ns;
+       timeout += req->data->timeout_clks;
 
        /* Check if we need to use timeout multiplier register */
        reg = OMAP_MMC_READ(host, SDIO);
@@ -883,52 +1178,27 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
        mmc_omap_start_request(host, req);
 }
 
-static void innovator_fpga_socket_power(int on)
+static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on,
+                               int vdd)
 {
-#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX)
-       if (on) {
-               fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3),
-                    OMAP1510_FPGA_POWER);
-       } else {
-               fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3),
-                    OMAP1510_FPGA_POWER);
-       }
-#endif
-}
+       struct mmc_omap_host *host;
 
-/*
- * Turn the socket power on/off. Innovator uses FPGA, most boards
- * probably use GPIO.
- */
-static void mmc_omap_power(struct mmc_omap_host *host, int on)
-{
-       if (on) {
-               if (machine_is_omap_innovator())
-                       innovator_fpga_socket_power(1);
-               else if (machine_is_omap_h2())
-                       tps65010_set_gpio_out_value(GPIO3, HIGH);
-               else if (machine_is_omap_h3())
-                       /* GPIO 4 of TPS65010 sends SD_EN signal */
-                       tps65010_set_gpio_out_value(GPIO4, HIGH);
-               else if (cpu_is_omap24xx()) {
-                       u16 reg = OMAP_MMC_READ(host, CON);
-                       OMAP_MMC_WRITE(host, CON, reg | (1 << 11));
-               } else
-                       if (host->power_pin >= 0)
-                               omap_set_gpio_dataout(host->power_pin, 1);
-       } else {
-               if (machine_is_omap_innovator())
-                       innovator_fpga_socket_power(0);
-               else if (machine_is_omap_h2())
-                       tps65010_set_gpio_out_value(GPIO3, LOW);
-               else if (machine_is_omap_h3())
-                       tps65010_set_gpio_out_value(GPIO4, LOW);
-               else if (cpu_is_omap24xx()) {
-                       u16 reg = OMAP_MMC_READ(host, CON);
-                       OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11));
-               } else
-                       if (host->power_pin >= 0)
-                               omap_set_gpio_dataout(host->power_pin, 0);
+       host = slot->host;
+
+       if (slot->pdata->set_power != NULL)
+               slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on,
+                                       vdd);
+
+       if (cpu_is_omap24xx()) {
+               u16 w;
+
+               if (power_on) {
+                       w = OMAP_MMC_READ(host, CON);
+                       OMAP_MMC_WRITE(host, CON, w | (1 << 11));
+               } else {
+                       w = OMAP_MMC_READ(host, CON);
+                       OMAP_MMC_WRITE(host, CON, w & ~(1 << 11));
+               }
        }
 }
 
@@ -965,25 +1235,37 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        struct mmc_omap_slot *slot = mmc_priv(mmc);
        struct mmc_omap_host *host = slot->host;
        int i, dsor;
+       int clk_enabled;
+
+       mmc_omap_select_slot(slot, 0);
 
        dsor = mmc_omap_calc_divisor(mmc, ios);
-       host->bus_mode = ios->bus_mode;
-       host->hw_bus_mode = host->bus_mode;
 
+       if (ios->vdd != slot->vdd)
+               slot->vdd = ios->vdd;
+
+       clk_enabled = 0;
        switch (ios->power_mode) {
        case MMC_POWER_OFF:
-               mmc_omap_power(host, 0);
+               mmc_omap_set_power(slot, 0, ios->vdd);
                break;
        case MMC_POWER_UP:
                /* Cannot touch dsor yet, just power up MMC */
-               mmc_omap_power(host, 1);
-               return;
+               mmc_omap_set_power(slot, 1, ios->vdd);
+               goto exit;
        case MMC_POWER_ON:
+               mmc_omap_fclk_enable(host, 1);
+               clk_enabled = 1;
                dsor |= 1 << 11;
                break;
        }
 
-       clk_enable(host->fclk);
+       if (slot->bus_mode != ios->bus_mode) {
+               if (slot->pdata->set_bus_mode != NULL)
+                       slot->pdata->set_bus_mode(mmc_dev(mmc), slot->id,
+                                                 ios->bus_mode);
+               slot->bus_mode = ios->bus_mode;
+       }
 
        /* On insanely high arm_per frequencies something sometimes
         * goes somehow out of sync, and the POW bit is not being set,
@@ -991,15 +1273,24 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
         * Writing to the CON register twice seems to do the trick. */
        for (i = 0; i < 2; i++)
                OMAP_MMC_WRITE(host, CON, dsor);
+       slot->saved_con = dsor;
        if (ios->power_mode == MMC_POWER_ON) {
+               /* worst case at 400kHz, 80 cycles makes 200 microsecs */
+               int usecs = 250;
+
                /* Send clock cycles, poll completion */
                OMAP_MMC_WRITE(host, IE, 0);
                OMAP_MMC_WRITE(host, STAT, 0xffff);
                OMAP_MMC_WRITE(host, CMD, 1 << 7);
-               while ((OMAP_MMC_READ(host, STAT) & 1) == 0);
+               while (usecs > 0 && (OMAP_MMC_READ(host, STAT) & 1) == 0) {
+                       udelay(1);
+                       usecs--;
+               }
                OMAP_MMC_WRITE(host, STAT, 1);
        }
-       clk_disable(host->fclk);
+
+exit:
+       mmc_omap_release_slot(slot, clk_enabled);
 }
 
 static const struct mmc_host_ops mmc_omap_ops = {
@@ -1025,7 +1316,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
 
        host->slots[id] = slot;
 
-       mmc->caps = MMC_CAP_MULTIWRITE;
+       mmc->caps = 0;
        if (host->pdata->conf.wire4)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
@@ -1062,8 +1353,24 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
                        goto err_remove_host;
        }
 
+       if (slot->pdata->get_cover_state != NULL) {
+               r = device_create_file(&mmc->class_dev,
+                                       &dev_attr_cover_switch);
+               if (r < 0)
+                       goto err_remove_slot_name;
+
+               setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
+                           (unsigned long)slot);
+               tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
+                            (unsigned long)slot);
+               tasklet_schedule(&slot->cover_tasklet);
+       }
+
        return 0;
 
+err_remove_slot_name:
+       if (slot->pdata->name != NULL)
+               device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
 err_remove_host:
        mmc_remove_host(mmc);
        mmc_free_host(mmc);
@@ -1076,6 +1383,12 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
 
        if (slot->pdata->name != NULL)
                device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
+       if (slot->pdata->get_cover_state != NULL)
+               device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
+
+       tasklet_kill(&slot->cover_tasklet);
+       del_timer_sync(&slot->cover_timer);
+       flush_scheduled_work();
 
        mmc_remove_host(mmc);
        mmc_free_host(mmc);
@@ -1114,14 +1427,21 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
                goto err_free_mem_region;
        }
 
+       INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);
+       INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work);
+
+       INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command);
+       setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer,
+                   (unsigned long) host);
+
+       spin_lock_init(&host->clk_lock);
+       setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
+
        spin_lock_init(&host->dma_lock);
-       init_timer(&host->dma_timer);
+       setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
        spin_lock_init(&host->slot_lock);
        init_waitqueue_head(&host->slot_wq);
 
-       host->dma_timer.function = mmc_omap_dma_timer;
-       host->dma_timer.data = (unsigned long) host;
-
        host->pdata = pdata;
        host->dev = &pdev->dev;
        platform_set_drvdata(pdev, host);
@@ -1135,7 +1455,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 
        host->irq = irq;
        host->phys_base = host->mem_res->start;
-       host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base);
+       host->virt_base = ioremap(res->start, res->end - res->start + 1);
+       if (!host->virt_base)
+               goto err_ioremap;
 
        if (cpu_is_omap24xx()) {
                host->iclk = clk_get(&pdev->dev, "mmc_ick");
@@ -1190,6 +1512,8 @@ err_free_iclk:
                clk_put(host->iclk);
        }
 err_free_mmc_host:
+       iounmap(host->virt_base);
+err_ioremap:
        kfree(host);
 err_free_mem_region:
        release_mem_region(res->start, res->end - res->start + 1);
@@ -1216,6 +1540,7 @@ static int mmc_omap_remove(struct platform_device *pdev)
        if (host->fclk && !IS_ERR(host->fclk))
                clk_put(host->fclk);
 
+       iounmap(host->virt_base);
        release_mem_region(pdev->resource[0].start,
                           pdev->resource[0].end - pdev->resource[0].start + 1);