[PATCH] mmc: sdhci: add support for re-tuning mode 2

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hosts which support re-tuning mode 2 has the capability to indicate
the re-tuning timing by issuing re-tuning request during data transfers.
During non-data transfers, re-tuning timing is determined by either
re-tuning timer or re-tuning request.

Since there is no way to determine the host's capability to generate
re-tuning request during non-data transfers, we will start the re-tuning
timer by default for re-tuning mode 2. If we ever received a re-tuning
request during non-data transfers, that means the host is also capable
of generating re-tuning request for non-data transfers, we will then
deactivate the re-tuning timer altogether.

The SDHCI_RETUNING_TIMER flag is added to indicate the fact that the
host requires re-tuning timer to trigger the re-tuning timing.

The SDHCI_NEEDS_RETUNING, SDHCI_RETUNING_TIMER flags and the max block
count of the host will be restored to their default values since they
are affected by different cards inserted too.

Signed-off-by: Aaron Lu <aaron.lu@xxxxxxx>
---
 drivers/mmc/host/sdhci.c  |  128 +++++++++++++++++++++++++++++++++------------
 drivers/mmc/host/sdhci.h  |    3 +
 include/linux/mmc/sdhci.h |    4 ++
 3 files changed, 102 insertions(+), 33 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0e02cc1..f620c72 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -222,6 +222,22 @@ static void sdhci_reinit(struct sdhci_host *host)
 {
 	sdhci_init(host, 0);
 	sdhci_enable_card_detection(host);
+
+	if (host->version >= SDHCI_SPEC_300) {
+		/*
+		 * Clear re-tuning related flags, since these flags
+		 * are also affected by different cards inserted
+		 */
+		host->flags &= ~(SDHCI_NEEDS_RETUNING |
+				SDHCI_RETUNING_TIMER);
+
+		/*
+		 * restore max block count, since it might be
+		 * reduced due to re-tuning mode 1 and 2
+		 */
+		host->mmc->max_blk_count =
+			(host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
+	}
 }
 
 static void sdhci_activate_led(struct sdhci_host *host)
@@ -1245,7 +1261,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
 		present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
 		/*
-		 * Check if the re-tuning timer has already expired and there
+		 * Check if the re-tuning timing has already arrived and there
 		 * is no on-going data transfer. If so, we need to execute
 		 * tuning procedure before sending command.
 		 */
@@ -1733,34 +1749,41 @@ 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 this is the very first time we are here, we setup the
+	 * corresponding re-tuning condition. Since only during the
+	 * first time, SDHCI_NEEDS_RETUNING flag won't be set.
 	 */
-	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;
+	if (!(host->flags & SDHCI_NEEDS_RETUNING)) {
+		if (host->tuning_count &&
+			host->tuning_mode != SDHCI_TUNING_MODE_RSV) {
+			host->flags |= SDHCI_RETUNING_TIMER;
+			mod_timer(&host->tuning_timer, jiffies +
+					host->tuning_count * HZ);
+		}
+		/* Enable re-tuning event for tuning mode 2 */
+		if (host->tuning_mode == SDHCI_TUNING_MODE_2)
+			ier |= SDHCI_INT_RETUNING;
+		/* Tuning mode 1 and 2 limits the maximum data length to 4MB */
+		if (host->tuning_mode == SDHCI_TUNING_MODE_1 ||
+			host->tuning_mode == SDHCI_TUNING_MODE_2)
+			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)
+		/* Reload the new initial value for re-tuning timer */
+		if (host->flags & SDHCI_RETUNING_TIMER)
 			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.
+	 * try tuning again at a later time, when the re-tuning timing arrives.
 	 * 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)
+	if (err && (host->tuning_mode != SDHCI_TUNING_MODE_RSV))
 		err = 0;
 
 	sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
@@ -2050,6 +2073,18 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 		}
 	}
 
+	/*
+	 * Reload re-tuning timer if transfer complete
+	 * occured for re-tuning mode 2
+	 */
+	if ((host->flags & SDHCI_RETUNING_TIMER) &&
+		(host->tuning_mode == SDHCI_TUNING_MODE_2) &&
+		(intmask & SDHCI_INT_DATA_END)) {
+		mod_timer(&host->tuning_timer, jiffies +
+				host->tuning_count * HZ);
+		host->flags &= ~SDHCI_NEEDS_RETUNING;
+	}
+
 	if (!host->data) {
 		/*
 		 * The "data complete" interrupt is also used to
@@ -2206,6 +2241,23 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 
 	intmask &= ~SDHCI_INT_CARD_INT;
 
+	if (intmask & SDHCI_INT_RETUNING) {
+		host->flags |= SDHCI_NEEDS_RETUNING;
+		if ((host->flags & SDHCI_RETUNING_TIMER) &&
+			(host->tuning_mode == SDHCI_TUNING_MODE_2)) {
+			u32 state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+			/*
+			 * If host is capable of generating re-tuning
+			 * request during non-data transfers, there is
+			 * no need to use re-tuning timer
+			 */
+			if (!(state & SDHCI_DATA_ACTIVE))
+				host->flags &= ~SDHCI_RETUNING_TIMER;
+		}
+	}
+
+	intmask &= ~SDHCI_INT_RETUNING;
+
 	if (intmask) {
 		printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
 			mmc_hostname(host->mmc), intmask);
@@ -2243,12 +2295,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) {
+	/* Disable re-tuning since we are suspending */
+	if ((host->version >= SDHCI_SPEC_300) &&
+		(host->tuning_mode != SDHCI_TUNING_MODE_RSV)) {
 		host->flags &= ~SDHCI_NEEDS_RETUNING;
-		mod_timer(&host->tuning_timer, jiffies +
-			host->tuning_count * HZ);
+		if (host->flags & SDHCI_RETUNING_TIMER)
+			del_timer_sync(&host->tuning_timer);
+		if (host->tuning_mode == SDHCI_TUNING_MODE_2)
+			sdhci_mask_irqs(host, SDHCI_INT_RETUNING);
 	}
 
 	ret = mmc_suspend_host(host->mmc);
@@ -2292,11 +2346,6 @@ 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;
 }
 
@@ -2570,12 +2619,22 @@ int sdhci_add_host(struct sdhci_host *host)
 	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);
+	if (host->tuning_count) {
+		/*
+		 * In case re-tuning timer is not disabled,
+		 * the actual value of re-tuning timer will be:
+		 * 0x1 - 0xb: 2 ^ (n - 1)
+		 * 0xc - 0xe: reserved
+		 * 0xf: get the value from other source
+		 */
+		if (host->tuning_count <= 0xb)
+			host->tuning_count = 1 << (host->tuning_count - 1);
+		else if (host->tuning_count == 0xf)
+			host->tuning_count = host->ops->get_tuning_count ?
+				host->ops->get_tuning_count(host) : 0;
+		else
+			host->tuning_count = 0;
+	}
 
 	/* Re-tuning mode supported by the Host Controller */
 	host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >>
@@ -2816,6 +2875,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
 
 	sdhci_disable_card_detection(host);
 
+	if (host->tuning_mode == SDHCI_TUNING_MODE_2)
+		sdhci_mask_irqs(host, SDHCI_INT_RETUNING);
+
 	mmc_remove_host(host->mmc);
 
 #ifdef SDHCI_USE_LEDS_CLASS
@@ -2828,7 +2890,7 @@ 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)
+	if (host->flags & SDHCI_RETUNING_TIMER)
 		del_timer_sync(&host->tuning_timer);
 
 	tasklet_kill(&host->card_tasklet);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 745c42f..ef4a0a3 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -64,6 +64,7 @@
 #define SDHCI_PRESENT_STATE	0x24
 #define  SDHCI_CMD_INHIBIT	0x00000001
 #define  SDHCI_DATA_INHIBIT	0x00000002
+#define  SDHCI_DATA_ACTIVE	0x00000004
 #define  SDHCI_DOING_WRITE	0x00000100
 #define  SDHCI_DOING_READ	0x00000200
 #define  SDHCI_SPACE_AVAILABLE	0x00000400
@@ -126,6 +127,7 @@
 #define  SDHCI_INT_CARD_INSERT	0x00000040
 #define  SDHCI_INT_CARD_REMOVE	0x00000080
 #define  SDHCI_INT_CARD_INT	0x00000100
+#define  SDHCI_INT_RETUNING	0x00001000
 #define  SDHCI_INT_ERROR	0x00008000
 #define  SDHCI_INT_TIMEOUT	0x00010000
 #define  SDHCI_INT_CRC		0x00020000
@@ -273,6 +275,7 @@ struct sdhci_ops {
 	void	(*platform_reset_enter)(struct sdhci_host *host, u8 mask);
 	void	(*platform_reset_exit)(struct sdhci_host *host, u8 mask);
 	int	(*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
+	int	(*get_tuning_count)(struct sdhci_host *host);
 
 };
 
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 5666f3a..938a4f5 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -115,6 +115,7 @@ struct sdhci_host {
 #define SDHCI_NEEDS_RETUNING	(1<<5)	/* Host needs retuning */
 #define SDHCI_AUTO_CMD12	(1<<6)	/* Auto CMD12 support */
 #define SDHCI_AUTO_CMD23	(1<<7)	/* Auto CMD23 support */
+#define SDHCI_RETUNING_TIMER	(1<<8)	/* Host uses re-tuning timer */
 
 	unsigned int version;	/* SDHCI spec. version */
 
@@ -158,6 +159,9 @@ struct sdhci_host {
 	unsigned int		tuning_count;	/* Timer count for re-tuning */
 	unsigned int		tuning_mode;	/* Re-tuning mode supported by host */
 #define SDHCI_TUNING_MODE_1	0
+#define SDHCI_TUNING_MODE_2	1
+#define SDHCI_TUNING_MODE_3	2
+#define SDHCI_TUNING_MODE_RSV	3
 	struct timer_list	tuning_timer;	/* Timer for tuning */
 
 	unsigned long private[0] ____cacheline_aligned;
-- 
1.7.1


--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux