TX2 SD card driver bug: SD does not enumerate in UHS mode

SD cards don’t seem to consistently mount in the correct mode on the TX2. They typically mount with a timing spec of “sd high-speed”, instead of the expected “sd uhs SDR104” spec. This data was obtained by reading the /sys/kernel/debug/mmc2/ios file in linux.

Without this, we cannot achieve the desired write speeds to an SD card that we are looking for.

We discovered this problem on our custom carrier board, but we are also able to consistently reproduce this on the Jetson devkits. The TX2 on the Jetson devkit was flashed with NVIDIA’s R28.1 factory image. No hardware or software customizations were performed.

I noticed that there is a factory image available for the TX1, so we ran some tests between the R24.2 and R28.1 releases. This was to identify if the problem was the hardware (TX2 vs TX1) or software (R24.2 vs 28.1) release.

The problem was reproduced in the 28.1 release. It was not reproduced on the 24.2 release. It looks like the problem is a change in drivers made between the R24.2 and R28.1 releases.

On the TX2, we have also found that when cards are correctly mounted as UHS, a temperature change from 25C to 60C will cause some driver errors:

mmc2 sdhci_data_irq 2776 SDHCI_INT_DATA_TIMEOUT
mmc2 sdhci_data_irq 2788 SDHCI_INT_DATA_CRC

For the SD cards we were are targeting, this resulted in a sharp decrease in the write performance: from 50 Mbps to 12 Mbps on average.

My questions:

  1. What is the suggested fix for this problem?

  2. Can you comment on what SD/MMC driver changes were made between the 24.2 or 28.1 releases?

Hi robb_n,

What do you mean “On the TX2, we have also found that when cards are correctly mounted as UHS, a temperature change from 25C to 60C”? Didn’t you say you cannot mount SD in UHS mode?

Hi WayneWWW,

On the TX2, the cards do not consistently mount in UHS mode (if they mount at all). I think the probability of mounting in UHS appears to be about 33%. The same behaviour was observed on the TX1 (R28.1).

On the TX1 (R24.2) there are no problems with the same set of SD cards.

robb_n,

Are you using U1 or U3 card? Please tell which cards you are using.

Hi WayneWWW,

We have tried both U1 and U3 cards. Here are two card models we are hoping to use with the TX2:

We have been using these cards in other non-TX2 systems for some time.

robb_n,

I also tried Kingston card as your U1 one and another card from Sandisk. Sandisk card is able to be detected as uhs SDR104. However, Kingston one seems not. I am looking into the driver and may take some time to find cause.

BTW, it looks impossible to mount my Kingston card in uhs mode. According to your previous comment, it sounds like there is probability, right? Does that happen to both cards?

(duplicate comment, please ignore)

Hi WayneWWW,

Yes, there is some probability that the Kingston card will correctly mount as UHS. It happens for both cards, but with different probabilities.

The Kingston SDA10 U1 cards mounted in UHS around 20% to 33% of the time. I have found that different cards of the same model show different probabilities. There could be some variance in the manufacturing of the card that causes the problem, but that doesn’t explain why it works reliably on the TX1 (R24.1).

The Kingston SDA3 U3 mounts in UHS more often, but I did not perform any extensive testing to find the exact probability.

Sorry for late reply.

Please try this patch to kernel.

From f88d3495c4461fe5742e1d0c43516878932e5012 Mon Sep 17 00:00:00 2001
Date: Thu, 29 Mar 2018 13:46:47 +0530
Subject: [PATCH] drivers: mmc: Fix voltage switch sequence

-Enable SLCG based on prods only after voltage switch sequence
is completed.
-Change to modify only card clock in mmc_set_ios during voltage switch.
-Add nvquirk to make this change chip agnostic as
different register sets identify SLCG on different chips.



---

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ee145d4..119cf32 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -698,6 +698,9 @@
 	int retries = 10;
 	u32 pocr = ocr;
 
+	if (host->ops->voltage_switch_req)
+		host->ops->voltage_switch_req(host, false);
+
 try_again:
 	if (!retries) {
 		ocr &= ~SD_OCR_S18R;
@@ -763,6 +766,8 @@
 		err = mmc_send_cid(host, cid);
 	else
 		err = mmc_all_send_cid(host, cid);
+	if (host->ops->voltage_switch_req)
+		host->ops->voltage_switch_req(host, true);
 
 	return err;
 }
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 2f327a9..0494f0a 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -63,6 +63,11 @@
 #define SDHCI_VNDR_CLK_CTRL_TRIM_VALUE_MASK		0x1F
 #define SDHCI_VNDR_CLK_CTRL_SDMMC_CLK			0x1
 
+/* Tegra SDHOST controller vendor register definitions */
+#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL			0x100
+#define SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE		BIT(6)
+#define SDHCI_CLOCK_CTRL_SDMMC_CLK			BIT(0)
+
 #define SDHCI_VNDR_SYS_SW_CTRL				0x104
 #define SDHCI_VNDR_SYS_SW_CTRL_STROBE_SHIFT		31
 
@@ -101,6 +106,9 @@
 #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300	0x20
 #define SDHCI_MISC_CTRL_ENABLE_DDR50		0x200
 
+#define SDHCI_TEGRA_VENDOR_MISC_CTRL_2		0x128
+#define SDHCI_MISC_CTRL_2_CLK_OVR_ON		0x40000000
+
 #define SDMMC_AUTO_CAL_STATUS	0x1EC
 #define SDMMC_AUTO_CAL_STATUS_AUTO_CAL_ACTIVE	0x80000000
 
@@ -116,6 +124,8 @@
 #define NVQUIRK_DISABLE_AUTO_CALIBRATION	BIT(6)
 #define NVQUIRK_UPDATE_PIN_CNTRL_REG		BIT(7)
 #define NVQUIRK_BROKEN_RTPM_FORBID		BIT(8)
+/* Quirk to identify SLCG registers */
+#define NVQUIRK_SDMMC_CLK_OVERRIDE	BIT(9)
 #define NVQUIRK2_SET_PLL_CLK_PARENT		BIT(0)
 /* Tegra register write WAR - needs follow on register read */
 #define NVQUIRK2_TEGRA_WRITE_REG		BIT(1)
@@ -239,6 +249,7 @@
 	struct pinctrl_state *schmitt_disable[2];
 	struct pinctrl_state *drv_code_strength;
 	struct pinctrl_state *default_drv_code_strength;
+	bool slcg_status;
 };
 
 static void tegra_sdhci_do_calibration(struct sdhci_host *sdhci,
@@ -521,7 +532,7 @@
 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 	const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
-	u32 misc_ctrl;
+	u32 misc_ctrl, misc_ctrl_2, clk_ctrl;
 	int err;
 
 	sdhci_reset(host, mask);
@@ -541,6 +552,7 @@
 		sdhci_tegra_set_tap_delay(host, plat->tap_delay,
 				SET_DEFAULT_TAP);
 
+	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
 	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
 	/* Erratum: Enable SDHCI spec v3.00 support */
 	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
@@ -553,6 +565,14 @@
 	if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104)
 		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
 	sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
+
+	if (soc_data->nvquirks & NVQUIRK_SDMMC_CLK_OVERRIDE) {
+		misc_ctrl_2 = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+		tegra_host->slcg_status = !(misc_ctrl_2 &
+						SDHCI_MISC_CTRL_2_CLK_OVR_ON);
+	} else
+		tegra_host->slcg_status = !(clk_ctrl &
+					SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE);
 
 	/* SEL_VREG should be 0 for all modes*/
 	vendor_trim_clear_sel_vreg(host, true);
@@ -1358,6 +1378,49 @@
 	return;
 }
 
+/* Configure voltage switch specific requirements */
+static void tegra_sdhci_voltage_switch_req(struct sdhci_host *host, bool req)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_tegra *tegra_host = pltfm_host->priv;
+	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+	u32 clk_ctrl;
+
+	if (!req) {
+		/* Disable SLCG */
+		clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+		clk_ctrl = clk_ctrl | SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE;
+		sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+
+		if (soc_data->nvquirks & NVQUIRK_SDMMC_CLK_OVERRIDE) {
+			clk_ctrl = sdhci_readl(host,
+						SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+			clk_ctrl = clk_ctrl | SDHCI_MISC_CTRL_2_CLK_OVR_ON;
+			sdhci_writel(host, clk_ctrl,
+						SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+		}
+	} else  {
+		/* Restore SLCG */
+		if (tegra_host->slcg_status) {
+			clk_ctrl = sdhci_readl(host,
+						SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+			clk_ctrl = clk_ctrl &
+					~SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE;
+			sdhci_writel(host, clk_ctrl,
+						SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+			if (soc_data->nvquirks &
+					NVQUIRK_SDMMC_CLK_OVERRIDE) {
+				clk_ctrl = sdhci_readl(host,
+						SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+				clk_ctrl = clk_ctrl &
+						~SDHCI_MISC_CTRL_2_CLK_OVR_ON;
+				sdhci_writel(host, clk_ctrl,
+						SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+			}
+		}
+	}
+
+}
 static int tegra_sdhci_suspend(struct sdhci_host *sdhci)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
@@ -1829,6 +1892,7 @@
 	.do_calibration	= tegra_sdhci_do_calibration,
 	.config_strobe	= tegra_sdhci_config_strobe,
 	.select_drive_strength	= tegra_sdhci_get_drive_strength,
+	.voltage_switch_req	= tegra_sdhci_voltage_switch_req,
 };
 
 static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
@@ -1910,11 +1974,13 @@
 
 static struct sdhci_tegra_soc_data soc_data_tegra186 = {
 	.pdata = &sdhci_tegra186_pdata,
+	.nvquirks = NVQUIRK_SDMMC_CLK_OVERRIDE,
 	.nvquirks2 = NVQUIRK2_SET_PLL_CLK_PARENT,
 };
 
 static struct sdhci_tegra_soc_data soc_data_tegra194 = {
 	.pdata = &sdhci_tegra194_pdata,
+	.nvquirks = NVQUIRK_SDMMC_CLK_OVERRIDE,
 };
 
 static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ff3aaf4..b6489a5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1712,6 +1712,8 @@
 			host->mmc->max_busy_timeout /= host->timeout_clk;
 		}
 	}
+	if (mmc->skip_host_clkgate)
+		goto unlock;
 
 	sdhci_set_power(host, ios->power_mode, ios->vdd);
 
@@ -1821,7 +1823,7 @@
 	 */
 	if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
 		sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
-
+unlock:
 	mmiowb();
 	spin_unlock_irqrestore(&host->lock, flags);
 
@@ -2488,6 +2490,15 @@
 		host->ops->pre_regulator_config(host, vdd);
 }
 
+static void sdhci_voltage_switch_req(struct mmc_host *mmc, bool req)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+
+	if (host->ops->voltage_switch_req)
+		host->ops->voltage_switch_req(host, req);
+
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.post_req	= sdhci_post_req,
@@ -2509,6 +2520,7 @@
 	.discard_cqe_task	= sdhci_cqe_task_discard_rq,
 	.enable_host_int	= sdhci_enable_host_interrupts,
 	.pre_regulator_config	= sdhci_regulator_config_pre,
+	.voltage_switch_req		= sdhci_voltage_switch_req,
 };
 
 /*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 9b01df2..ee3f2f8 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -611,6 +611,7 @@
 	void	(*do_calibration)(struct sdhci_host *sdhci,
 			unsigned char signal_voltage);
 	void	(*config_strobe)(struct sdhci_host *host, bool enable);
+	void	(*voltage_switch_req)(struct sdhci_host *sdhci, bool req);
 };
 
 #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f7df93c..da057ddb 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -171,6 +171,7 @@
 	void	(*enable_host_int)(struct mmc_host *host, bool enable);
 	void	(*pre_regulator_config)(struct mmc_host *host, int vdd);
 	void	(*config_strobe)(struct mmc_host *host, bool enable);
+	void	(*voltage_switch_req)(struct mmc_host *host, bool req);
 };
 
 struct mmc_card;

Update full patch for this issue.
0002-mmc-tegra-Disable-device-clock-instead-skipping-clkg.txt (1.66 KB)
0001-drivers-mmc-Fix-voltage-switch-sequence.txt (9.51 KB)

Hi,

I want to reactivate this - I have the same issue on R32 (4.9.140-tegra). Using a 200GB SanDisk SDXC UHS-I card.

If I insert the card before boot, I consistently get HS mode (~20MB/s read max).
When I unplug and re-insert the SD card after boot, it mounts in UHS mode (~85MB/s read max).
When I then unbind/rebind the SD platform driver, it goes back to HS mode.

I have seen the kernel patch but this is several releases later so I’m not sure how to approach this…?

EDIT: I have observed this issue with a range of SD cards on the Orbitty carrier, both with R32 and R28. It does NOT happen on the Dev Kit R28 using any of the microSD cards with the SD adapter.

output below

/// checking mode shortly after system startup; the SD card was inserted BEFORE boot

spx@bolt-s-000 ~> sudo cat  /sys/kernel/debug/mmc2/ios

clock:		50000000 Hz
vdd:		21 (3.3 ~ 3.4 V)
bus mode:	2 (push-pull)
chip select:	0 (don't care)
power mode:	2 (on)
bus width:	2 (4 bits)
timing spec:	2 (sd high-speed)
signal voltage:	0 (3.30 V)
driver type:	0 (driver type B)

/// physically removing and re-inserting here

spx@bolt-s-000 ~> sudo cat  /sys/kernel/debug/mmc2/ios

clock:		208000000 Hz
vdd:		21 (3.3 ~ 3.4 V)
bus mode:	2 (push-pull)
chip select:	0 (don't care)
power mode:	2 (on)
bus width:	2 (4 bits)
timing spec:	6 (sd uhs SDR104)
signal voltage:	1 (1.80 V)
driver type:	0 (driver type B)

/// resetting the driver

spx@bolt-s-000 ~> echo 3400000.sdhci | sudo tee /sys/bus/platform/drivers/sdhci-tegra/unbind
3400000.sdhci
spx@bolt-s-000 ~> echo 3400000.sdhci | sudo tee /sys/bus/platform/drivers/sdhci-tegra/bind
3400000.sdhci

/// now we're back to lower speeds

spx@bolt-s-000 ~> sudo cat  /sys/kernel/debug/mmc2/ios

clock:		50000000 Hz
vdd:		21 (3.3 ~ 3.4 V)
bus mode:	2 (push-pull)
chip select:	0 (don't care)
power mode:	2 (on)
bus width:	2 (4 bits)
timing spec:	2 (sd high-speed)
signal voltage:	0 (3.30 V)
driver type:	0 (driver type B)

I also tried applying the kernel patch posted above to a R28 system (on an Orbitty). It did not make any difference.

Does this issue only happen to Orbitty carrier board?

Yes. After many more attempts I have observed a very low chance (5% or so) that it does come up correctly on the Orbitty. I have tried many times with the devkit but there it always seems to come up correctly.

I’ve gotten in touch with Connecttech support, but thery have not encountered the issue.

If there was a way to cause a “disconnect” of the SD card via software somehow that could be a workaround (as I mentioned the unbind/bind which I found has the opposite effect - maybe there’s something else that can tigger an interruption of the contact of the sd card?)…

Hi all,

I plan to use the TX2 Jetson and J120 to record 4K@30fps video with a MICROSDXC Card. The type of MicroSRXC card which I use is RP-SMTE64DA1 (specification: +SD speed class:Class10; UHS speed grade U3, Read performance up to 95MB/s and Write performance upto 90MB/s, as link https://media.digikey.com/pdf/Data%20Sheets/Panasonic%20Semiconductors%20ICs%20PDFs/TE_Series_DS.pdf). but now I don’t have a 4K camera to check recording by micro SDcard.
But, I’m quite confused, so I want to ask you: with that micro SDcard and TX2, J120, Can I recording 4K@30fps video?

Thank you so much!