]> err.no Git - linux-2.6/blobdiff - drivers/mmc/host/omap.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / drivers / mmc / host / omap.c
index 3d59c5d81b3d1e5e282e046b81250a347640a2fe..dbc26eb6a89e06c280f263a88ed02a20736220e7 100644 (file)
@@ -138,6 +138,11 @@ struct mmc_omap_host {
        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;
@@ -236,6 +241,21 @@ no_claim:
 static void mmc_omap_start_request(struct mmc_omap_host *host,
                                   struct mmc_request *req);
 
+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;
@@ -257,21 +277,19 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
        /* 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;
        }
 
@@ -400,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)
 {
@@ -424,7 +456,8 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
                return;
        }
 
-       mmc_omap_start_command(host, data->stop);
+       host->stop_data = data;
+       schedule_work(&host->send_stop_work);
 }
 
 static void
@@ -970,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);
@@ -985,21 +1018,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);
@@ -1243,11 +1276,17 @@ 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);
        }
 
@@ -1278,7 +1317,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;
 
@@ -1389,6 +1428,9 @@ 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);