Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / mmc / host / sdhci.c
index 07cca55..c31a334 100644 (file)
@@ -46,6 +46,8 @@ static void sdhci_finish_data(struct sdhci_host *);
 
 static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
+static int sdhci_execute_tuning(struct mmc_host *mmc);
+static void sdhci_tuning_timer(unsigned long data);
 
 static void sdhci_dumpregs(struct sdhci_host *host)
 {
@@ -125,11 +127,15 @@ static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
 
 static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
 {
-       u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+       u32 present, irqs;
 
        if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
                return;
 
+       present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+                             SDHCI_CARD_PRESENT;
+       irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
+
        if (enable)
                sdhci_unmask_irqs(host, irqs);
        else
@@ -836,9 +842,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 }
 
 static void sdhci_set_transfer_mode(struct sdhci_host *host,
-       struct mmc_data *data)
+       struct mmc_command *cmd)
 {
        u16 mode;
+       struct mmc_data *data = cmd->data;
 
        if (data == NULL)
                return;
@@ -846,12 +853,20 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
        WARN_ON(!host->data);
 
        mode = SDHCI_TRNS_BLK_CNT_EN;
-       if (data->blocks > 1) {
-               if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
-                       mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
-               else
-                       mode |= SDHCI_TRNS_MULTI;
+       if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
+               mode |= SDHCI_TRNS_MULTI;
+               /*
+                * If we are sending CMD23, CMD12 never gets sent
+                * on successful completion (so no Auto-CMD12).
+                */
+               if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12))
+                       mode |= SDHCI_TRNS_AUTO_CMD12;
+               else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
+                       mode |= SDHCI_TRNS_AUTO_CMD23;
+                       sdhci_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2);
+               }
        }
+
        if (data->flags & MMC_DATA_READ)
                mode |= SDHCI_TRNS_READ;
        if (host->flags & SDHCI_REQ_USE_DMA)
@@ -891,7 +906,15 @@ static void sdhci_finish_data(struct sdhci_host *host)
        else
                data->bytes_xfered = data->blksz * data->blocks;
 
-       if (data->stop) {
+       /*
+        * Need to send CMD12 if -
+        * a) open-ended multiblock transfer (no CMD23)
+        * b) error in multiblock transfer
+        */
+       if (data->stop &&
+           (data->error ||
+            !host->mrq->sbc)) {
+
                /*
                 * The controller needs a reset of internal state machines
                 * upon error conditions.
@@ -947,7 +970,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 
        sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
 
-       sdhci_set_transfer_mode(host, cmd->data);
+       sdhci_set_transfer_mode(host, cmd);
 
        if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
                printk(KERN_ERR "%s: Unsupported response type!\n",
@@ -1002,13 +1025,21 @@ static void sdhci_finish_command(struct sdhci_host *host)
 
        host->cmd->error = 0;
 
-       if (host->data && host->data_early)
-               sdhci_finish_data(host);
+       /* Finished CMD23, now send actual command. */
+       if (host->cmd == host->mrq->sbc) {
+               host->cmd = NULL;
+               sdhci_send_command(host, host->mrq->cmd);
+       } else {
 
-       if (!host->cmd->data)
-               tasklet_schedule(&host->finish_tasklet);
+               /* Processed actual command. */
+               if (host->data && host->data_early)
+                       sdhci_finish_data(host);
 
-       host->cmd = NULL;
+               if (!host->cmd->data)
+                       tasklet_schedule(&host->finish_tasklet);
+
+               host->cmd = NULL;
+       }
 }
 
 static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
@@ -1187,7 +1218,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 #ifndef SDHCI_USE_LEDS_CLASS
        sdhci_activate_led(host);
 #endif
-       if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
+
+       /*
+        * Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
+        * requests if Auto-CMD12 is enabled.
+        */
+       if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) {
                if (mrq->stop) {
                        mrq->data->stop = NULL;
                        mrq->stop = NULL;
@@ -1206,8 +1242,30 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        if (!present || host->flags & SDHCI_DEVICE_DEAD) {
                host->mrq->cmd->error = -ENOMEDIUM;
                tasklet_schedule(&host->finish_tasklet);
-       } else
-               sdhci_send_command(host, mrq->cmd);
+       } else {
+               u32 present_state;
+
+               present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+               /*
+                * Check if the re-tuning timer has already expired and there
+                * is no on-going data transfer. If so, we need to execute
+                * tuning procedure before sending command.
+                */
+               if ((host->flags & SDHCI_NEEDS_RETUNING) &&
+                   !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
+                       spin_unlock_irqrestore(&host->lock, flags);
+                       sdhci_execute_tuning(mmc);
+                       spin_lock_irqsave(&host->lock, flags);
+
+                       /* Restore original mmc_request structure */
+                       host->mrq = mrq;
+               }
+
+               if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
+                       sdhci_send_command(host, mrq->sbc);
+               else
+                       sdhci_send_command(host, mrq->cmd);
+       }
 
        mmiowb();
        spin_unlock_irqrestore(&host->lock, flags);
@@ -1325,27 +1383,30 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        sdhci_set_clock(host, clock);
                }
 
-               ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
-
-               /* Select Bus Speed Mode for host */
-               ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
-               if (ios->timing == MMC_TIMING_UHS_SDR12)
-                       ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
-               else if (ios->timing == MMC_TIMING_UHS_SDR25)
-                       ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
-               else if (ios->timing == MMC_TIMING_UHS_SDR50)
-                       ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
-               else if (ios->timing == MMC_TIMING_UHS_SDR104)
-                       ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
-               else if (ios->timing == MMC_TIMING_UHS_DDR50)
-                       ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
 
                /* Reset SD Clock Enable */
                clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
                clk &= ~SDHCI_CLOCK_CARD_EN;
                sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 
-               sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+               if (host->ops->set_uhs_signaling)
+                       host->ops->set_uhs_signaling(host, ios->timing);
+               else {
+                       ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+                       /* Select Bus Speed Mode for host */
+                       ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+                       if (ios->timing == MMC_TIMING_UHS_SDR12)
+                               ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+                       else if (ios->timing == MMC_TIMING_UHS_SDR25)
+                               ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+                       else if (ios->timing == MMC_TIMING_UHS_SDR50)
+                               ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+                       else if (ios->timing == MMC_TIMING_UHS_SDR104)
+                               ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+                       else if (ios->timing == MMC_TIMING_UHS_DDR50)
+                               ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+                       sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+               }
 
                /* Re-enable SD Clock */
                clock = host->clock;
@@ -1673,6 +1734,37 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
        }
 
 out:
+       /*
+        * If this is the very first time we are here, we start the retuning
+        * timer. Since only during the first time, SDHCI_NEEDS_RETUNING
+        * flag won't be set, we check this condition before actually starting
+        * the timer.
+        */
+       if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count &&
+           (host->tuning_mode == SDHCI_TUNING_MODE_1)) {
+               mod_timer(&host->tuning_timer, jiffies +
+                       host->tuning_count * HZ);
+               /* Tuning mode 1 limits the maximum data length to 4MB */
+               mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size;
+       } else {
+               host->flags &= ~SDHCI_NEEDS_RETUNING;
+               /* Reload the new initial value for timer */
+               if (host->tuning_mode == SDHCI_TUNING_MODE_1)
+                       mod_timer(&host->tuning_timer, jiffies +
+                               host->tuning_count * HZ);
+       }
+
+       /*
+        * In case tuning fails, host controllers which support re-tuning can
+        * try tuning again at a later time, when the re-tuning timer expires.
+        * So for these controllers, we return 0. Since there might be other
+        * controllers who do not have this capability, we return error for
+        * them.
+        */
+       if (err && host->tuning_count &&
+           host->tuning_mode == SDHCI_TUNING_MODE_1)
+               err = 0;
+
        sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
        spin_unlock(&host->lock);
        enable_irq(host->irq);
@@ -1775,6 +1867,9 @@ static void sdhci_tasklet_finish(unsigned long param)
 
        del_timer(&host->timer);
 
+       if (host->version >= SDHCI_SPEC_300)
+               del_timer(&host->tuning_timer);
+
        mrq = host->mrq;
 
        /*
@@ -1848,6 +1943,20 @@ static void sdhci_timeout_timer(unsigned long data)
        spin_unlock_irqrestore(&host->lock, flags);
 }
 
+static void sdhci_tuning_timer(unsigned long data)
+{
+       struct sdhci_host *host;
+       unsigned long flags;
+
+       host = (struct sdhci_host *)data;
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       host->flags |= SDHCI_NEEDS_RETUNING;
+
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
 /*****************************************************************************\
  *                                                                           *
  * Interrupt handling                                                        *
@@ -2049,13 +2158,30 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
                mmc_hostname(host->mmc), intmask);
 
        if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+               u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+                             SDHCI_CARD_PRESENT;
+
+               /*
+                * There is a observation on i.mx esdhc.  INSERT bit will be
+                * immediately set again when it gets cleared, if a card is
+                * inserted.  We have to mask the irq to prevent interrupt
+                * storm which will freeze the system.  And the REMOVE gets
+                * the same situation.
+                *
+                * More testing are needed here to ensure it works for other
+                * platforms though.
+                */
+               sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
+                                               SDHCI_INT_CARD_REMOVE);
+               sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
+                                                 SDHCI_INT_CARD_INSERT);
+
                sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
-                       SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
+                            SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
+               intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
                tasklet_schedule(&host->card_tasklet);
        }
 
-       intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
-
        if (intmask & SDHCI_INT_CMD_MASK) {
                sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
                        SDHCI_INT_STATUS);
@@ -2122,6 +2248,14 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
 
        sdhci_disable_card_detection(host);
 
+       /* Disable tuning since we are suspending */
+       if (host->version >= SDHCI_SPEC_300 && host->tuning_count &&
+           host->tuning_mode == SDHCI_TUNING_MODE_1) {
+               host->flags &= ~SDHCI_NEEDS_RETUNING;
+               mod_timer(&host->tuning_timer, jiffies +
+                       host->tuning_count * HZ);
+       }
+
        ret = mmc_suspend_host(host->mmc);
        if (ret)
                return ret;
@@ -2163,6 +2297,11 @@ int sdhci_resume_host(struct sdhci_host *host)
        ret = mmc_resume_host(host->mmc);
        sdhci_enable_card_detection(host);
 
+       /* Set the re-tuning expiration flag */
+       if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
+           (host->tuning_mode == SDHCI_TUNING_MODE_1))
+               host->flags |= SDHCI_NEEDS_RETUNING;
+
        return ret;
 }
 
@@ -2370,7 +2509,25 @@ int sdhci_add_host(struct sdhci_host *host)
        } else
                mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
 
-       mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE;
+       if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
+               mmc->max_discard_to = (1 << 27) / (mmc->f_max / 1000);
+       else
+               mmc->max_discard_to = (1 << 27) / host->timeout_clk;
+
+       mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
+
+       if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
+               host->flags |= SDHCI_AUTO_CMD12;
+
+       /* Auto-CMD23 stuff only works in ADMA or PIO. */
+       if ((host->version >= SDHCI_SPEC_300) &&
+           ((host->flags & SDHCI_USE_ADMA) ||
+            !(host->flags & SDHCI_USE_SDMA))) {
+               host->flags |= SDHCI_AUTO_CMD23;
+               DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc));
+       } else {
+               DBG("%s: Auto-CMD23 unavailable\n", mmc_hostname(mmc));
+       }
 
        /*
         * A controller may support 8-bit width, but the board itself
@@ -2414,6 +2571,21 @@ int sdhci_add_host(struct sdhci_host *host)
        if (caps[1] & SDHCI_DRIVER_TYPE_D)
                mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
 
+       /* Initial value for re-tuning timer count */
+       host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
+                             SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+
+       /*
+        * In case Re-tuning Timer is not disabled, the actual value of
+        * re-tuning timer will be 2 ^ (n - 1).
+        */
+       if (host->tuning_count)
+               host->tuning_count = 1 << (host->tuning_count - 1);
+
+       /* Re-tuning mode supported by the Host Controller */
+       host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >>
+                            SDHCI_RETUNING_MODE_SHIFT;
+
        ocr_avail = 0;
        /*
         * According to SD Host Controller spec v3.00, if the Host System
@@ -2559,9 +2731,15 @@ int sdhci_add_host(struct sdhci_host *host)
 
        setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
 
-       if (host->version >= SDHCI_SPEC_300)
+       if (host->version >= SDHCI_SPEC_300) {
                init_waitqueue_head(&host->buf_ready_int);
 
+               /* Initialize re-tuning timer */
+               init_timer(&host->tuning_timer);
+               host->tuning_timer.data = (unsigned long)host;
+               host->tuning_timer.function = sdhci_tuning_timer;
+       }
+
        ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
                mmc_hostname(mmc), host);
        if (ret)
@@ -2655,6 +2833,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
        free_irq(host->irq, host);
 
        del_timer_sync(&host->timer);
+       if (host->version >= SDHCI_SPEC_300)
+               del_timer_sync(&host->tuning_timer);
 
        tasklet_kill(&host->card_tasklet);
        tasklet_kill(&host->finish_tasklet);