]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mmc/host/omap.c
MMC: OMAP: General cleanup for MMC multislot support
[linux-2.6-omap-h63xx.git] / drivers / mmc / host / omap.c
index 712a9608acf77938045e97d874825fa773664840..535499f11e7c2cabbb6cf67ad60bac16f217c78b 100644 (file)
@@ -326,27 +326,33 @@ 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_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.
@@ -354,14 +360,44 @@ 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);
+               mmc_request_done(mmc, data->mrq);
                return;
        }
 
        mmc_omap_start_command(host, data->stop);
 }
 
+static void
+mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
+{
+       int loops;
+       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, CMD, 1 << 7);
+       loops = 0;
+       while (!(OMAP_MMC_READ(host, STAT) & OMAP_MMC_STAT_END_OF_CMD)) {
+               udelay(1);
+               loops++;
+               if (loops == 100000)
+                       break;
+       }
+       OMAP_MMC_WRITE(host, STAT, OMAP_MMC_STAT_END_OF_CMD);
+       OMAP_MMC_WRITE(host, IE, ie);
+}
+
 static void
 mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
 {
@@ -439,9 +475,14 @@ 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;
-               clk_disable(host->fclk);
-               mmc_request_done(host->mmc, cmd->mrq);
+               mmc = host->mmc;
+               mmc_omap_release_slot(host->current_slot);
+               mmc_request_done(mmc, cmd->mrq);
        }
 }
 
@@ -506,11 +547,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);
@@ -521,12 +563,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
@@ -538,12 +587,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;
@@ -567,12 +616,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                        if (host->cmd) {
                                struct mmc_omap_slot *slot =
                                        host->current_slot;
-                               if (!mmc_omap_cover_is_open(slot))
+                               if (slot == NULL ||
+                                   !mmc_omap_cover_is_open(slot))
                                        dev_err(mmc_dev(host->mmc),
-                                               "command timeout, CMD %d\n",
-                                               host->cmd->opcode);
+                                               "command timeout (CMD%d)\n",
+                                               cmd);
                                host->cmd->error = -ETIMEDOUT;
                                end_command = 1;
+                               cmd_error = 1;
                        }
                }
 
@@ -580,9 +631,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");
@@ -591,13 +643,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))) {
@@ -605,13 +657,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                }
        }
 
-       if (end_command) {
+       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;
 }
@@ -820,13 +873,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);
@@ -943,52 +995,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));
+               }
        }
 }
 
@@ -1027,23 +1054,31 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        int i, dsor;
 
        dsor = mmc_omap_calc_divisor(mmc, ios);
-       host->bus_mode = ios->bus_mode;
-       host->hw_bus_mode = host->bus_mode;
+
+       mmc_omap_select_slot(slot, 0);
+
+       if (ios->vdd != slot->vdd)
+               slot->vdd = ios->vdd;
 
        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:
                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,
@@ -1051,6 +1086,7 @@ 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) {
                /* Send clock cycles, poll completion */
                OMAP_MMC_WRITE(host, IE, 0);
@@ -1059,7 +1095,9 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                while ((OMAP_MMC_READ(host, STAT) & 1) == 0);
                OMAP_MMC_WRITE(host, STAT, 1);
        }
-       clk_disable(host->fclk);
+
+exit:
+       mmc_omap_release_slot(slot);
 }
 
 static const struct mmc_host_ops mmc_omap_ops = {