mmc: sdhci: Allow for finishing multiple requests
authorAdrian Hunter <adrian.hunter@intel.com>
Wed, 29 Jun 2016 13:24:34 +0000 (16:24 +0300)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 25 Jul 2016 08:34:43 +0000 (10:34 +0200)
In order to support commands during data transfer, there will have to be up
to two active requests (mrqs) at a time, instead of just one. That means
recording which request is finished.  Doing that obsoletes host->mrq which
is therefore removed.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h

index 9580f76caf57d2bfffb87c35b1a346da0d857a7b..e37d0e91ecb5999b62950b23ff140a010463f5d0 100644 (file)
@@ -938,6 +938,29 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
                 (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
 }
 
+static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
+{
+       int i;
+
+       for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+               if (host->mrqs_done[i] == mrq) {
+                       WARN_ON(1);
+                       return;
+               }
+       }
+
+       for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+               if (!host->mrqs_done[i]) {
+                       host->mrqs_done[i] = mrq;
+                       break;
+               }
+       }
+
+       WARN_ON(i >= SDHCI_MAX_MRQS);
+
+       tasklet_schedule(&host->finish_tasklet);
+}
+
 static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
 {
        if (host->cmd && host->cmd->mrq == mrq)
@@ -952,7 +975,7 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
        if (sdhci_needs_reset(host, mrq))
                host->pending_reset = true;
 
-       tasklet_schedule(&host->finish_tasklet);
+       __sdhci_finish_mrq(host, mrq);
 }
 
 static void sdhci_finish_data(struct sdhci_host *host)
@@ -1440,8 +1463,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        spin_lock_irqsave(&host->lock, flags);
 
-       WARN_ON(host->mrq != NULL);
-
        sdhci_led_activate(host);
 
        /*
@@ -1455,8 +1476,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                }
        }
 
-       host->mrq = mrq;
-
        if (!present || host->flags & SDHCI_DEVICE_DEAD) {
                mrq->cmd->error = -ENOMEDIUM;
                sdhci_finish_mrq(host, mrq);
@@ -1993,13 +2012,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
                cmd.retries = 0;
                cmd.data = NULL;
+               cmd.mrq = &mrq;
                cmd.error = 0;
 
                if (tuning_loop_counter-- == 0)
                        break;
 
                mrq.cmd = &cmd;
-               host->mrq = &mrq;
 
                /*
                 * In response to CMD19, the card sends 64 bytes of tuning
@@ -2029,7 +2048,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                sdhci_send_command(host, &cmd);
 
                host->cmd = NULL;
-               host->mrq = NULL;
 
                spin_unlock_irqrestore(&host->lock, flags);
                /* Wait for Buffer Read Ready interrupt */
@@ -2230,26 +2248,26 @@ static const struct mmc_host_ops sdhci_ops = {
  *                                                                           *
 \*****************************************************************************/
 
-static void sdhci_tasklet_finish(unsigned long param)
+static bool sdhci_request_done(struct sdhci_host *host)
 {
-       struct sdhci_host *host;
        unsigned long flags;
        struct mmc_request *mrq;
-
-       host = (struct sdhci_host*)param;
+       int i;
 
        spin_lock_irqsave(&host->lock, flags);
 
-        /*
-         * If this tasklet gets rescheduled while running, it will
-         * be run again afterwards but without any active request.
-         */
-       if (!host->mrq) {
-               spin_unlock_irqrestore(&host->lock, flags);
-               return;
+       for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+               mrq = host->mrqs_done[i];
+               if (mrq) {
+                       host->mrqs_done[i] = NULL;
+                       break;
+               }
        }
 
-       mrq = host->mrq;
+       if (!mrq) {
+               spin_unlock_irqrestore(&host->lock, flags);
+               return true;
+       }
 
        sdhci_del_timer(host, mrq);
 
@@ -2287,14 +2305,23 @@ static void sdhci_tasklet_finish(unsigned long param)
                host->pending_reset = false;
        }
 
-       host->mrq = NULL;
-
-       sdhci_led_deactivate(host);
+       if (!sdhci_has_requests(host))
+               sdhci_led_deactivate(host);
 
        mmiowb();
        spin_unlock_irqrestore(&host->lock, flags);
 
        mmc_request_done(host->mmc, mrq);
+
+       return false;
+}
+
+static void sdhci_tasklet_finish(unsigned long param)
+{
+       struct sdhci_host *host = (struct sdhci_host *)param;
+
+       while (!sdhci_request_done(host))
+               ;
 }
 
 static void sdhci_timeout_timer(unsigned long data)
index a1de4223243956af9df3f55f4eacde3fd2dc28ce..1f0413b9007f430f815c2ae799e5f9a7d18dfb08 100644 (file)
@@ -314,6 +314,9 @@ struct sdhci_adma2_64_desc {
  */
 #define SDHCI_MAX_SEGS         128
 
+/* Allow for a a command request and a data request at the same time */
+#define SDHCI_MAX_MRQS         2
+
 enum sdhci_cookie {
        COOKIE_UNMAPPED,
        COOKIE_PRE_MAPPED,      /* mapped by sdhci_pre_req() */
@@ -465,7 +468,7 @@ struct sdhci_host {
        bool preset_enabled;    /* Preset is enabled */
        bool pending_reset;     /* Cmd/data reset is pending */
 
-       struct mmc_request *mrq;        /* Current request */
+       struct mmc_request *mrqs_done[SDHCI_MAX_MRQS];  /* Requests done */
        struct mmc_command *cmd;        /* Current command */
        struct mmc_command *data_cmd;   /* Current data command */
        struct mmc_data *data;  /* Current data request */