Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
[safe/jmp/linux-2.6] / drivers / mmc / host / omap.c
index a1dfa74..2b28168 100644 (file)
 #include <linux/clk.h>
 #include <linux/scatterlist.h>
 #include <linux/i2c/tps65010.h>
+#include <linux/slab.h>
 
 #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 <plat/board.h>
+#include <plat/mmc.h>
+#include <mach/gpio.h>
+#include <plat/dma.h>
+#include <plat/mux.h>
+#include <plat/fpga.h>
 
 #define        OMAP_MMC_REG_CMD        0x00
-#define        OMAP_MMC_REG_ARGL       0x04
-#define        OMAP_MMC_REG_ARGH       0x08
-#define        OMAP_MMC_REG_CON        0x0c
-#define        OMAP_MMC_REG_STAT       0x10
-#define        OMAP_MMC_REG_IE         0x14
-#define        OMAP_MMC_REG_CTO        0x18
-#define        OMAP_MMC_REG_DTO        0x1c
-#define        OMAP_MMC_REG_DATA       0x20
-#define        OMAP_MMC_REG_BLEN       0x24
-#define        OMAP_MMC_REG_NBLK       0x28
-#define        OMAP_MMC_REG_BUF        0x2c
-#define OMAP_MMC_REG_SDIO      0x34
-#define        OMAP_MMC_REG_REV        0x3c
-#define        OMAP_MMC_REG_RSP0       0x40
-#define        OMAP_MMC_REG_RSP1       0x44
-#define        OMAP_MMC_REG_RSP2       0x48
-#define        OMAP_MMC_REG_RSP3       0x4c
-#define        OMAP_MMC_REG_RSP4       0x50
-#define        OMAP_MMC_REG_RSP5       0x54
-#define        OMAP_MMC_REG_RSP6       0x58
-#define        OMAP_MMC_REG_RSP7       0x5c
-#define        OMAP_MMC_REG_IOSR       0x60
-#define        OMAP_MMC_REG_SYSC       0x64
-#define        OMAP_MMC_REG_SYSS       0x68
+#define        OMAP_MMC_REG_ARGL       0x01
+#define        OMAP_MMC_REG_ARGH       0x02
+#define        OMAP_MMC_REG_CON        0x03
+#define        OMAP_MMC_REG_STAT       0x04
+#define        OMAP_MMC_REG_IE         0x05
+#define        OMAP_MMC_REG_CTO        0x06
+#define        OMAP_MMC_REG_DTO        0x07
+#define        OMAP_MMC_REG_DATA       0x08
+#define        OMAP_MMC_REG_BLEN       0x09
+#define        OMAP_MMC_REG_NBLK       0x0a
+#define        OMAP_MMC_REG_BUF        0x0b
+#define        OMAP_MMC_REG_SDIO       0x0d
+#define        OMAP_MMC_REG_REV        0x0f
+#define        OMAP_MMC_REG_RSP0       0x10
+#define        OMAP_MMC_REG_RSP1       0x11
+#define        OMAP_MMC_REG_RSP2       0x12
+#define        OMAP_MMC_REG_RSP3       0x13
+#define        OMAP_MMC_REG_RSP4       0x14
+#define        OMAP_MMC_REG_RSP5       0x15
+#define        OMAP_MMC_REG_RSP6       0x16
+#define        OMAP_MMC_REG_RSP7       0x17
+#define        OMAP_MMC_REG_IOSR       0x18
+#define        OMAP_MMC_REG_SYSC       0x19
+#define        OMAP_MMC_REG_SYSS       0x1a
 
 #define        OMAP_MMC_STAT_CARD_ERR          (1 << 14)
 #define        OMAP_MMC_STAT_CARD_IRQ          (1 << 13)
@@ -78,8 +78,9 @@
 #define        OMAP_MMC_STAT_CARD_BUSY         (1 <<  2)
 #define        OMAP_MMC_STAT_END_OF_CMD        (1 <<  0)
 
-#define OMAP_MMC_READ(host, reg)       __raw_readw((host)->virt_base + OMAP_MMC_REG_##reg)
-#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg)
+#define OMAP_MMC_REG(host, reg)                (OMAP_MMC_REG_##reg << (host)->reg_shift)
+#define OMAP_MMC_READ(host, reg)       __raw_readw((host)->virt_base + OMAP_MMC_REG(host, reg))
+#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG(host, reg))
 
 /*
  * Command types
@@ -94,7 +95,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,8 +107,8 @@ struct mmc_omap_slot {
        unsigned int            fclk_freq;
        unsigned                powered:1;
 
-       struct work_struct      switch_work;
-       struct timer_list       switch_timer;
+       struct tasklet_struct   cover_tasklet;
+       struct timer_list       cover_timer;
        unsigned                cover_open;
 
        struct mmc_request      *mrq;
@@ -133,9 +134,16 @@ struct mmc_omap_host {
        int                     irq;
        unsigned char           bus_mode;
        unsigned char           hw_bus_mode;
+       unsigned int            reg_shift;
 
-       struct work_struct      cmd_abort;
-       struct timer_list       cmd_timer;
+       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;
@@ -152,17 +160,44 @@ struct mmc_omap_host {
        struct timer_list       dma_timer;
        unsigned                dma_len;
 
-       short                   power_pin;
-
        struct mmc_omap_slot    *slots[OMAP_MMC_MAX_SLOTS];
        struct mmc_omap_slot    *current_slot;
        spinlock_t              slot_lock;
        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;
@@ -179,52 +214,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);
+
+               /* 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);
+               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;
        }
 
@@ -236,7 +301,10 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot)
 static inline
 int mmc_omap_cover_is_open(struct mmc_omap_slot *slot)
 {
-       return slot->pdata->get_cover_state(mmc_dev(slot->mmc), slot->id);
+       if (slot->pdata->get_cover_state)
+               return slot->pdata->get_cover_state(mmc_dev(slot->mmc),
+                                                   slot->id);
+       return 0;
 }
 
 static ssize_t
@@ -317,7 +385,7 @@ 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_timer, jiffies + HZ/2);
+       mod_timer(&host->cmd_abort_timer, jiffies + HZ/2);
 
        OMAP_MMC_WRITE(host, CTO, 200);
        OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
@@ -350,6 +418,20 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
                     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)
 {
@@ -369,16 +451,17 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
 
                host->mrq = NULL;
                mmc = host->mmc;
-               mmc_omap_release_slot(host->current_slot);
+               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)
+mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)
 {
        struct mmc_omap_slot *slot = host->current_slot;
        unsigned int restarts, passes, timeout;
@@ -387,7 +470,7 @@ mmc_omap_send_abort(struct mmc_omap_host *host)
        /* 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 < 10000) {
+       while (restarts < maxloops) {
                OMAP_MMC_WRITE(host, STAT, 0xFFFF);
                OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
 
@@ -409,18 +492,13 @@ out:
 static void
 mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
 {
-       u16 ie;
-
        if (host->dma_in_use)
                mmc_omap_release_dma(host, data, 1);
 
        host->data = NULL;
        host->sg_len = 0;
 
-       ie = OMAP_MMC_READ(host, IE);
-       OMAP_MMC_WRITE(host, IE, 0);
-       OMAP_MMC_WRITE(host, IE, ie);
-       mmc_omap_send_abort(host);
+       mmc_omap_send_abort(host, 10000);
 }
 
 static void
@@ -476,7 +554,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
 {
        host->cmd = NULL;
 
-       del_timer(&host->cmd_timer);
+       del_timer(&host->cmd_abort_timer);
 
        if (cmd->flags & MMC_RSP_PRESENT) {
                if (cmd->flags & MMC_RSP_136) {
@@ -508,7 +586,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
                        mmc_omap_abort_xfer(host, host->data);
                host->mrq = NULL;
                mmc = host->mmc;
-               mmc_omap_release_slot(host->current_slot);
+               mmc_omap_release_slot(host->current_slot, 1);
                mmc_request_done(mmc, cmd->mrq);
        }
 }
@@ -520,38 +598,48 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
 static void mmc_omap_abort_command(struct work_struct *work)
 {
        struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
-                                                 cmd_abort);
-       u16 ie;
-
-       ie = OMAP_MMC_READ(host, IE);
-       OMAP_MMC_WRITE(host, IE, 0);
-
-       if (!host->cmd) {
-               OMAP_MMC_WRITE(host, IE, ie);
-               return;
-       }
+                                                 cmd_abort_work);
+       BUG_ON(!host->cmd);
 
        dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
                host->cmd->opcode);
 
-       if (host->data && host->dma_in_use)
-               mmc_omap_release_dma(host, host->data, 1);
+       if (host->cmd->error == 0)
+               host->cmd->error = -ETIMEDOUT;
 
-       host->data = NULL;
-       host->sg_len = 0;
+       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;
+               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);
 
-       mmc_omap_send_abort(host);
-       host->cmd->error = -ETIMEDOUT;
-       mmc_omap_cmd_done(host, host->cmd);
-       OMAP_MMC_WRITE(host, IE, ie);
+       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;
 
-       schedule_work(&host->cmd_abort);
+       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 */
@@ -567,6 +655,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)
@@ -586,9 +682,9 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
        host->data->bytes_xfered += n;
 
        if (write) {
-               __raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+               __raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
        } else {
-               __raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+               __raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
        }
 }
 
@@ -725,6 +821,15 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                }
        }
 
+       if (cmd_error && host->data) {
+               del_timer(&host->cmd_abort_timer);
+               host->abort = 1;
+               OMAP_MMC_WRITE(host, IE, 0);
+               disable_irq_nosync(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) {
@@ -737,40 +842,51 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-void omap_mmc_notify_cover_event(struct device *dev, int slot, int is_closed)
+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(slot >= host->nr_slots);
+       BUG_ON(num >= host->nr_slots);
 
        /* Other subsystems can call in here before we're initialised. */
-       if (host->nr_slots == 0 || !host->slots[slot])
+       if (host->nr_slots == 0 || !host->slots[num])
                return;
 
-       schedule_work(&host->slots[slot]->switch_work);
+       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_switch_timer(unsigned long arg)
+static void mmc_omap_cover_timer(unsigned long arg)
 {
        struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
-
-       schedule_work(&slot->switch_work);
+       tasklet_schedule(&slot->cover_tasklet);
 }
 
-static void mmc_omap_cover_handler(struct work_struct *work)
+static void mmc_omap_cover_handler(unsigned long param)
 {
-       struct mmc_omap_slot *slot = container_of(work, struct mmc_omap_slot,
-                                                 switch_work);
-       int cover_open;
+       struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
+       int cover_open = mmc_omap_cover_is_open(slot);
 
-       cover_open = mmc_omap_cover_is_open(slot);
-       if (cover_open != slot->cover_open) {
-               sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
-               slot->cover_open = cover_open;
-               dev_info(mmc_dev(slot->mmc), "cover is now %s\n",
-                        cover_open ? "open" : "closed");
-       }
-       mmc_detect_change(slot->mmc, slot->id);
+       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 */
@@ -786,7 +902,7 @@ mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
        int dst_port = 0;
        int sync_dev = 0;
 
-       data_addr = host->phys_base + OMAP_MMC_REG_DATA;
+       data_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
        frame = data->blksz;
        count = sg_dma_len(sg);
 
@@ -887,7 +1003,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);
@@ -900,23 +1016,23 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
        }
 
        if (is_read) {
-               if (host->id == 1) {
+               if (host->id == 0) {
                        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) {
+               if (host->id == 0) {
                        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);
@@ -1120,14 +1236,16 @@ 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;
-
-       dsor = mmc_omap_calc_divisor(mmc, ios);
+       int clk_enabled;
 
        mmc_omap_select_slot(slot, 0);
 
+       dsor = mmc_omap_calc_divisor(mmc, ios);
+
        if (ios->vdd != slot->vdd)
                slot->vdd = ios->vdd;
 
+       clk_enabled = 0;
        switch (ios->power_mode) {
        case MMC_POWER_OFF:
                mmc_omap_set_power(slot, 0, ios->vdd);
@@ -1137,6 +1255,8 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                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;
        }
@@ -1156,16 +1276,22 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                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);
        }
 
 exit:
-       mmc_omap_release_slot(slot);
+       mmc_omap_release_slot(slot, clk_enabled);
 }
 
 static const struct mmc_host_ops mmc_omap_ops = {
@@ -1191,8 +1317,8 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
 
        host->slots[id] = slot;
 
-       mmc->caps = MMC_CAP_MULTIWRITE;
-       if (host->pdata->conf.wire4)
+       mmc->caps = 0;
+       if (host->pdata->slots[id].wires >= 4)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
        mmc->ops = &mmc_omap_ops;
@@ -1234,11 +1360,11 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
                if (r < 0)
                        goto err_remove_slot_name;
 
-               INIT_WORK(&slot->switch_work, mmc_omap_cover_handler);
-               init_timer(&slot->switch_timer);
-               slot->switch_timer.function = mmc_omap_switch_timer;
-               slot->switch_timer.data = (unsigned long) slot;
-               schedule_work(&slot->switch_work);
+               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;
@@ -1261,7 +1387,8 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
        if (slot->pdata->get_cover_state != NULL)
                device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
 
-       del_timer_sync(&slot->switch_timer);
+       tasklet_kill(&slot->cover_tasklet);
+       del_timer_sync(&slot->cover_timer);
        flush_scheduled_work();
 
        mmc_remove_host(mmc);
@@ -1301,19 +1428,21 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
                goto err_free_mem_region;
        }
 
-       INIT_WORK(&host->cmd_abort, mmc_omap_abort_command);
-       init_timer(&host->cmd_timer);
-       host->cmd_timer.function = mmc_omap_cmd_timer;
-       host->cmd_timer.data = (unsigned long) host;
+       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);
@@ -1323,24 +1452,23 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
        host->irq = irq;
 
        host->use_dma = 1;
+       host->dev->dma_mask = &pdata->dma_mask;
        host->dma_ch = -1;
 
        host->irq = irq;
        host->phys_base = host->mem_res->start;
-       host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base);
-
-       if (cpu_is_omap24xx()) {
-               host->iclk = clk_get(&pdev->dev, "mmc_ick");
-               if (IS_ERR(host->iclk))
-                       goto err_free_mmc_host;
-               clk_enable(host->iclk);
+       host->virt_base = ioremap(res->start, res->end - res->start + 1);
+       if (!host->virt_base)
+               goto err_ioremap;
+
+       host->iclk = clk_get(&pdev->dev, "ick");
+       if (IS_ERR(host->iclk)) {
+               ret = PTR_ERR(host->iclk);
+               goto err_free_mmc_host;
        }
+       clk_enable(host->iclk);
 
-       if (!cpu_is_omap24xx())
-               host->fclk = clk_get(&pdev->dev, "mmc_ck");
-       else
-               host->fclk = clk_get(&pdev->dev, "mmc_fck");
-
+       host->fclk = clk_get(&pdev->dev, "fck");
        if (IS_ERR(host->fclk)) {
                ret = PTR_ERR(host->fclk);
                goto err_free_iclk;
@@ -1367,6 +1495,8 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
                }
        }
 
+       host->reg_shift = (cpu_is_omap7xx() ? 1 : 2);
+
        return 0;
 
 err_plat_cleanup:
@@ -1377,11 +1507,11 @@ err_free_irq:
 err_free_fclk:
        clk_put(host->fclk);
 err_free_iclk:
-       if (host->iclk != NULL) {
-               clk_disable(host->iclk);
-               clk_put(host->iclk);
-       }
+       clk_disable(host->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);
@@ -1403,11 +1533,13 @@ static int mmc_omap_remove(struct platform_device *pdev)
        if (host->pdata->cleanup)
                host->pdata->cleanup(&pdev->dev);
 
-       if (host->iclk && !IS_ERR(host->iclk))
-               clk_put(host->iclk);
-       if (host->fclk && !IS_ERR(host->fclk))
-               clk_put(host->fclk);
+       mmc_omap_fclk_enable(host, 0);
+       free_irq(host->irq, host);
+       clk_put(host->fclk);
+       clk_disable(host->iclk);
+       clk_put(host->iclk);
 
+       iounmap(host->virt_base);
        release_mem_region(pdev->resource[0].start,
                           pdev->resource[0].end - pdev->resource[0].start + 1);
 
@@ -1429,7 +1561,7 @@ static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
                struct mmc_omap_slot *slot;
 
                slot = host->slots[i];
-               ret = mmc_suspend_host(slot->mmc, mesg);
+               ret = mmc_suspend_host(slot->mmc);
                if (ret < 0) {
                        while (--i >= 0) {
                                slot = host->slots[i];
@@ -1467,7 +1599,6 @@ static int mmc_omap_resume(struct platform_device *pdev)
 #endif
 
 static struct platform_driver mmc_omap_driver = {
-       .probe          = mmc_omap_probe,
        .remove         = mmc_omap_remove,
        .suspend        = mmc_omap_suspend,
        .resume         = mmc_omap_resume,
@@ -1479,7 +1610,7 @@ static struct platform_driver mmc_omap_driver = {
 
 static int __init mmc_omap_init(void)
 {
-       return platform_driver_register(&mmc_omap_driver);
+       return platform_driver_probe(&mmc_omap_driver, mmc_omap_probe);
 }
 
 static void __exit mmc_omap_exit(void)