All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V3 0/7] Preparations to support SD UHS-II cards
@ 2022-02-22  3:39 Jason Lai
  2022-02-22  3:39 ` [PATCH V3 1/7] mmc: core: Cleanup printing of speed mode at card insertion Jason Lai
                   ` (7 more replies)
  0 siblings, 8 replies; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Jason Lai <jason.lai@genesyslogic.com.tw>

Series [1] that has been posted by Ulf Hansson which provided some guidance
and an overall structure.

Series [2] focused on UHS-II card control side to address Ulf's intention
regarding to "modularising" sd_uhs2.c.

Series [3] is based on series [2] and adopt most of Ulf's comments.

This series is the successor version of post [3], which is base on Ulf's "next" branch 2022/02/14):
1. Modify settings in uhs2_config_write().
2. Fix some compilation errors.
3. Fix some warnings and errors when executing checkpatch.pl.

Kind regards
Jason Lai

[1]
https://patchwork.kernel.org/project/linux-mmc/list/?series=438509

[2]
https://patchwork.kernel.org/project/linux-mmc/list/?series=589827

[3]
https://patchwork.kernel.org/project/linux-mmc/list/?series=606241

Jason Lai (3):
  mmc: add UHS-II related definitions in headers
  mmc: Implement content of UHS-II card initialization functions
  mmc: core: Support UHS-II card access

Ulf Hansson (4):
  mmc: core: Cleanup printing of speed mode at card insertion
  mmc: core: Prepare to support SD UHS-II cards
  mmc: core: Announce successful insertion of an SD UHS-II card
  mmc: core: Extend support for mmc regulators with a vqmmc2

 drivers/mmc/core/Makefile    |    2 +-
 drivers/mmc/core/bus.c       |   38 +-
 drivers/mmc/core/core.c      |   43 +-
 drivers/mmc/core/core.h      |    1 +
 drivers/mmc/core/host.h      |    4 +
 drivers/mmc/core/regulator.c |   34 ++
 drivers/mmc/core/sd_uhs2.c   | 1088 ++++++++++++++++++++++++++++++++++
 drivers/mmc/core/sd_uhs2.h   |   16 +
 include/linux/mmc/card.h     |   35 ++
 include/linux/mmc/core.h     |    6 +
 include/linux/mmc/host.h     |   69 +++
 include/linux/mmc/sd_uhs2.h  |  198 +++++++
 12 files changed, 1514 insertions(+), 20 deletions(-)
 create mode 100644 drivers/mmc/core/sd_uhs2.c
 create mode 100644 drivers/mmc/core/sd_uhs2.h
 create mode 100644 include/linux/mmc/sd_uhs2.h

-- 
2.35.1


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH V3 1/7] mmc: core: Cleanup printing of speed mode at card insertion
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
@ 2022-02-22  3:39 ` Jason Lai
  2022-02-22  3:39 ` [PATCH V3 2/7] mmc: core: Prepare to support SD UHS-II cards Jason Lai
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Ulf Hansson <ulf.hansson@linaro.org>

The current print of the bus speed mode in mmc_add_card() has grown over
the years and is now difficult to parse. Let's clean up the code and also
take the opportunity to properly announce "DDR" for eMMCs as
"high speed DDR", which is according to the eMMC spec.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/mmc/core/bus.c | 36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 58a60afa650b..eefe74bf1356 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -299,6 +299,7 @@ int mmc_add_card(struct mmc_card *card)
 {
 	int ret;
 	const char *type;
+	const char *speed_mode = "";
 	const char *uhs_bus_speed_mode = "";
 	static const char *const uhs_speeds[] = {
 		[UHS_SDR12_BUS_SPEED] = "SDR12 ",
@@ -337,27 +338,30 @@ int mmc_add_card(struct mmc_card *card)
 		break;
 	}
 
+	if (mmc_card_hs(card))
+		speed_mode = "high speed ";
+	else if (mmc_card_uhs(card))
+		speed_mode = "ultra high speed ";
+	else if	(mmc_card_ddr52(card))
+		speed_mode = "high speed DDR ";
+	else if (mmc_card_hs200(card))
+		speed_mode = "HS200 ";
+	else if (mmc_card_hs400es(card))
+		speed_mode = "HS400 Enhanced strobe ";
+	else if (mmc_card_hs400(card))
+		speed_mode = "HS400 ";
+
 	if (mmc_card_uhs(card) &&
 		(card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
 		uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
 
-	if (mmc_host_is_spi(card->host)) {
-		pr_info("%s: new %s%s%s card on SPI\n",
-			mmc_hostname(card->host),
-			mmc_card_hs(card) ? "high speed " : "",
-			mmc_card_ddr52(card) ? "DDR " : "",
-			type);
-	} else {
-		pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
-			mmc_hostname(card->host),
-			mmc_card_uhs(card) ? "ultra high speed " :
-			(mmc_card_hs(card) ? "high speed " : ""),
-			mmc_card_hs400(card) ? "HS400 " :
-			(mmc_card_hs200(card) ? "HS200 " : ""),
-			mmc_card_hs400es(card) ? "Enhanced strobe " : "",
-			mmc_card_ddr52(card) ? "DDR " : "",
+	if (mmc_host_is_spi(card->host))
+		pr_info("%s: new %s%s card on SPI\n",
+			mmc_hostname(card->host), speed_mode, type);
+	else
+		pr_info("%s: new %s%s%s card at address %04x\n",
+			mmc_hostname(card->host), speed_mode,
 			uhs_bus_speed_mode, type, card->rca);
-	}
 
 #ifdef CONFIG_DEBUG_FS
 	mmc_add_card_debugfs(card);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH V3 2/7] mmc: core: Prepare to support SD UHS-II cards
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
  2022-02-22  3:39 ` [PATCH V3 1/7] mmc: core: Cleanup printing of speed mode at card insertion Jason Lai
@ 2022-02-22  3:39 ` Jason Lai
  2022-02-22  3:39 ` [PATCH V3 3/7] mmc: core: Announce successful insertion of an SD UHS-II card Jason Lai
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Ulf Hansson <ulf.hansson@linaro.org>

The SD UHS-II interface was introduced to the SD spec v4.00 several years
ago. The interface is fundamentally different from an electrical and a
protocol point of view, comparing to the legacy SD interface.

However, the legacy SD protocol is supported through a specific transport
layer (SD-TRAN) defined in the UHS-II addendum of the spec. This allows the
SD card to be managed in a very similar way as a legacy SD card, hence a
lot of code can be re-used to support these new types of cards through the
mmc subsystem.

Moreover, an SD card that supports the UHS-II interface shall also be
backwards compatible with the legacy SD interface, which allows a UHS-II
card to be inserted into a legacy slot. As a matter of fact, this is
already supported by mmc subsystem as of today.

To prepare to add support for UHS-II, this change puts the basic foundation
in the mmc core in place, allowing it to be more easily reviewed before
subsequent changes implements the actual support.

Basically, the approach here adds a new UHS-II bus_ops type and adds a
separate initialization path for the UHS-II card. The intent is to avoid us
from sprinkling the legacy initialization path, but also to simplify
implementation of the UHS-II specific bits.

At this point, there is only one new host ops added to manage the various
ios settings needed for UHS-II. Additional host ops that are needed, are
being added from subsequent changes.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/mmc/core/Makefile  |   2 +-
 drivers/mmc/core/core.c    |  17 ++-
 drivers/mmc/core/core.h    |   1 +
 drivers/mmc/core/sd_uhs2.c | 289 +++++++++++++++++++++++++++++++++++++
 include/linux/mmc/card.h   |   7 +
 include/linux/mmc/host.h   |  22 +++
 6 files changed, 336 insertions(+), 2 deletions(-)
 create mode 100644 drivers/mmc/core/sd_uhs2.c

diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 6a907736cd7a..15b067e8b0d1 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_MMC)		+= mmc_core.o
 mmc_core-y			:= core.o bus.o host.o \
 				   mmc.o mmc_ops.o sd.o sd_ops.o \
 				   sdio.o sdio_ops.o sdio_bus.o \
-				   sdio_cis.o sdio_io.o sdio_irq.o \
+				   sdio_cis.o sdio_io.o sdio_irq.o sd_uhs2.o\
 				   slot-gpio.o regulator.o
 mmc_core-$(CONFIG_OF)		+= pwrseq.o
 obj-$(CONFIG_PWRSEQ_SIMPLE)	+= pwrseq_simple.o
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 368f10405e13..fc3d8d61a97c 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2230,6 +2230,18 @@ void mmc_rescan(struct work_struct *work)
 		goto out;
 	}
 
+	/*
+	 * Ideally we should favor initialization of legacy SD cards and defer
+	 * UHS-II enumeration. However, it seems like cards doesn't reliably
+	 * announce their support for UHS-II in the response to the ACMD41,
+	 * while initializing the legacy SD interface. Therefore, let's start
+	 * with UHS-II for now.
+	 */
+	if (!mmc_attach_sd_uhs2(host)) {
+		mmc_release_host(host);
+		goto out;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
 		unsigned int freq = freqs[i];
 		if (freq > host->f_max) {
@@ -2251,10 +2263,13 @@ void mmc_rescan(struct work_struct *work)
 
 void mmc_start_host(struct mmc_host *host)
 {
+	bool power_up = !(host->caps2 &
+			 (MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_SD_UHS2));
+
 	host->f_init = max(min(freqs[0], host->f_max), host->f_min);
 	host->rescan_disable = 0;
 
-	if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {
+	if (power_up) {
 		mmc_claim_host(host);
 		mmc_power_up(host, host->ocr_avail);
 		mmc_release_host(host);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index f5f3f623ea49..2d80afc95e58 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -81,6 +81,7 @@ int mmc_detect_card_removed(struct mmc_host *host);
 int mmc_attach_mmc(struct mmc_host *host);
 int mmc_attach_sd(struct mmc_host *host);
 int mmc_attach_sdio(struct mmc_host *host);
+int mmc_attach_sd_uhs2(struct mmc_host *host);
 
 /* Module parameters */
 extern bool use_spi_crc;
diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
new file mode 100644
index 000000000000..800957f74632
--- /dev/null
+++ b/drivers/mmc/core/sd_uhs2.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * Support for SD UHS-II cards
+ */
+#include <linux/err.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+#include "core.h"
+#include "bus.h"
+#include "sd.h"
+#include "mmc_ops.h"
+
+static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
+
+static int sd_uhs2_set_ios(struct mmc_host *host)
+{
+	struct mmc_ios *ios = &host->ios;
+
+	return host->ops->uhs2_set_ios(host, ios);
+}
+
+static int sd_uhs2_power_up(struct mmc_host *host)
+{
+	host->ios.vdd = fls(host->ocr_avail) - 1;
+	host->ios.clock = host->f_init;
+	host->ios.timing = MMC_TIMING_SD_UHS2;
+	host->ios.power_mode = MMC_POWER_UP;
+
+	return sd_uhs2_set_ios(host);
+}
+
+static void sd_uhs2_power_off(struct mmc_host *host)
+{
+	host->ios.vdd = 0;
+	host->ios.clock = 0;
+	host->ios.timing = MMC_TIMING_LEGACY;
+	host->ios.power_mode = MMC_POWER_OFF;
+
+	sd_uhs2_set_ios(host);
+}
+
+/*
+ * Run the phy initialization sequence, which mainly relies on the UHS-II host
+ * to check that we reach the expected electrical state, between the host and
+ * the card.
+ */
+static int sd_uhs2_phy_init(struct mmc_host *host)
+{
+	return 0;
+}
+
+/*
+ * Do the early initialization of the card, by sending the device init broadcast
+ * command and wait for the process to be completed.
+ */
+static int sd_uhs2_dev_init(struct mmc_host *host)
+{
+	return 0;
+}
+
+/*
+ * Run the enumeration process by sending the enumerate command to the card.
+ * Note that, we currently support only the point to point connection, which
+ * means only one card can be attached per host/slot.
+ */
+static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id)
+{
+	return 0;
+}
+
+/*
+ * Read the UHS-II configuration registers (CFG_REG) of the card, by sending it
+ * commands and by parsing the responses. Store a copy of the relevant data in
+ * card->uhs2_config.
+ */
+static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
+{
+	return 0;
+}
+
+/*
+ * Based on the card's and host's UHS-II capabilities, let's update the
+ * configuration of the card and the host. This may also include to move to a
+ * greater speed range/mode. Depending on the updated configuration, we may need
+ * to do a soft reset of the card via sending it a GO_DORMANT_STATE command.
+ *
+ * In the final step, let's check if the card signals "config completion", which
+ * indicates that the card has moved from config state into active state.
+ */
+static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
+{
+	return 0;
+}
+
+/*
+ * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
+ * commands/requests to be backwards compatible through the legacy SD protocol.
+ * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should
+ * be set through a legacy CMD6. Note that, the power limit that becomes set,
+ * survives a soft reset through the GO_DORMANT_STATE command.
+ */
+static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
+{
+	return 0;
+}
+
+/*
+ * Allocate the data structure for the mmc_card and run the UHS-II specific
+ * initialization sequence.
+ */
+static int sd_uhs2_init_card(struct mmc_host *host)
+{
+	struct mmc_card *card;
+	u32 node_id;
+	int err;
+
+	err = sd_uhs2_dev_init(host);
+	if (err)
+		return err;
+
+	err = sd_uhs2_enum(host, &node_id);
+	if (err)
+		return err;
+
+	card = mmc_alloc_card(host, &sd_type);
+	if (IS_ERR(card))
+		return PTR_ERR(card);
+
+	card->uhs2_config.node_id = node_id;
+	card->type = MMC_TYPE_SD;
+
+	err = sd_uhs2_config_read(host, card);
+	if (err)
+		goto err;
+
+	err = sd_uhs2_config_write(host, card);
+	if (err)
+		goto err;
+
+	err = sd_uhs2_legacy_init(host, card);
+	if (err)
+		goto err;
+
+	host->card = card;
+	return 0;
+
+err:
+	mmc_remove_card(card);
+	return err;
+}
+
+static void sd_uhs2_remove(struct mmc_host *host)
+{
+	mmc_remove_card(host->card);
+	host->card = NULL;
+}
+
+static int sd_uhs2_alive(struct mmc_host *host)
+{
+	return mmc_send_status(host->card, NULL);
+}
+
+static void sd_uhs2_detect(struct mmc_host *host)
+{
+	int err;
+
+	mmc_get_card(host->card, NULL);
+	err = _mmc_detect_card_removed(host);
+	mmc_put_card(host->card, NULL);
+
+	if (err) {
+		sd_uhs2_remove(host);
+
+		mmc_claim_host(host);
+		mmc_detach_bus(host);
+		sd_uhs2_power_off(host);
+		mmc_release_host(host);
+	}
+}
+
+static int sd_uhs2_suspend(struct mmc_host *host)
+{
+	return 0;
+}
+
+static int sd_uhs2_resume(struct mmc_host *host)
+{
+	return 0;
+}
+
+static int sd_uhs2_runtime_suspend(struct mmc_host *host)
+{
+	return 0;
+}
+
+static int sd_uhs2_runtime_resume(struct mmc_host *host)
+{
+	return 0;
+}
+
+static int sd_uhs2_shutdown(struct mmc_host *host)
+{
+	return 0;
+}
+
+static int sd_uhs2_hw_reset(struct mmc_host *host)
+{
+	return 0;
+}
+
+static const struct mmc_bus_ops sd_uhs2_ops = {
+	.remove = sd_uhs2_remove,
+	.alive = sd_uhs2_alive,
+	.detect = sd_uhs2_detect,
+	.suspend = sd_uhs2_suspend,
+	.resume = sd_uhs2_resume,
+	.runtime_suspend = sd_uhs2_runtime_suspend,
+	.runtime_resume = sd_uhs2_runtime_resume,
+	.shutdown = sd_uhs2_shutdown,
+	.hw_reset = sd_uhs2_hw_reset,
+};
+
+static int sd_uhs2_attach(struct mmc_host *host)
+{
+	int err;
+
+	err = sd_uhs2_power_up(host);
+	if (err)
+		goto err;
+
+	err = sd_uhs2_phy_init(host);
+	if (err)
+		goto err;
+
+	err = sd_uhs2_init_card(host);
+	if (err)
+		goto err;
+
+	mmc_attach_bus(host, &sd_uhs2_ops);
+
+	mmc_release_host(host);
+
+	err = mmc_add_card(host->card);
+	if (err)
+		goto remove_card;
+
+	mmc_claim_host(host);
+	return 0;
+
+remove_card:
+	mmc_remove_card(host->card);
+	host->card = NULL;
+	mmc_claim_host(host);
+	mmc_detach_bus(host);
+err:
+	sd_uhs2_power_off(host);
+	return err;
+}
+
+int mmc_attach_sd_uhs2(struct mmc_host *host)
+{
+	int i, err = 0;
+
+	if (!(host->caps2 & MMC_CAP2_SD_UHS2))
+		return -EOPNOTSUPP;
+
+	/* Turn off the legacy SD interface before trying with UHS-II. */
+	mmc_power_off(host);
+
+	/*
+	 * Start UHS-II initialization at 52MHz and possibly make a retry at
+	 * 26MHz according to the spec. It's required that the host driver
+	 * validates ios->clock, to set a rate within the correct range.
+	 */
+	for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) {
+		host->f_init = sd_uhs2_freqs[i];
+		err = sd_uhs2_attach(host);
+		if (!err)
+			break;
+	}
+
+	return err;
+}
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 37f975875102..610577d531c3 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -211,6 +211,11 @@ struct sd_ext_reg {
 #define SD_EXT_PERF_CMD_QUEUE	(1<<4)
 };
 
+struct sd_uhs2_config {
+	u32			node_id;
+	/* TODO: Extend with more register configs. */
+};
+
 struct sdio_cccr {
 	unsigned int		sdio_vsn;
 	unsigned int		sd_vsn;
@@ -315,6 +320,8 @@ struct mmc_card {
 	struct sd_ext_reg	ext_power;	/* SD extension reg for PM */
 	struct sd_ext_reg	ext_perf;	/* SD extension reg for PERF */
 
+	struct sd_uhs2_config	uhs2_config;	/* SD UHS-II config */
+
 	unsigned int		sdio_funcs;	/* number of SDIO functions */
 	atomic_t		sdio_funcs_probed; /* number of probed SDIO funcs */
 	struct sdio_cccr	cccr;		/* common card info */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7afb57cab00b..16ccfb61fc58 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -63,6 +63,10 @@ struct mmc_ios {
 #define MMC_TIMING_MMC_HS400	10
 #define MMC_TIMING_SD_EXP	11
 #define MMC_TIMING_SD_EXP_1_2V	12
+#define MMC_TIMING_SD_UHS2	13
+
+	unsigned char	vqmmc2_voltage;
+#define MMC_VQMMC2_VOLTAGE_180	0
 
 	unsigned char	signal_voltage;		/* signalling voltage (1.8V or 3.3V) */
 
@@ -91,6 +95,10 @@ struct mmc_clk_phase_map {
 	struct mmc_clk_phase phase[MMC_NUM_CLK_PHASES];
 };
 
+struct sd_uhs2_caps {
+	/* TODO: Add UHS-II capabilities for the host. */
+};
+
 struct mmc_host;
 
 struct mmc_host_ops {
@@ -126,6 +134,17 @@ struct mmc_host_ops {
 	 */
 	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
 
+	/*
+	 * The uhs2_set_ios callback is mandatory to implement for hosts that
+	 * supports the SD UHS-II interface (MMC_CAP2_SD_UHS2), while the
+	 * callback is unused for the other cases. Note that, the struct
+	 * mmc_ios is being re-used for this as well.
+	 *
+	 * Expected return values for the uhs2_set_ios callback are a negative
+	 * errno in case of a failure or zero for success.
+	 */
+	int	(*uhs2_set_ios)(struct mmc_host *host, struct mmc_ios *ios);
+
 	/*
 	 * Return values for the get_ro callback should be:
 	 *   0 for a read/write card
@@ -377,6 +396,7 @@ struct mmc_host {
 				 MMC_CAP2_HS200_1_2V_SDR)
 #define MMC_CAP2_SD_EXP		(1 << 7)	/* SD express via PCIe */
 #define MMC_CAP2_SD_EXP_1_2V	(1 << 8)	/* SD express 1.2V */
+#define MMC_CAP2_SD_UHS2	(1 << 9)	/* SD UHS-II support */
 #define MMC_CAP2_CD_ACTIVE_HIGH	(1 << 10)	/* Card-detect signal active high */
 #define MMC_CAP2_RO_ACTIVE_HIGH	(1 << 11)	/* Write-protect signal active high */
 #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14)	/* Don't power up before scan */
@@ -403,6 +423,8 @@ struct mmc_host {
 #endif
 #define MMC_CAP2_ALT_GPT_TEGRA	(1 << 28)	/* Host with eMMC that has GPT entry at a non-standard location */
 
+	struct sd_uhs2_caps	uhs2_caps;	/* SD UHS-II capabilities */
+
 	int			fixed_drv_type;	/* fixed driver type for non-removable media */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH V3 3/7] mmc: core: Announce successful insertion of an SD UHS-II card
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
  2022-02-22  3:39 ` [PATCH V3 1/7] mmc: core: Cleanup printing of speed mode at card insertion Jason Lai
  2022-02-22  3:39 ` [PATCH V3 2/7] mmc: core: Prepare to support SD UHS-II cards Jason Lai
@ 2022-02-22  3:39 ` Jason Lai
  2022-02-22  3:39 ` [PATCH V3 4/7] mmc: core: Extend support for mmc regulators with a vqmmc2 Jason Lai
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Ulf Hansson <ulf.hansson@linaro.org>

To inform the users about SD UHS-II cards, let's extend the print at card
insertion with a "UHS-II" substring. Within this change, it seems
reasonable to convert from using "ultra high speed" into "UHS-I speed", for
the UHS-I type, as it should makes it more clear.

Note that, the new print for UHS-II cards doesn't include the actual
selected speed mode. Instead, this is going to be added from subsequent
change.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/mmc/core/bus.c  | 4 +++-
 drivers/mmc/core/host.h | 4 ++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index eefe74bf1356..cceedc7a7213 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -341,7 +341,9 @@ int mmc_add_card(struct mmc_card *card)
 	if (mmc_card_hs(card))
 		speed_mode = "high speed ";
 	else if (mmc_card_uhs(card))
-		speed_mode = "ultra high speed ";
+		speed_mode = "UHS-I speed ";
+	else if (mmc_card_uhs2(card))
+		speed_mode = "UHS-II speed ";
 	else if	(mmc_card_ddr52(card))
 		speed_mode = "high speed DDR ";
 	else if (mmc_card_hs200(card))
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index 48c4952512a5..2c2ad9d63c0b 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -89,5 +89,9 @@ static inline bool mmc_card_sd_express(struct mmc_host *host)
 		host->ios.timing == MMC_TIMING_SD_EXP_1_2V;
 }
 
+static inline bool mmc_card_uhs2(struct mmc_card *card)
+{
+	return card->host->ios.timing == MMC_TIMING_SD_UHS2;
+}
 #endif
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH V3 4/7] mmc: core: Extend support for mmc regulators with a vqmmc2
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
                   ` (2 preceding siblings ...)
  2022-02-22  3:39 ` [PATCH V3 3/7] mmc: core: Announce successful insertion of an SD UHS-II card Jason Lai
@ 2022-02-22  3:39 ` Jason Lai
  2022-02-22  3:39 ` [PATCH V3 5/7] mmc: add UHS-II related definitions in headers Jason Lai
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Ulf Hansson <ulf.hansson@linaro.org>

To allow an additional external regulator to be controlled by an mmc host
driver, let's add support for a vqmmc2 regulator to the mmc core.

For an SD UHS-II interface the vqmmc2 regulator may correspond to the so
called vdd2 supply, as described by the SD spec. Initially, only 1.8V is
needed, hence limit the new helper function, mmc_regulator_set_vqmmc2() to
this too.

Note that, to allow for flexibility mmc host drivers need to manage the
enable/disable of the vqmmc2 regulator themselves, while the regulator is
looked up through the common mmc_regulator_get_supply().

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/mmc/core/regulator.c | 34 ++++++++++++++++++++++++++++++++++
 include/linux/mmc/host.h     |  8 ++++++++
 2 files changed, 42 insertions(+)

diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
index 609201a467ef..3c189682797c 100644
--- a/drivers/mmc/core/regulator.c
+++ b/drivers/mmc/core/regulator.c
@@ -223,6 +223,33 @@ int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
 }
 EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
 
+/**
+ * mmc_regulator_set_vqmmc2 - Set vqmmc2 as per the ios->vqmmc2_voltage
+ * @mmc: The mmc host to regulate
+ * @ios: The io bus settings
+ *
+ * Sets a new voltage level for the vqmmc2 regulator, which may correspond to
+ * the vdd2 regulator for an SD UHS-II interface. This function is expected to
+ * be called by mmc host drivers.
+ *
+ * Returns a negative error code on failure, zero if the voltage level was
+ * changed successfully or a positive value if the level didn't need to change.
+ */
+int mmc_regulator_set_vqmmc2(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	if (IS_ERR(mmc->supply.vqmmc2))
+		return -EINVAL;
+
+	switch (ios->vqmmc2_voltage) {
+	case MMC_VQMMC2_VOLTAGE_180:
+		return mmc_regulator_set_voltage_if_supported(
+			mmc->supply.vqmmc2, 1700000, 1800000, 1950000);
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc2);
+
 #else
 
 static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
@@ -249,6 +276,7 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
 
 	mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");
 	mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
+	mmc->supply.vqmmc2 = devm_regulator_get_optional(dev, "vqmmc2");
 
 	if (IS_ERR(mmc->supply.vmmc)) {
 		if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
@@ -268,6 +296,12 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
 		dev_dbg(dev, "No vqmmc regulator found\n");
 	}
 
+	if (IS_ERR(mmc->supply.vqmmc2)) {
+		if (PTR_ERR(mmc->supply.vqmmc2) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_dbg(dev, "No vqmmc2 regulator found\n");
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 16ccfb61fc58..d770941f05c7 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -305,6 +305,7 @@ struct mmc_pwrseq;
 struct mmc_supply {
 	struct regulator *vmmc;		/* Card power supply */
 	struct regulator *vqmmc;	/* Optional Vccq supply */
+	struct regulator *vqmmc2;	/* Optional supply for phy */
 };
 
 struct mmc_ctx {
@@ -583,6 +584,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
 			struct regulator *supply,
 			unsigned short vdd_bit);
 int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios);
+int mmc_regulator_set_vqmmc2(struct mmc_host *mmc, struct mmc_ios *ios);
 #else
 static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
 				 struct regulator *supply,
@@ -596,6 +598,12 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
 {
 	return -EINVAL;
 }
+
+static inline int mmc_regulator_set_vqmmc2(struct mmc_host *mmc,
+					   struct mmc_ios *ios)
+{
+	return -EINVAL;
+}
 #endif
 
 int mmc_regulator_get_supply(struct mmc_host *mmc);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH V3 5/7] mmc: add UHS-II related definitions in headers
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
                   ` (3 preceding siblings ...)
  2022-02-22  3:39 ` [PATCH V3 4/7] mmc: core: Extend support for mmc regulators with a vqmmc2 Jason Lai
@ 2022-02-22  3:39 ` Jason Lai
  2022-02-22  3:39 ` [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions Jason Lai
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Jason Lai <jason.lai@genesyslogic.com.tw>

1. Define UHS2 members in some data structures.
2. Define UHS2 registers and messages.

Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
---
 drivers/mmc/core/sd_uhs2.h  |  16 +++
 include/linux/mmc/card.h    |  30 +++++-
 include/linux/mmc/core.h    |   6 ++
 include/linux/mmc/host.h    |  41 +++++++-
 include/linux/mmc/sd_uhs2.h | 198 ++++++++++++++++++++++++++++++++++++
 5 files changed, 289 insertions(+), 2 deletions(-)
 create mode 100644 drivers/mmc/core/sd_uhs2.h
 create mode 100644 include/linux/mmc/sd_uhs2.h

diff --git a/drivers/mmc/core/sd_uhs2.h b/drivers/mmc/core/sd_uhs2.h
new file mode 100644
index 000000000000..91e895b331e0
--- /dev/null
+++ b/drivers/mmc/core/sd_uhs2.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Header file for UHS-II packets, Host Controller registers and I/O
+ * accessors.
+ *
+ *  Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ */
+#ifndef MMC_UHS2_H
+#define MMC_UHS2_H
+
+struct mmc_host;
+struct mmc_request;
+
+int sd_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
+
+#endif /* MMC_UHS2_H */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 610577d531c3..48c72bb75d74 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -213,7 +213,35 @@ struct sd_ext_reg {
 
 struct sd_uhs2_config {
 	u32			node_id;
-	/* TODO: Extend with more register configs. */
+
+	u32			dap;
+	u32			gap;
+	u32			n_fcu;
+	u32			maxblk_len;
+	u8			n_lanes;
+	u8			dadr_len;
+	u8			app_type;
+	u8			phy_minor_rev;
+	u8			phy_major_rev;
+	u8			can_hibernate;
+	u8			n_lss_sync;
+	u8			n_lss_dir;
+	u8			link_minor_rev;
+	u8			link_major_rev;
+	u8			dev_type;
+	u8			n_data_gap;
+
+	u32			n_fcu_set;
+	u32			maxblk_len_set;
+	u8			n_lanes_set;
+	u8			speed_range_set;
+	u8			n_lss_sync_set;
+	u8			n_lss_dir_set;
+	u8			n_data_gap_set;
+	u8			pwrctrl_mode_set;
+	u8			max_retry_set;
+
+	u8			cfg_complete;
 };
 
 struct sdio_cccr {
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 71101d1ec825..cfc87be6700a 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -2,6 +2,7 @@
 /*
  *  linux/include/linux/mmc/core.h
  */
+
 #ifndef LINUX_MMC_CORE_H
 #define LINUX_MMC_CORE_H
 
@@ -109,6 +110,11 @@ struct mmc_command {
 	unsigned int		busy_timeout;	/* busy detect timeout in ms */
 	struct mmc_data		*data;		/* data segment associated with cmd */
 	struct mmc_request	*mrq;		/* associated request */
+
+	struct uhs2_command	*uhs2_cmd;	/* UHS2 command */
+	u8			*uhs2_resp;	/* UHS2 native cmd resp */
+	u8			uhs2_resp_len;	/* UHS2 native cmd resp len */
+	u8			uhs2_tmode0_flag; /* UHS2 transfer mode flag */
 };
 
 struct mmc_data {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index d770941f05c7..39b39e0cdb2f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -13,6 +13,7 @@
 
 #include <linux/mmc/core.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/sd_uhs2.h>
 #include <linux/mmc/pm.h>
 #include <linux/dma-direction.h>
 #include <linux/blk-crypto-profile.h>
@@ -96,7 +97,38 @@ struct mmc_clk_phase_map {
 };
 
 struct sd_uhs2_caps {
-	/* TODO: Add UHS-II capabilities for the host. */
+	int			flags;
+#define MMC_UHS2_SUPPORT	BIT(0)
+#define MMC_UHS2_INITIALIZED	BIT(1)
+#define MMC_UHS2_2L_HD		BIT(2)
+#define MMC_UHS2_APP_CMD	BIT(3)
+#define MMC_UHS2_SPEED_B	BIT(4)
+#define MMC_SUPPORT_ADMA3	BIT(5)
+
+	u32	dap;
+	u32	gap;
+	u32	group_desc;
+	u32	maxblk_len;
+	u32	n_fcu;
+	u8	n_lanes;
+	u8	addr64;
+	u8	card_type;
+	u8	phy_rev;
+	u8	speed_range;
+	u8	can_hibernate;
+	u8	n_lss_sync;
+	u8	n_lss_dir;
+	u8	link_rev;
+	u8	host_type;
+	u8	n_data_gap;
+
+	u32	maxblk_len_set;
+	u32	n_fcu_set;
+	u8	n_lanes_set;
+	u8	n_lss_sync_set;
+	u8	n_lss_dir_set;
+	u8	n_data_gap_set;
+	u8	max_retry_set;
 };
 
 struct mmc_host;
@@ -212,6 +244,13 @@ struct mmc_host_ops {
 
 	/* Initialize an SD express card, mandatory for MMC_CAP2_SD_EXP. */
 	int	(*init_sd_express)(struct mmc_host *host, struct mmc_ios *ios);
+
+	/* UHS2 interfaces */
+	/*
+	 * Every host controller can assign its own actions to set up their
+	 * controller.
+	 */
+	int	(*uhs2_host_operation)(struct mmc_host *host, enum uhs2_action act);
 };
 
 struct mmc_cqe_ops {
diff --git a/include/linux/mmc/sd_uhs2.h b/include/linux/mmc/sd_uhs2.h
new file mode 100644
index 000000000000..e134e194d581
--- /dev/null
+++ b/include/linux/mmc/sd_uhs2.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Header file for UHS-II packets, Host Controller registers and I/O
+ * accessors.
+ *
+ *  Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ */
+#ifndef LINUX_MMC_UHS2_H
+#define LINUX_MMC_UHS2_H
+
+/* LINK Layer definition */
+/* UHS2 Header */
+#define UHS2_NATIVE_PACKET_POS	7
+#define UHS2_NATIVE_PACKET	(1 << UHS2_NATIVE_PACKET_POS)
+
+#define UHS2_PACKET_TYPE_POS	4
+#define UHS2_PACKET_TYPE_CCMD	(0 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_DCMD	(1 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_RES	(2 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_DATA	(3 << UHS2_PACKET_TYPE_POS)
+#define UHS2_PACKET_TYPE_MSG	(7 << UHS2_PACKET_TYPE_POS)
+
+#define UHS2_DEST_ID_MASK	0x0F
+#define UHS2_DEST_ID		0x1
+
+#define UHS2_SRC_ID_POS		12
+#define UHS2_SRC_ID_MASK	0xF000
+
+#define UHS2_TRANS_ID_POS	8
+#define UHS2_TRANS_ID_MASK	0x0700
+
+/* UHS2 MSG */
+#define UHS2_MSG_CTG_POS	5
+#define UHS2_MSG_CTG_LMSG	0x00
+#define UHS2_MSG_CTG_INT	0x60
+#define UHS2_MSG_CTG_AMSG	0x80
+
+#define UHS2_MSG_CTG_FCREQ	0x00
+#define UHS2_MSG_CTG_FCRDY	0x01
+#define UHS2_MSG_CTG_STAT	0x02
+
+#define UHS2_MSG_CODE_POS			8
+#define UHS2_MSG_CODE_FC_UNRECOVER_ERR		0x8
+#define UHS2_MSG_CODE_STAT_UNRECOVER_ERR	0x8
+#define UHS2_MSG_CODE_STAT_RECOVER_ERR		0x1
+
+/* TRANS Layer definition */
+
+/* Native packets*/
+#define UHS2_NATIVE_CMD_RW_POS	7
+#define UHS2_NATIVE_CMD_WRITE	(1 << UHS2_NATIVE_CMD_RW_POS)
+#define UHS2_NATIVE_CMD_READ	(0 << UHS2_NATIVE_CMD_RW_POS)
+
+#define UHS2_NATIVE_CMD_PLEN_POS	4
+#define UHS2_NATIVE_CMD_PLEN_4B		(1 << UHS2_NATIVE_CMD_PLEN_POS)
+#define UHS2_NATIVE_CMD_PLEN_8B		(2 << UHS2_NATIVE_CMD_PLEN_POS)
+#define UHS2_NATIVE_CMD_PLEN_16B	(3 << UHS2_NATIVE_CMD_PLEN_POS)
+
+#define UHS2_NATIVE_CCMD_GET_MIOADR_MASK	0xF00
+#define UHS2_NATIVE_CCMD_MIOADR_MASK		0x0F
+
+#define UHS2_NATIVE_CCMD_LIOADR_POS		8
+#define UHS2_NATIVE_CCMD_GET_LIOADR_MASK	0x0FF
+
+#define UHS2_DCMD_DM_POS	6
+#define UHS2_DCMD_2L_HD_MODE	(1 << UHS2_DCMD_DM_POS)
+#define UHS2_DCMD_LM_POS	5
+#define UHS2_DCMD_LM_TLEN_EXIST	(1 << UHS2_DCMD_LM_POS)
+#define UHS2_DCMD_TLUM_POS	4
+#define UHS2_DCMD_TLUM_BYTE_MODE	(1 << UHS2_DCMD_TLUM_POS)
+#define UHS2_NATIVE_DCMD_DAM_POS	3
+#define UHS2_NATIVE_DCMD_DAM_IO		(1 << UHS2_NATIVE_DCMD_DAM_POS)
+
+#define UHS2_RES_NACK_POS	7
+#define UHS2_RES_NACK_MASK	(0x1 << UHS2_RES_NACK_POS)
+
+#define UHS2_RES_ECODE_POS	4
+#define UHS2_RES_ECODE_MASK	0x7
+#define UHS2_RES_ECODE_COND	1
+#define UHS2_RES_ECODE_ARG	2
+#define UHS2_RES_ECODE_GEN	3
+
+/* IOADR of device registers */
+#define UHS2_IOADR_GENERIC_CAPS		0x00
+#define UHS2_IOADR_PHY_CAPS		0x02
+#define UHS2_IOADR_LINK_CAPS		0x04
+#define UHS2_IOADR_RSV_CAPS		0x06
+#define UHS2_IOADR_GENERIC_SETTINGS	0x08
+#define UHS2_IOADR_PHY_SETTINGS		0x0A
+#define UHS2_IOADR_LINK_SETTINGS	0x0C
+#define UHS2_IOADR_PRESET		0x40
+
+/* SD application packets */
+#define UHS2_SD_CMD_INDEX_POS		8
+
+#define UHS2_SD_CMD_APP_POS		14
+#define UHS2_SD_CMD_APP			(1 << UHS2_SD_CMD_APP_POS)
+
+struct uhs2_command {
+	u16	header;
+	u16	arg;
+	u32	*payload;
+	u32	payload_len;
+	u32	packet_len;
+};
+
+enum uhs2_action {
+	UHS2_SET_CONFIG,
+	UHS2_ENABLE_INT,
+	UHS2_DISABLE_INT,
+	UHS2_SET_SPEED_B,
+	UHS2_CHECK_DORMANT,
+	UHS2_SW_RESET,
+	UHS2_DETECT_INIT,
+	UHS2_DISABLE_CLK,
+	UHS2_ENABLE_CLK,
+	UHS2_POST_ATTACH_SD
+};
+
+/* UHS-II Device Registers */
+#define UHS2_DEV_CONFIG_REG	0x000
+
+/* General Caps and Settings registers */
+#define  UHS2_DEV_CONFIG_GEN_CAPS	(UHS2_DEV_CONFIG_REG + 0x000)
+#define   UHS2_DEV_CONFIG_N_LANES_POS	8
+#define   UHS2_DEV_CONFIG_N_LANES_MASK	0x3F
+#define   UHS2_DEV_CONFIG_2L_HD_FD	0x1
+#define   UHS2_DEV_CONFIG_2D1U_FD	0x2
+#define   UHS2_DEV_CONFIG_1D2U_FD	0x4
+#define   UHS2_DEV_CONFIG_2D2U_FD	0x8
+#define   UHS2_DEV_CONFIG_DADR_POS	14
+#define   UHS2_DEV_CONFIG_DADR_MASK	0x1
+#define   UHS2_DEV_CONFIG_APP_POS	16
+#define   UHS2_DEV_CONFIG_APP_MASK	0xFF
+#define   UHS2_DEV_CONFIG_APP_SD_MEM	0x1
+
+#define  UHS2_DEV_CONFIG_GEN_SET	(UHS2_DEV_CONFIG_REG + 0x008)
+#define   UHS2_DEV_CONFIG_GEN_SET_N_LANES_POS	8
+#define   UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD	0x0
+#define   UHS2_DEV_CONFIG_GEN_SET_2D1U_FD	0x2
+#define   UHS2_DEV_CONFIG_GEN_SET_1D2U_FD	0x3
+#define   UHS2_DEV_CONFIG_GEN_SET_2D2U_FD	0x4
+#define   UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE	(0x1 << 31)
+
+/* PHY Caps and Settings registers */
+#define  UHS2_DEV_CONFIG_PHY_CAPS	(UHS2_DEV_CONFIG_REG + 0x002)
+#define   UHS2_DEV_CONFIG_PHY_MINOR_MASK	0xF
+#define   UHS2_DEV_CONFIG_PHY_MAJOR_POS		4
+#define   UHS2_DEV_CONFIG_PHY_MAJOR_MASK	0x3
+#define   UHS2_DEV_CONFIG_CAN_HIBER_POS		15
+#define   UHS2_DEV_CONFIG_CAN_HIBER_MASK	0x1
+#define  UHS2_DEV_CONFIG_PHY_CAPS1	(UHS2_DEV_CONFIG_REG + 0x003)
+#define   UHS2_DEV_CONFIG_N_LSS_SYN_MASK	0xF
+#define   UHS2_DEV_CONFIG_N_LSS_DIR_POS		4
+#define   UHS2_DEV_CONFIG_N_LSS_DIR_MASK	0xF
+
+#define  UHS2_DEV_CONFIG_PHY_SET	(UHS2_DEV_CONFIG_REG + 0x00A)
+#define   UHS2_DEV_CONFIG_PHY_SET_SPEED_POS	6
+#define   UHS2_DEV_CONFIG_PHY_SET_SPEED_A	0x0
+#define   UHS2_DEV_CONFIG_PHY_SET_SPEED_B	0x1
+
+/* LINK-TRAN Caps and Settings registers */
+#define  UHS2_DEV_CONFIG_LINK_TRAN_CAPS	(UHS2_DEV_CONFIG_REG + 0x004)
+#define   UHS2_DEV_CONFIG_LT_MINOR_MASK		0xF
+#define   UHS2_DEV_CONFIG_LT_MAJOR_POS		4
+#define   UHS2_DEV_CONFIG_LT_MAJOR_MASK		0x3
+#define   UHS2_DEV_CONFIG_N_FCU_POS		8
+#define   UHS2_DEV_CONFIG_N_FCU_MASK		0xFF
+#define   UHS2_DEV_CONFIG_DEV_TYPE_POS		16
+#define   UHS2_DEV_CONFIG_DEV_TYPE_MASK		0x7
+#define   UHS2_DEV_CONFIG_MAX_BLK_LEN_POS	20
+#define   UHS2_DEV_CONFIG_MAX_BLK_LEN_MASK	0xFFF
+#define  UHS2_DEV_CONFIG_LINK_TRAN_CAPS1	(UHS2_DEV_CONFIG_REG + 0x005)
+#define   UHS2_DEV_CONFIG_N_DATA_GAP_MASK	0xFF
+
+#define  UHS2_DEV_CONFIG_LINK_TRAN_SET	(UHS2_DEV_CONFIG_REG + 0x00C)
+#define   UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN	0x200
+#define   UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS	16
+
+/* Preset register */
+#define  UHS2_DEV_CONFIG_PRESET		(UHS2_DEV_CONFIG_REG + 0x040)
+
+#define UHS2_DEV_INT_REG	0x100
+
+#define UHS2_DEV_STATUS_REG	0x180
+
+#define UHS2_DEV_CMD_REG	0x200
+#define  UHS2_DEV_CMD_FULL_RESET	(UHS2_DEV_CMD_REG + 0x000)
+#define  UHS2_DEV_CMD_GO_DORMANT_STATE	(UHS2_DEV_CMD_REG + 0x001)
+#define   UHS2_DEV_CMD_DORMANT_HIBER	(0x1 << 7)
+#define  UHS2_DEV_CMD_DEVICE_INIT	(UHS2_DEV_CMD_REG + 0x002)
+#define  UHS2_DEV_CMD_ENUMERATE		(UHS2_DEV_CMD_REG + 0x003)
+#define  UHS2_DEV_CMD_TRANS_ABORT	(UHS2_DEV_CMD_REG + 0x004)
+
+#define UHS2_RCLK_MAX	52000000
+#define UHS2_RCLK_MIN	26000000
+
+#endif /* LINUX_MMC_UHS2_H */
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
                   ` (4 preceding siblings ...)
  2022-02-22  3:39 ` [PATCH V3 5/7] mmc: add UHS-II related definitions in headers Jason Lai
@ 2022-02-22  3:39 ` Jason Lai
  2022-03-23 16:15   ` Ulf Hansson
  2022-02-22  3:39 ` [PATCH V3 7/7] mmc: core: Support UHS-II card access Jason Lai
  2022-03-18 13:16 ` [PATCH V3 0/7] Preparations to support SD UHS-II cards Ulf Hansson
  7 siblings, 1 reply; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Jason Lai <jason.lai@genesyslogic.com.tw>

UHS-II card initialization flow is divided into 2 categories: PHY & Card.
Part 1 - PHY Initialization:
         Every host controller may need their own avtivation operation to
         establish LINK between controller and card. So we add a new member
         function(uhs2_detect_init) in struct mmc_host_ops for host
         controller use.
Part 2 - Card Initialization:
         This part can be divided into 6 substeps.
         1. Send UHS-II CCMD DEVICE_INIT to card.
         2. Send UHS-II CCMD ENUMERATE to card.
         3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
            of card.
         4. Host compares capabilities of host controller and card, then
            write the negotiated values to Setting field in CFG_REG of card
            through UHS-II Native Write CCMD.
         5. Switch host controller's clock to Range B if it is supported by
            both host controller and card.
         6. Execute legacy SD initialization flow.
Part 3 - Provide a function to tranaform legacy SD command packet into
         UHS-II SD-TRAN DCMD packet.

Most of the code added above came from Intel's original patch[3].

[3]
https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
git-send-email-yi.y.sun@intel.com/

Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
---
 drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 817 insertions(+), 18 deletions(-)

diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
index 800957f74632..f1e8e30301eb 100644
--- a/drivers/mmc/core/sd_uhs2.c
+++ b/drivers/mmc/core/sd_uhs2.c
@@ -3,6 +3,7 @@
  * Copyright (C) 2021 Linaro Ltd
  *
  * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
  *
  * Support for SD UHS-II cards
  */
@@ -10,19 +11,31 @@
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd_uhs2.h>
 
 #include "core.h"
 #include "bus.h"
+#include "card.h"
 #include "sd.h"
+#include "sd_ops.h"
 #include "mmc_ops.h"
+#include "sd_uhs2.h"
 
 static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
 
 static int sd_uhs2_set_ios(struct mmc_host *host)
 {
 	struct mmc_ios *ios = &host->ios;
+	int err = 0;
 
-	return host->ops->uhs2_set_ios(host, ios);
+	pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
+		 mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
+		 ios->timing);
+
+	host->ops->set_ios(host, ios);
+
+	return err;
 }
 
 static int sd_uhs2_power_up(struct mmc_host *host)
@@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
 	sd_uhs2_set_ios(host);
 }
 
+/**
+ * sd_uhs2_cmd_assemble() - build up UHS-II command packet which is embedded in
+ *                          mmc_command structure
+ * @cmd:	MMC command to executed
+ * @uhs2_cmd:	UHS2 command corresponded to MMC command
+ * @header:	Header field of UHS-II command cxpacket
+ * @arg:	Argument field of UHS-II command packet
+ * @payload:	Payload field of UHS-II command packet
+ * @plen:	Payload length
+ * @resp:	Response buffer is allocated by caller and it is used to keep
+ *              the response of CM-TRAN command. For SD-TRAN command, uhs2_resp
+ *              should be null and SD-TRAN command response should be stored in
+ *              resp of mmc_command.
+ * @resp_len:	Response buffer length
+ *
+ * The uhs2_command structure contains message packets which are transmited/
+ * received on UHS-II bus. This function fills in the contents of uhs2_command
+ * structure and embededs UHS2 command into mmc_command structure, which is used
+ * in legacy SD operation functions.
+ *
+ */
+static void sd_uhs2_cmd_assemble(struct mmc_command *cmd,
+				 struct uhs2_command *uhs2_cmd,
+				 u16 header, u16 arg, u32 *payload,
+				 u8 plen, u8 *resp, u8 resp_len)
+{
+	uhs2_cmd->header = header;
+	uhs2_cmd->arg = arg;
+	uhs2_cmd->payload = payload;
+	uhs2_cmd->payload_len = plen * sizeof(u32);
+	uhs2_cmd->packet_len = uhs2_cmd->payload_len + 4;
+
+	cmd->uhs2_cmd = uhs2_cmd;
+	cmd->uhs2_resp = resp;
+	cmd->uhs2_resp_len = resp_len;
+}
+
 /*
  * Run the phy initialization sequence, which mainly relies on the UHS-II host
  * to check that we reach the expected electrical state, between the host and
@@ -52,7 +102,15 @@ static void sd_uhs2_power_off(struct mmc_host *host)
  */
 static int sd_uhs2_phy_init(struct mmc_host *host)
 {
-	return 0;
+	int err = 0;
+
+	err = host->ops->uhs2_host_operation(host, UHS2_DETECT_INIT);
+	if (err) {
+		pr_err("%s: failed to initial phy for UHS-II!\n",
+		       mmc_hostname(host));
+	}
+
+	return err;
 }
 
 /*
@@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
  */
 static int sd_uhs2_dev_init(struct mmc_host *host)
 {
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	u32 cnt;
+	u32 dap, gap, resp_gap;
+	u16 header = 0, arg = 0;
+	u32 payload[1];
+	u8 plen = 1;
+	u8 gd = 0, cf = 1;
+	u8 resp[6] = {0};
+	u8 resp_len = 6;
+	int err;
+
+	dap = host->uhs2_caps.dap;
+	gap = host->uhs2_caps.gap;
+
+	header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
+	arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
+	       UHS2_NATIVE_CMD_WRITE |
+	       UHS2_NATIVE_CMD_PLEN_4B |
+	       (UHS2_DEV_CMD_DEVICE_INIT >> 8);
+
+	/*
+	 * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
+	 * Max. time from DEVICE_INIT CCMD EOP reception on Device
+	 * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
+	 * 1 second.
+	 */
+	cmd.busy_timeout = 1000;
+
+	/*
+	 * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
+	 * When the number of the DEVICE_INIT commands is reach to
+	 * 30 tiems, Host shall stop issuing DEVICE_INIT command
+	 * and regard it as an error.
+	 */
+	for (cnt = 0; cnt < 30; cnt++) {
+		payload[0] = ((dap & 0xF) << 12) |
+			      (cf << 11)         |
+			      ((gd & 0xF) << 4)  |
+			      (gap & 0xF);
+
+		sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
+
+		err = mmc_wait_for_cmd(host, &cmd, 0);
+
+		if (err) {
+			pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+			       mmc_hostname(host), __func__, err);
+			return -EIO;
+		}
+
+		if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
+			pr_err("%s: DEVICE_INIT response is wrong!\n",
+			       mmc_hostname(host));
+			return -EIO;
+		}
+
+		if (resp[5] & 0x8) {
+			host->uhs2_caps.group_desc = gd;
+			break;
+		}
+		resp_gap = resp[4] & 0x0F;
+		if (gap == resp_gap)
+			gd++;
+	}
+	if (cnt == 30) {
+		pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
+		       mmc_hostname(host));
+		return -EIO;
+	}
+
 	return 0;
 }
 
@@ -71,16 +200,168 @@ static int sd_uhs2_dev_init(struct mmc_host *host)
  */
 static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id)
 {
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	u16 header = 0, arg = 0;
+	u32 payload[1];
+	u8 plen = 1;
+	u8 id_f = 0xF, id_l = 0x0;
+	u8 resp[8] = {0};
+	u8 resp_len = 8;
+	int err;
+
+	header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
+	arg = ((UHS2_DEV_CMD_ENUMERATE & 0xFF) << 8) |
+	       UHS2_NATIVE_CMD_WRITE |
+	       UHS2_NATIVE_CMD_PLEN_4B |
+	       (UHS2_DEV_CMD_ENUMERATE >> 8);
+
+	payload[0] = (id_f << 4) | id_l;
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, resp, resp_len);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	if (resp[3] != (UHS2_DEV_CMD_ENUMERATE & 0xFF)) {
+		pr_err("%s: ENUMERATE response is wrong!\n",
+		       mmc_hostname(host));
+		return -EIO;
+	}
+
+	id_f = (resp[4] >> 4) & 0xF;
+	id_l = resp[4] & 0xF;
+	*node_id = id_f;
+
 	return 0;
 }
 
 /*
- * Read the UHS-II configuration registers (CFG_REG) of the card, by sending it
- * commands and by parsing the responses. Store a copy of the relevant data in
- * card->uhs2_config.
+ * Read the UHS-II configuration registers (CFG_REG) from card and store these
+ * configurations to card->uhs2_config.
  */
 static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
 {
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	u16 header = 0, arg = 0;
+	u32 cap;
+	int err;
+
+	header = UHS2_NATIVE_PACKET |
+		 UHS2_PACKET_TYPE_CCMD |
+		 card->uhs2_config.node_id;
+	arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
+	       UHS2_NATIVE_CMD_READ |
+	       UHS2_NATIVE_CMD_PLEN_4B |
+	       (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
+
+	/* There is no payload because per spec, there should be
+	 * no payload field for read CCMD.
+	 * Plen is set in arg. Per spec, plen for read CCMD
+	 * represents the len of read data which is assigned in payload
+	 * of following RES (p136).
+	 */
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return err;
+	}
+
+	cap = cmd.resp[0];
+	card->uhs2_config.n_lanes =
+				(cap >> UHS2_DEV_CONFIG_N_LANES_POS) &
+				UHS2_DEV_CONFIG_N_LANES_MASK;
+	card->uhs2_config.dadr_len =
+				(cap >> UHS2_DEV_CONFIG_DADR_POS) &
+				UHS2_DEV_CONFIG_DADR_MASK;
+	card->uhs2_config.app_type =
+				(cap >> UHS2_DEV_CONFIG_APP_POS) &
+				UHS2_DEV_CONFIG_APP_MASK;
+
+	arg = ((UHS2_DEV_CONFIG_PHY_CAPS & 0xFF) << 8) |
+	       UHS2_NATIVE_CMD_READ |
+	       UHS2_NATIVE_CMD_PLEN_8B |
+	      (UHS2_DEV_CONFIG_PHY_CAPS >> 8);
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	cap = cmd.resp[0];
+	card->uhs2_config.phy_minor_rev =
+				cap & UHS2_DEV_CONFIG_PHY_MINOR_MASK;
+	card->uhs2_config.phy_major_rev =
+				(cap >> UHS2_DEV_CONFIG_PHY_MAJOR_POS) &
+				 UHS2_DEV_CONFIG_PHY_MAJOR_MASK;
+	card->uhs2_config.can_hibernate =
+				(cap >> UHS2_DEV_CONFIG_CAN_HIBER_POS) &
+				 UHS2_DEV_CONFIG_CAN_HIBER_MASK;
+
+	cap = cmd.resp[1];
+	card->uhs2_config.n_lss_sync =
+				cap & UHS2_DEV_CONFIG_N_LSS_SYN_MASK;
+	card->uhs2_config.n_lss_dir =
+				(cap >> UHS2_DEV_CONFIG_N_LSS_DIR_POS) &
+				UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+	if (card->uhs2_config.n_lss_sync == 0)
+		card->uhs2_config.n_lss_sync = 16 << 2;
+	else
+		card->uhs2_config.n_lss_sync <<= 2;
+
+	if (card->uhs2_config.n_lss_dir == 0)
+		card->uhs2_config.n_lss_dir = 16 << 3;
+	else
+		card->uhs2_config.n_lss_dir <<= 3;
+
+	arg = ((UHS2_DEV_CONFIG_LINK_TRAN_CAPS & 0xFF) << 8) |
+		UHS2_NATIVE_CMD_READ |
+		UHS2_NATIVE_CMD_PLEN_8B |
+		(UHS2_DEV_CONFIG_LINK_TRAN_CAPS >> 8);
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	cap = cmd.resp[0];
+	card->uhs2_config.link_minor_rev =
+				cap & UHS2_DEV_CONFIG_LT_MINOR_MASK;
+	card->uhs2_config.link_major_rev =
+				(cap >> UHS2_DEV_CONFIG_LT_MAJOR_POS) &
+				UHS2_DEV_CONFIG_LT_MAJOR_MASK;
+	card->uhs2_config.n_fcu =
+				(cap >> UHS2_DEV_CONFIG_N_FCU_POS) &
+				UHS2_DEV_CONFIG_N_FCU_MASK;
+	card->uhs2_config.dev_type =
+				(cap >> UHS2_DEV_CONFIG_DEV_TYPE_POS) &
+				UHS2_DEV_CONFIG_DEV_TYPE_MASK;
+	card->uhs2_config.maxblk_len =
+				(cap >> UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) &
+				UHS2_DEV_CONFIG_MAX_BLK_LEN_MASK;
+
+	cap = cmd.resp[1];
+	card->uhs2_config.n_data_gap =
+				cap & UHS2_DEV_CONFIG_N_DATA_GAP_MASK;
+	if (card->uhs2_config.n_fcu == 0)
+		card->uhs2_config.n_fcu = 256;
+
 	return 0;
 }
 
@@ -95,9 +376,317 @@ static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
  */
 static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
 {
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	u16 header = 0, arg = 0;
+	u32 payload[2];
+	u8 nMinDataGap;
+	u8 plen;
+	int err;
+	u8 resp[5] = {0};
+	u8 resp_len = 5;
+
+	header = UHS2_NATIVE_PACKET |
+		 UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
+	arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+	       UHS2_NATIVE_CMD_WRITE |
+	       UHS2_NATIVE_CMD_PLEN_8B |
+	       (UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+	if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
+	    host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
+		/* Support HD */
+		host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
+		nMinDataGap = 1;
+	} else {
+		/* Only support 2L-FD so far */
+		host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
+		nMinDataGap = 3;
+	}
+
+	/*
+	 * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
+	 * defined in UHS-II addendem Ver1.01 are optional.
+	 */
+	host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+	card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
+
+	plen = 2;
+	payload[0] = card->uhs2_config.n_lanes_set <<
+		     UHS2_DEV_CONFIG_N_LANES_POS;
+	payload[1] = 0;
+	payload[0] = cpu_to_be32(payload[0]);
+	payload[1] = cpu_to_be32(payload[1]);
+
+	/*
+	 * There is no payload because per spec, there should be
+	 * no payload field for read CCMD.
+	 * Plen is set in arg. Per spec, plen for read CCMD
+	 * represents the len of read data which is assigned in payload
+	 * of following RES (p136).
+	 */
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	arg = ((UHS2_DEV_CONFIG_PHY_SET & 0xFF) << 8) |
+	       UHS2_NATIVE_CMD_WRITE |
+	       UHS2_NATIVE_CMD_PLEN_8B |
+	       (UHS2_DEV_CONFIG_PHY_SET >> 8);
+
+	if (host->uhs2_caps.speed_range == UHS2_DEV_CONFIG_PHY_SET_SPEED_B) {
+		host->uhs2_caps.flags |= MMC_UHS2_SPEED_B;
+		card->uhs2_config.speed_range_set =
+					UHS2_DEV_CONFIG_PHY_SET_SPEED_B;
+	} else {
+		card->uhs2_config.speed_range_set = UHS2_DEV_CONFIG_PHY_SET_SPEED_A;
+		host->uhs2_caps.flags &= ~MMC_UHS2_SPEED_B;
+	}
+
+	payload[0] = card->uhs2_config.speed_range_set << UHS2_DEV_CONFIG_PHY_SET_SPEED_POS;
+
+	card->uhs2_config.n_lss_sync_set = (max(card->uhs2_config.n_lss_sync,
+						host->uhs2_caps.n_lss_sync) >> 2) &
+					   UHS2_DEV_CONFIG_N_LSS_SYN_MASK;
+	host->uhs2_caps.n_lss_sync_set = card->uhs2_config.n_lss_sync_set;
+
+	card->uhs2_config.n_lss_dir_set = (max(card->uhs2_config.n_lss_dir,
+					       host->uhs2_caps.n_lss_dir) >> 3) &
+					  UHS2_DEV_CONFIG_N_LSS_DIR_MASK;
+	host->uhs2_caps.n_lss_dir_set = card->uhs2_config.n_lss_dir_set;
+
+	payload[1] = (card->uhs2_config.n_lss_dir_set << UHS2_DEV_CONFIG_N_LSS_DIR_POS) |
+		     card->uhs2_config.n_lss_sync_set;
+	payload[0] = cpu_to_be32(payload[0]);
+	payload[1] = cpu_to_be32(payload[1]);
+
+	resp_len = 4;
+	memset(resp, 0, sizeof(resp));
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, resp, resp_len);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	if ((resp[2] & 0x80)) {
+		pr_err("%s: %s: UHS2 CMD not accepted, resp= 0x%x!\n",
+		       mmc_hostname(host), __func__, resp[2]);
+		return -EIO;
+	}
+
+	arg = ((UHS2_DEV_CONFIG_LINK_TRAN_SET & 0xFF) << 8) |
+		UHS2_NATIVE_CMD_WRITE |
+		UHS2_NATIVE_CMD_PLEN_8B |
+		(UHS2_DEV_CONFIG_LINK_TRAN_SET >> 8);
+
+	plen = 2;
+
+	if (card->uhs2_config.app_type == UHS2_DEV_CONFIG_APP_SD_MEM)
+		card->uhs2_config.maxblk_len_set = UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN;
+	else
+		card->uhs2_config.maxblk_len_set = min(card->uhs2_config.maxblk_len,
+						       host->uhs2_caps.maxblk_len);
+	host->uhs2_caps.maxblk_len_set = card->uhs2_config.maxblk_len_set;
+
+	card->uhs2_config.n_fcu_set = min(card->uhs2_config.n_fcu, host->uhs2_caps.n_fcu);
+	host->uhs2_caps.n_fcu_set = card->uhs2_config.n_fcu_set;
+
+	card->uhs2_config.n_data_gap_set = max(nMinDataGap, card->uhs2_config.n_data_gap);
+	host->uhs2_caps.n_data_gap_set = card->uhs2_config.n_data_gap_set;
+
+	host->uhs2_caps.max_retry_set = 3;
+	card->uhs2_config.max_retry_set = host->uhs2_caps.max_retry_set;
+
+	payload[0] = (card->uhs2_config.maxblk_len_set << UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) |
+		     (card->uhs2_config.max_retry_set << UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS) |
+		     (card->uhs2_config.n_fcu_set << UHS2_DEV_CONFIG_N_FCU_POS);
+	payload[1] = card->uhs2_config.n_data_gap_set;
+	payload[0] = cpu_to_be32(payload[0]);
+	payload[1] = cpu_to_be32(payload[1]);
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+	       UHS2_NATIVE_CMD_WRITE |
+	       UHS2_NATIVE_CMD_PLEN_8B |
+	       (UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+	plen = 2;
+	payload[0] = 0;
+	payload[1] = UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE;
+	payload[0] = cpu_to_be32(payload[0]);
+	payload[1] = cpu_to_be32(payload[1]);
+
+	resp_len = 5;
+	memset(resp, 0, sizeof(resp));
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, resp, resp_len);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	/* Set host Config Setting registers */
+	if (!host->ops->uhs2_host_operation(host, UHS2_SET_CONFIG)) {
+		pr_err("%s: %s: UHS2 SET_CONFIG fail!\n", mmc_hostname(host), __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
+{
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	u16 header = 0, arg = 0;
+	u32 payload[1];
+	u8 plen = 1;
+	int err;
+
+	/* Disable Normal INT */
+	if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
+		pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
+		       mmc_hostname(host), __func__);
+		return -EIO;
+	}
+
+	header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
+
+	arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
+		UHS2_NATIVE_CMD_WRITE |
+		UHS2_NATIVE_CMD_PLEN_4B |
+		(UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
+
+	if (hibernate)
+		payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	/* Check Dormant State in Present */
+	if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
+		pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
+		       mmc_hostname(host), __func__);
+		return -EIO;
+	}
+
+	host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
+
 	return 0;
 }
 
+static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
+{
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	u16 header = 0, arg = 0;
+	int err;
+	int timeout = 100;
+
+	/* Change Speed Range at controller side. */
+	if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
+		pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
+		       mmc_hostname(host), __func__);
+		return -EIO;
+	}
+
+	err = sd_uhs2_go_dormant(host, false, node_id);
+	if (err) {
+		pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return -EIO;
+	}
+
+	/* restore sd clock */
+	mmc_delay(5);
+	host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);
+
+	/* Enable Normal INT */
+	if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
+		pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
+		       mmc_hostname(host), __func__);
+		return -EIO;
+	}
+
+	/*
+	 * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
+	 * switch to Active State
+	 */
+	header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
+	arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+		UHS2_NATIVE_CMD_READ |
+		UHS2_NATIVE_CMD_PLEN_8B |
+		(UHS2_DEV_CONFIG_GEN_SET >> 8);
+	do {
+		sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
+		err = mmc_wait_for_cmd(host, &cmd, 0);
+		if (err) {
+			pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+			       mmc_hostname(host), __func__, err);
+			return -EIO;
+		}
+
+		if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
+			break;
+
+		timeout--;
+		if (timeout == 0) {
+			pr_err("%s: %s: Not switch to Active in 100 ms\n",
+			       mmc_hostname(host), __func__);
+			return -EIO;
+		}
+
+		mmc_delay(1);
+	} while (1);
+
+	return 0;
+}
+
+static int sd_uhs2_get_ro(struct mmc_host *host)
+{
+	int ro;
+
+	/*
+	 * Some systems don't feature a write-protect pin and don't need one.
+	 * E.g. because they only have micro-SD card slot. For those systems
+	 * assume that the SD card is always read-write.
+	 */
+	if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
+		return 0;
+
+	if (!host->ops->get_ro)
+		return -1;
+
+	ro = host->ops->get_ro(host);
+
+	return ro;
+}
+
 /*
  * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
  * commands/requests to be backwards compatible through the legacy SD protocol.
@@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
  */
 static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
 {
+	int err;
+	u32 cid[4];
+	u32 ocr;
+	u32 rocr = 0;
+	int ro;
+
+	WARN_ON(!host->claimed);
+
+	/* Send CMD0 to reset SD card */
+	mmc_go_idle(host);
+
+	/* Send CMD8 to communicate SD interface operation condition */
+	err = mmc_send_if_cond(host, host->ocr_avail);
+	if (err) {
+		pr_err("%s: %s: SEND_IF_COND fail!\n",
+		       mmc_hostname(host), __func__);
+		return err;
+	}
+
+	/*
+	 * Probe SD card working voltage.
+	 */
+	err = mmc_send_app_op_cond(host, 0, &ocr);
+	if (err) {
+		pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
+		       mmc_hostname(host), __func__);
+		return err;
+	}
+	card->ocr = ocr;
+
+	/*
+	 * Some SD cards claims an out of spec VDD voltage range. Let's treat
+	 * these bits as being in-valid and especially also bit7.
+	 */
+	ocr &= ~0x7FFF;
+	rocr = mmc_select_voltage(host, ocr);
+
+	/*
+	 * Some cards have zero value of rocr in UHS-II mode. Assign host's
+	 * ocr value to rocr.
+	 */
+	if (!rocr) {
+		if (host->ocr_avail) {
+			rocr = host->ocr_avail;
+		} else {
+			pr_err("%s: %s: there is no valid OCR.\n",
+			       mmc_hostname(host), __func__);
+			return -EINVAL;
+		}
+	}
+
+	/* Wait SD power on ready */
+	ocr = rocr;
+	err = mmc_send_app_op_cond(host, ocr, &rocr);
+	if (err) {
+		pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
+		       __func__);
+		return err;
+	}
+
+	err = mmc_send_cid(host, cid);
+	if (err) {
+		pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
+		       __func__);
+		return err;
+	}
+	memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+
+	/*
+	 * Call the optional HC's init_card function to handle quirks.
+	 */
+	if (host->ops->init_card)
+		host->ops->init_card(host, card);
+
+	/*
+	 * For native busses:  get card RCA and quit open drain mode.
+	 */
+	err = mmc_send_relative_addr(host, &card->rca);
+	if (err) {
+		pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
+		       __func__);
+		return err;
+	}
+
+	err = mmc_sd_get_csd(card);
+	if (err) {
+		pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
+		       __func__);
+		return err;
+	}
+
+	/*
+	 * Select card, as all following commands rely on that.
+	 */
+	err = mmc_select_card(card);
+	if (err) {
+		pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
+		       __func__);
+		return err;
+	}
+
+	/*
+	 * Check if read-only switch is active.
+	 */
+	ro = sd_uhs2_get_ro(host);
+	if (ro < 0) {
+		pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
+			mmc_hostname(host));
+	} else if (ro > 0) {
+		mmc_card_set_readonly(card);
+	}
+
 	return 0;
 }
 
+static void sd_uhs2_remove(struct mmc_host *host)
+{
+	mmc_remove_card(host->card);
+	host->card = NULL;
+}
+
 /*
  * Allocate the data structure for the mmc_card and run the UHS-II specific
  * initialization sequence.
@@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
 	int err;
 
 	err = sd_uhs2_dev_init(host);
-	if (err)
+	if (err) {
+		pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
 		return err;
+	}
 
 	err = sd_uhs2_enum(host, &node_id);
-	if (err)
+	if (err) {
+		pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
 		return err;
+	}
 
 	card = mmc_alloc_card(host, &sd_type);
 	if (IS_ERR(card))
 		return PTR_ERR(card);
+	host->card = card;
 
 	card->uhs2_config.node_id = node_id;
 	card->type = MMC_TYPE_SD;
@@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
 	if (err)
 		goto err;
 
+	/* Change to Speed Range B if it is supported */
+	if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
+		err = sd_uhs2_change_speed(host, node_id);
+		if (err) {
+			pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
+			       mmc_hostname(host), __func__);
+			return err;
+		}
+	}
+
 	err = sd_uhs2_config_write(host, card);
 	if (err)
 		goto err;
@@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
 	if (err)
 		goto err;
 
-	host->card = card;
 	return 0;
 
 err:
-	mmc_remove_card(card);
+	sd_uhs2_remove(host);
 	return err;
 }
 
-static void sd_uhs2_remove(struct mmc_host *host)
-{
-	mmc_remove_card(host->card);
-	host->card = NULL;
-}
-
 static int sd_uhs2_alive(struct mmc_host *host)
 {
 	return mmc_send_status(host->card, NULL);
@@ -214,6 +929,70 @@ static int sd_uhs2_hw_reset(struct mmc_host *host)
 	return 0;
 }
 
+/*
+ * sd_uhs2_prepare_cmd - prepare for SD command packet
+ * @host:	MMC host
+ * @mrq:	MMC request
+ *
+ * Initialize and fill in a header and a payload of SD command packet.
+ * The caller should allocate uhs2_command in host->cmd->uhs2_cmd in
+ * advance.
+ *
+ * Return:	0 on success, non-zero error on failure
+ */
+int sd_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd;
+	struct uhs2_command *uhs2_cmd;
+	u16 header = 0, arg = 0;
+	u32 *payload;
+	u8 plen = 0;
+
+	cmd = mrq->cmd;
+	header = host->card->uhs2_config.node_id;
+	if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
+		header |= UHS2_PACKET_TYPE_DCMD;
+	else
+		header |= UHS2_PACKET_TYPE_CCMD;
+
+	arg = cmd->opcode << UHS2_SD_CMD_INDEX_POS;
+	if (host->uhs2_caps.flags & MMC_UHS2_APP_CMD) {
+		arg |= UHS2_SD_CMD_APP;
+		host->uhs2_caps.flags &= ~MMC_UHS2_APP_CMD;
+	}
+
+	uhs2_cmd = cmd->uhs2_cmd;
+	payload = uhs2_cmd->payload;
+	plen = 2; /* at the maximum */
+
+	if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC &&
+	    !cmd->uhs2_tmode0_flag) {
+		if (host->uhs2_caps.flags & MMC_UHS2_2L_HD)
+			arg |= UHS2_DCMD_2L_HD_MODE;
+
+		arg |= UHS2_DCMD_LM_TLEN_EXIST;
+
+		if (cmd->data->blocks == 1 &&
+		    cmd->data->blksz != 512 &&
+		    cmd->opcode != MMC_READ_SINGLE_BLOCK &&
+		    cmd->opcode != MMC_WRITE_BLOCK) {
+			arg |= UHS2_DCMD_TLUM_BYTE_MODE;
+			payload[1] = cpu_to_be32(cmd->data->blksz);
+		} else {
+			payload[1] = cpu_to_be32(cmd->data->blocks);
+		}
+	} else {
+		plen = 1;
+	}
+
+	payload[0] = cpu_to_be32(cmd->arg);
+
+	sd_uhs2_cmd_assemble(cmd, uhs2_cmd, header, arg, payload, plen, NULL, 0);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sd_uhs2_prepare_cmd);
+
 static const struct mmc_bus_ops sd_uhs2_ops = {
 	.remove = sd_uhs2_remove,
 	.alive = sd_uhs2_alive,
@@ -251,18 +1030,33 @@ static int sd_uhs2_attach(struct mmc_host *host)
 		goto remove_card;
 
 	mmc_claim_host(host);
+
+	host->ops->uhs2_host_operation(host, UHS2_POST_ATTACH_SD);
+
 	return 0;
 
 remove_card:
-	mmc_remove_card(host->card);
-	host->card = NULL;
+	sd_uhs2_remove(host);
 	mmc_claim_host(host);
-	mmc_detach_bus(host);
+
 err:
+	mmc_detach_bus(host);
 	sd_uhs2_power_off(host);
+	if (host->uhs2_caps.flags & MMC_UHS2_INITIALIZED)
+		host->uhs2_caps.flags &= ~MMC_UHS2_INITIALIZED;
+	host->uhs2_caps.flags &= ~MMC_UHS2_SUPPORT;
 	return err;
 }
 
+/**
+ * mmc_attach_sd_uhs2 - select UHS2 interface
+ * @host: MMC host
+ *
+ * Try to select UHS2 interface and initialize the bus for a given
+ * frequency, @freq.
+ *
+ * Return:	0 on success, non-zero error on failure
+ */
 int mmc_attach_sd_uhs2(struct mmc_host *host)
 {
 	int i, err = 0;
@@ -273,6 +1067,8 @@ int mmc_attach_sd_uhs2(struct mmc_host *host)
 	/* Turn off the legacy SD interface before trying with UHS-II. */
 	mmc_power_off(host);
 
+	host->uhs2_caps.flags |= MMC_UHS2_SUPPORT;
+
 	/*
 	 * Start UHS-II initialization at 52MHz and possibly make a retry at
 	 * 26MHz according to the spec. It's required that the host driver
@@ -280,6 +1076,9 @@ int mmc_attach_sd_uhs2(struct mmc_host *host)
 	 */
 	for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) {
 		host->f_init = sd_uhs2_freqs[i];
+		pr_info("%s: %s: trying to init UHS-II card at %u Hz\n",
+			mmc_hostname(host), __func__, host->f_init);
+
 		err = sd_uhs2_attach(host);
 		if (!err)
 			break;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH V3 7/7] mmc: core: Support UHS-II card access
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
                   ` (5 preceding siblings ...)
  2022-02-22  3:39 ` [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions Jason Lai
@ 2022-02-22  3:39 ` Jason Lai
  2022-03-23 16:23   ` Ulf Hansson
  2022-03-18 13:16 ` [PATCH V3 0/7] Preparations to support SD UHS-II cards Ulf Hansson
  7 siblings, 1 reply; 27+ messages in thread
From: Jason Lai @ 2022-02-22  3:39 UTC (permalink / raw)
  To: ulf.hansson, takahiro.akashi, adrian.hunter
  Cc: linux-mmc, dlunev, ben.chuang, greg.tu, jason.lai, otis.wu,
	jasonlai.genesyslogic

From: Jason Lai <jason.lai@genesyslogic.com.tw>

Embed UHS-II access functionality into the MMC request processing flow.

Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
---
 drivers/mmc/core/core.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index fc3d8d61a97c..d2dcaa64bf27 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -31,6 +31,7 @@
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/sd.h>
 #include <linux/mmc/slot-gpio.h>
+#include <linux/mmc/sd_uhs2.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/mmc.h>
@@ -42,6 +43,7 @@
 #include "host.h"
 #include "sdio_bus.h"
 #include "pwrseq.h"
+#include "sd_uhs2.h"
 
 #include "mmc_ops.h"
 #include "sd_ops.h"
@@ -335,6 +337,8 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
 
 int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 {
+	struct uhs2_command uhs2_cmd;
+	u32 payload[4]; /* for maximum size */
 	int err;
 
 	init_completion(&mrq->cmd_completion);
@@ -352,6 +356,13 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 	if (err)
 		return err;
 
+	if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
+	    host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
+		uhs2_cmd.payload = payload;
+		mrq->cmd->uhs2_cmd = &uhs2_cmd;
+		sd_uhs2_prepare_cmd(host, mrq);
+	}
+
 	led_trigger_event(host->led, LED_FULL);
 	__mmc_start_request(host, mrq);
 
@@ -431,6 +442,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
  */
 int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
 {
+	struct uhs2_command uhs2_cmd;
+	u32 payload[4]; /* for maximum size */
 	int err;
 
 	/*
@@ -451,6 +464,13 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
 	if (err)
 		goto out_err;
 
+	if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
+	    host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
+		uhs2_cmd.payload = payload;
+		mrq->cmd->uhs2_cmd = &uhs2_cmd;
+		sd_uhs2_prepare_cmd(host, mrq);
+	}
+
 	err = host->cqe_ops->cqe_request(host, mrq);
 	if (err)
 		goto out_err;
@@ -899,8 +919,10 @@ static inline void mmc_set_ios(struct mmc_host *host)
  */
 void mmc_set_chip_select(struct mmc_host *host, int mode)
 {
-	host->ios.chip_select = mode;
-	mmc_set_ios(host);
+	if (!(host->uhs2_caps.flags & MMC_UHS2_INITIALIZED)) {
+		host->ios.chip_select = mode;
+		mmc_set_ios(host);
+	}
 }
 
 /*
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 0/7] Preparations to support SD UHS-II cards
  2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
                   ` (6 preceding siblings ...)
  2022-02-22  3:39 ` [PATCH V3 7/7] mmc: core: Support UHS-II card access Jason Lai
@ 2022-03-18 13:16 ` Ulf Hansson
  2022-03-23 13:45   ` Ulf Hansson
  7 siblings, 1 reply; 27+ messages in thread
From: Ulf Hansson @ 2022-03-18 13:16 UTC (permalink / raw)
  To: Jason Lai
  Cc: takahiro.akashi, adrian.hunter, linux-mmc, dlunev, ben.chuang,
	greg.tu, Jason.Lai, otis.wu

Hi Jason,

On Tue, 22 Feb 2022 at 04:39, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
>
> From: Jason Lai <jason.lai@genesyslogic.com.tw>
>
> Series [1] that has been posted by Ulf Hansson which provided some guidance
> and an overall structure.
>
> Series [2] focused on UHS-II card control side to address Ulf's intention
> regarding to "modularising" sd_uhs2.c.
>
> Series [3] is based on series [2] and adopt most of Ulf's comments.
>
> This series is the successor version of post [3], which is base on Ulf's "next" branch 2022/02/14):
> 1. Modify settings in uhs2_config_write().
> 2. Fix some compilation errors.
> 3. Fix some warnings and errors when executing checkpatch.pl.
>
> Kind regards
> Jason Lai
>
> [1]
> https://patchwork.kernel.org/project/linux-mmc/list/?series=438509
>
> [2]
> https://patchwork.kernel.org/project/linux-mmc/list/?series=589827
>
> [3]
> https://patchwork.kernel.org/project/linux-mmc/list/?series=606241
>
> Jason Lai (3):
>   mmc: add UHS-II related definitions in headers
>   mmc: Implement content of UHS-II card initialization functions
>   mmc: core: Support UHS-II card access
>
> Ulf Hansson (4):
>   mmc: core: Cleanup printing of speed mode at card insertion
>   mmc: core: Prepare to support SD UHS-II cards
>   mmc: core: Announce successful insertion of an SD UHS-II card
>   mmc: core: Extend support for mmc regulators with a vqmmc2
>
>  drivers/mmc/core/Makefile    |    2 +-
>  drivers/mmc/core/bus.c       |   38 +-
>  drivers/mmc/core/core.c      |   43 +-
>  drivers/mmc/core/core.h      |    1 +
>  drivers/mmc/core/host.h      |    4 +
>  drivers/mmc/core/regulator.c |   34 ++
>  drivers/mmc/core/sd_uhs2.c   | 1088 ++++++++++++++++++++++++++++++++++
>  drivers/mmc/core/sd_uhs2.h   |   16 +
>  include/linux/mmc/card.h     |   35 ++
>  include/linux/mmc/core.h     |    6 +
>  include/linux/mmc/host.h     |   69 +++
>  include/linux/mmc/sd_uhs2.h  |  198 +++++++
>  12 files changed, 1514 insertions(+), 20 deletions(-)
>  create mode 100644 drivers/mmc/core/sd_uhs2.c
>  create mode 100644 drivers/mmc/core/sd_uhs2.h
>  create mode 100644 include/linux/mmc/sd_uhs2.h
>

I decided to help out a bit and are working on some improvements to
some of the patches from this series.

Although, rather than me reposting new versions of these patches, I
will share a public branch via my mmc git tree within a few days. It
will be based upon the v3 series, but incorporating some new changes
from my side. The changes will be explained as a part of the commit
messages. I will let you know as soon as the branch is available and I
am also reviewing your series, so will provide you with some comments
soon.

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 0/7] Preparations to support SD UHS-II cards
  2022-03-18 13:16 ` [PATCH V3 0/7] Preparations to support SD UHS-II cards Ulf Hansson
@ 2022-03-23 13:45   ` Ulf Hansson
  0 siblings, 0 replies; 27+ messages in thread
From: Ulf Hansson @ 2022-03-23 13:45 UTC (permalink / raw)
  To: Jason Lai
  Cc: takahiro.akashi, adrian.hunter, linux-mmc, dlunev, ben.chuang,
	greg.tu, Jason.Lai, otis.wu

On Fri, 18 Mar 2022 at 14:16, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> Hi Jason,
>
> On Tue, 22 Feb 2022 at 04:39, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> >
> > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> >
> > Series [1] that has been posted by Ulf Hansson which provided some guidance
> > and an overall structure.
> >
> > Series [2] focused on UHS-II card control side to address Ulf's intention
> > regarding to "modularising" sd_uhs2.c.
> >
> > Series [3] is based on series [2] and adopt most of Ulf's comments.
> >
> > This series is the successor version of post [3], which is base on Ulf's "next" branch 2022/02/14):
> > 1. Modify settings in uhs2_config_write().
> > 2. Fix some compilation errors.
> > 3. Fix some warnings and errors when executing checkpatch.pl.
> >
> > Kind regards
> > Jason Lai
> >
> > [1]
> > https://patchwork.kernel.org/project/linux-mmc/list/?series=438509
> >
> > [2]
> > https://patchwork.kernel.org/project/linux-mmc/list/?series=589827
> >
> > [3]
> > https://patchwork.kernel.org/project/linux-mmc/list/?series=606241
> >
> > Jason Lai (3):
> >   mmc: add UHS-II related definitions in headers
> >   mmc: Implement content of UHS-II card initialization functions
> >   mmc: core: Support UHS-II card access
> >
> > Ulf Hansson (4):
> >   mmc: core: Cleanup printing of speed mode at card insertion
> >   mmc: core: Prepare to support SD UHS-II cards
> >   mmc: core: Announce successful insertion of an SD UHS-II card
> >   mmc: core: Extend support for mmc regulators with a vqmmc2
> >
> >  drivers/mmc/core/Makefile    |    2 +-
> >  drivers/mmc/core/bus.c       |   38 +-
> >  drivers/mmc/core/core.c      |   43 +-
> >  drivers/mmc/core/core.h      |    1 +
> >  drivers/mmc/core/host.h      |    4 +
> >  drivers/mmc/core/regulator.c |   34 ++
> >  drivers/mmc/core/sd_uhs2.c   | 1088 ++++++++++++++++++++++++++++++++++
> >  drivers/mmc/core/sd_uhs2.h   |   16 +
> >  include/linux/mmc/card.h     |   35 ++
> >  include/linux/mmc/core.h     |    6 +
> >  include/linux/mmc/host.h     |   69 +++
> >  include/linux/mmc/sd_uhs2.h  |  198 +++++++
> >  12 files changed, 1514 insertions(+), 20 deletions(-)
> >  create mode 100644 drivers/mmc/core/sd_uhs2.c
> >  create mode 100644 drivers/mmc/core/sd_uhs2.h
> >  create mode 100644 include/linux/mmc/sd_uhs2.h
> >
>
> I decided to help out a bit and are working on some improvements to
> some of the patches from this series.
>
> Although, rather than me reposting new versions of these patches, I
> will share a public branch via my mmc git tree within a few days. It
> will be based upon the v3 series, but incorporating some new changes
> from my side. The changes will be explained as a part of the commit
> messages. I will let you know as soon as the branch is available and I
> am also reviewing your series, so will provide you with some comments
> soon.

Hi Jason,

So I have worked on some various improvements for this series and have
amended the patches here in v3. It's mostly cosmetic changes along
with some restructuring of the code. I have described the changes in a
specific section in each of the commit messages (please remove these
parts before posting a new version).

git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git wip_uhs2_v3

Going forward, I will continue to review the series and provide you
with some additional comments.

For the next submission, I suggest you start from the branch I
provided above. I would also appreciate it if you can provide some
information of what has changed for each of the patches in the series,
when you post a new version. This makes it easier to review.

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-02-22  3:39 ` [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions Jason Lai
@ 2022-03-23 16:15   ` Ulf Hansson
  2022-03-24  1:29     ` AKASHI Takahiro
  2022-04-07 10:45     ` Lai Jason
  0 siblings, 2 replies; 27+ messages in thread
From: Ulf Hansson @ 2022-03-23 16:15 UTC (permalink / raw)
  To: Jason Lai
  Cc: takahiro.akashi, adrian.hunter, linux-mmc, dlunev, ben.chuang,
	greg.tu, Jason.Lai, otis.wu

On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
>
> From: Jason Lai <jason.lai@genesyslogic.com.tw>
>
> UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> Part 1 - PHY Initialization:
>          Every host controller may need their own avtivation operation to
>          establish LINK between controller and card. So we add a new member
>          function(uhs2_detect_init) in struct mmc_host_ops for host
>          controller use.
> Part 2 - Card Initialization:
>          This part can be divided into 6 substeps.
>          1. Send UHS-II CCMD DEVICE_INIT to card.
>          2. Send UHS-II CCMD ENUMERATE to card.
>          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
>             of card.
>          4. Host compares capabilities of host controller and card, then
>             write the negotiated values to Setting field in CFG_REG of card
>             through UHS-II Native Write CCMD.
>          5. Switch host controller's clock to Range B if it is supported by
>             both host controller and card.
>          6. Execute legacy SD initialization flow.
> Part 3 - Provide a function to tranaform legacy SD command packet into
>          UHS-II SD-TRAN DCMD packet.
>
> Most of the code added above came from Intel's original patch[3].
>
> [3]
> https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> git-send-email-yi.y.sun@intel.com/
>
> Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> ---
>  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
>  1 file changed, 817 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> index 800957f74632..f1e8e30301eb 100644
> --- a/drivers/mmc/core/sd_uhs2.c
> +++ b/drivers/mmc/core/sd_uhs2.c
> @@ -3,6 +3,7 @@
>   * Copyright (C) 2021 Linaro Ltd
>   *
>   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
>   *
>   * Support for SD UHS-II cards
>   */
> @@ -10,19 +11,31 @@
>
>  #include <linux/mmc/host.h>
>  #include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/sd_uhs2.h>
>
>  #include "core.h"
>  #include "bus.h"
> +#include "card.h"
>  #include "sd.h"
> +#include "sd_ops.h"
>  #include "mmc_ops.h"
> +#include "sd_uhs2.h"
>
>  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
>
>  static int sd_uhs2_set_ios(struct mmc_host *host)
>  {
>         struct mmc_ios *ios = &host->ios;
> +       int err = 0;
>
> -       return host->ops->uhs2_set_ios(host, ios);
> +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> +                ios->timing);
> +
> +       host->ops->set_ios(host, ios);

We discussed using the ->set_ios() callback in a previous version. To
repeat myself, I don't think it's a good idea. UHS-II needs an
entirely different power sequence than the legacy interface(s), hence
I think it's simply cleaner to separate them.

To move forward, I see two options.
1) Use only the ->uhs2_host_operation() ops.
2) Use a combination of the ->uhs2_set_ios() ops and the
->uhs2_host_operation() ops.

Both options work for me. However, perhaps if you could incorporate
the changes done on the host driver at next submission, it becomes
easier for me to understand what makes best sense.

> +
> +       return err;
>  }
>
>  static int sd_uhs2_power_up(struct mmc_host *host)
> @@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
>         sd_uhs2_set_ios(host);
>  }

[...]

>
>  /*
> @@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
>   */
>  static int sd_uhs2_dev_init(struct mmc_host *host)
>  {
> +       struct mmc_command cmd = {0};
> +       struct uhs2_command uhs2_cmd = {};
> +       u32 cnt;
> +       u32 dap, gap, resp_gap;
> +       u16 header = 0, arg = 0;

No need to initiate these.

> +       u32 payload[1];

u32?

> +       u8 plen = 1;
> +       u8 gd = 0, cf = 1;
> +       u8 resp[6] = {0};
> +       u8 resp_len = 6;

Many of these variables are just constant numbers. If it makes sense
to add definitions for them, then please do that instead. If not, just
give the value directly in the code.

For example: plen = 1; (I assume that is payload length). This can
just be given as an in-parameter to sd_uhs2_cmd_assemble(), without
further explanation.

The point is, sd_uhs2_cmd_assemble() should have a well described
description of its in-parameters, so no need for further descriptions,
I think.

This comment applies to all the new code/functions that are added in
the $subject patch. Please go through all of the code and fix this.


> +       int err;
> +
> +       dap = host->uhs2_caps.dap;
> +       gap = host->uhs2_caps.gap;
> +
> +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> +              UHS2_NATIVE_CMD_WRITE |
> +              UHS2_NATIVE_CMD_PLEN_4B |
> +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> +
> +       /*
> +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> +        * 1 second.
> +        */
> +       cmd.busy_timeout = 1000;
> +
> +       /*
> +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> +        * When the number of the DEVICE_INIT commands is reach to
> +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> +        * and regard it as an error.
> +        */
> +       for (cnt = 0; cnt < 30; cnt++) {
> +               payload[0] = ((dap & 0xF) << 12) |
> +                             (cf << 11)         |
> +                             ((gd & 0xF) << 4)  |
> +                             (gap & 0xF);

To me, it looks like the payload data deserves to be explained a bit.
Perhaps you can add a comment explaining what pieces it consists of so
this becomes more clear?

> +
> +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
> +
> +               err = mmc_wait_for_cmd(host, &cmd, 0);
> +
> +               if (err) {
> +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> +                              mmc_hostname(host), __func__, err);
> +                       return -EIO;

Why do you override the original error code that was returned from
mmc_wait_for_cmd()?

Normally it's preferred to keep the error code, unless there is good
reason not to.

Again, I won't add more comments like this in the code from the
$subject patch. But please go through it all to avoid this kind of
thing.

> +               }
> +
> +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> +                              mmc_hostname(host));
> +                       return -EIO;
> +               }
> +
> +               if (resp[5] & 0x8) {
> +                       host->uhs2_caps.group_desc = gd;
> +                       break;

I suggest you do a return 0 here. In this way you can skip the check
"if (cnt == 30)" below and just return an error code instead.

> +               }
> +               resp_gap = resp[4] & 0x0F;
> +               if (gap == resp_gap)
> +                       gd++;
> +       }
> +       if (cnt == 30) {
> +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> +                      mmc_hostname(host));
> +               return -EIO;
> +       }
> +
>         return 0;
>  }
>

>  static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
>  {
> +       struct mmc_command cmd = {0};
> +       struct uhs2_command uhs2_cmd = {};
> +       u16 header = 0, arg = 0;
> +       u32 cap;
> +       int err;
> +
> +       header = UHS2_NATIVE_PACKET |
> +                UHS2_PACKET_TYPE_CCMD |
> +                card->uhs2_config.node_id;
> +       arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
> +              UHS2_NATIVE_CMD_READ |
> +              UHS2_NATIVE_CMD_PLEN_4B |
> +              (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
> +
> +       /* There is no payload because per spec, there should be
> +        * no payload field for read CCMD.
> +        * Plen is set in arg. Per spec, plen for read CCMD
> +        * represents the len of read data which is assigned in payload
> +        * of following RES (p136).
> +        */
> +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);

We are reading the configuration data here and onwards, piece by
piece. Perhaps if you can add a small comment about each piece we are
reading, before each call to mmc_wait_for_cmd(), that can help to
easier understand what goes on.

[...]

>  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
>  {
> +       struct mmc_command cmd = {0};
> +       struct uhs2_command uhs2_cmd = {};
> +       u16 header = 0, arg = 0;
> +       u32 payload[2];
> +       u8 nMinDataGap;
> +       u8 plen;
> +       int err;
> +       u8 resp[5] = {0};
> +       u8 resp_len = 5;
> +
> +       header = UHS2_NATIVE_PACKET |
> +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> +              UHS2_NATIVE_CMD_WRITE |
> +              UHS2_NATIVE_CMD_PLEN_8B |
> +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> +
> +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> +               /* Support HD */
> +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;

How is the uhs2_caps.flags field intended to be used? To me it looks
like a way for the mmc core to exchange status/configuration
information about the initialization process of the card, with the mmc
host driver. Perhaps there is more too. Is that correct?

If so, I think it looks quite similar for what we have in the struct
mmc_ios, for the legacy interface(s). I am not saying we should use
that, just trying to understand what would suit best here.

> +               nMinDataGap = 1;
> +       } else {
> +               /* Only support 2L-FD so far */
> +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> +               nMinDataGap = 3;
> +       }
> +
> +       /*
> +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> +        * defined in UHS-II addendem Ver1.01 are optional.
> +        */
> +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;

[...]

> +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> +{

Looks like the in-parameter "hibernate" is superfluous, as it's always
set to "false" by the caller.

> +       struct mmc_command cmd = {0};
> +       struct uhs2_command uhs2_cmd = {};
> +       u16 header = 0, arg = 0;
> +       u32 payload[1];
> +       u8 plen = 1;
> +       int err;
> +
> +       /* Disable Normal INT */
> +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> +                      mmc_hostname(host), __func__);
> +               return -EIO;
> +       }
> +
> +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> +
> +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> +               UHS2_NATIVE_CMD_WRITE |
> +               UHS2_NATIVE_CMD_PLEN_4B |
> +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> +
> +       if (hibernate)
> +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> +
> +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> +
> +       err = mmc_wait_for_cmd(host, &cmd, 0);
> +       if (err) {
> +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> +                      mmc_hostname(host), __func__, err);
> +               return -EIO;
> +       }
> +
> +       /* Check Dormant State in Present */
> +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> +                      mmc_hostname(host), __func__);
> +               return -EIO;
> +       }
> +
> +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> +
>         return 0;
>  }
>
> +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> +{
> +       struct mmc_command cmd = {0};
> +       struct uhs2_command uhs2_cmd = {};
> +       u16 header = 0, arg = 0;
> +       int err;
> +       int timeout = 100;
> +
> +       /* Change Speed Range at controller side. */
> +       if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
> +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
> +                      mmc_hostname(host), __func__);
> +               return -EIO;
> +       }
> +
> +       err = sd_uhs2_go_dormant(host, false, node_id);
> +       if (err) {
> +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> +                      mmc_hostname(host), __func__, err);
> +               return -EIO;
> +       }
> +
> +       /* restore sd clock */
> +       mmc_delay(5);
> +       host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);

I think the code can be a bit better structured here. More precisely,
since sd_uhs2_go_dormant() is the one that calls
->uhs2_host_operation(host, UHS2_DISABLE_INT) and
->uhs2_host_operation(host, UHS2_DISABLE_CLK), it's then up to
sd_uhs2_change_speed() to restore these changes.

To me, it would be more clear if both enabling and disabling of the
clock /interrupt are managed in sd_uhs2_change_speed().

> +
> +       /* Enable Normal INT */
> +       if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
> +               pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
> +                      mmc_hostname(host), __func__);
> +               return -EIO;
> +       }
> +
> +       /*
> +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> +        * switch to Active State
> +        */
> +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> +               UHS2_NATIVE_CMD_READ |
> +               UHS2_NATIVE_CMD_PLEN_8B |
> +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> +       do {
> +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> +               err = mmc_wait_for_cmd(host, &cmd, 0);
> +               if (err) {
> +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> +                              mmc_hostname(host), __func__, err);
> +                       return -EIO;
> +               }
> +
> +               if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> +                       break;
> +
> +               timeout--;
> +               if (timeout == 0) {
> +                       pr_err("%s: %s: Not switch to Active in 100 ms\n",
> +                              mmc_hostname(host), __func__);
> +                       return -EIO;
> +               }
> +
> +               mmc_delay(1);
> +       } while (1);

We really want to avoid these kinds of polling loops, for several
reasons. Please convert into using __mmc_poll_for_busy() instead.

> +
> +       return 0;
> +}
> +
> +static int sd_uhs2_get_ro(struct mmc_host *host)
> +{
> +       int ro;
> +
> +       /*
> +        * Some systems don't feature a write-protect pin and don't need one.
> +        * E.g. because they only have micro-SD card slot. For those systems
> +        * assume that the SD card is always read-write.
> +        */
> +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> +               return 0;
> +
> +       if (!host->ops->get_ro)
> +               return -1;
> +
> +       ro = host->ops->get_ro(host);
> +
> +       return ro;

This can be replaced with mmc_sd_get_ro(). Let's avoid the open coding
and make that function being shared instead.

> +}
> +
>  /*
>   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
>   * commands/requests to be backwards compatible through the legacy SD protocol.
> @@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
>   */
>  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
>  {
> +       int err;
> +       u32 cid[4];
> +       u32 ocr;
> +       u32 rocr = 0;
> +       int ro;
> +
> +       WARN_ON(!host->claimed);

Drop this, it's an internal function, we should know that the host is
claimed before calling sd_uhs2_legacy_init().

> +
> +       /* Send CMD0 to reset SD card */
> +       mmc_go_idle(host);
> +
> +       /* Send CMD8 to communicate SD interface operation condition */
> +       err = mmc_send_if_cond(host, host->ocr_avail);
> +       if (err) {
> +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> +                      mmc_hostname(host), __func__);

Please drop these prints for every command/operation that fails. We
already have trace/debug options for commands/requests.

This applies to all the below code as well (perhaps there are few
cases not covered by the existing trace/debug support, those may be
converted to pr_debug().

> +               return err;
> +       }
> +
> +       /*
> +        * Probe SD card working voltage.
> +        */
> +       err = mmc_send_app_op_cond(host, 0, &ocr);
> +       if (err) {
> +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> +                      mmc_hostname(host), __func__);
> +               return err;
> +       }
> +       card->ocr = ocr;
> +
> +       /*
> +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> +        * these bits as being in-valid and especially also bit7.
> +        */
> +       ocr &= ~0x7FFF;
> +       rocr = mmc_select_voltage(host, ocr);

If the host has MMC_CAP2_FULL_PWR_CYCLE set, mmc_select_voltage() may
end up calling mmc_power_cycle(). This is not going to work for
UHS-II.

Either we need to modify mmc_select_voltage() so it becomes aware that
it can be called for UHS-II initialization, allowing it to avoid the
path to mmc_power_cycle() - or simply open code the part from
mmc_select_voltage() for UHS-II here. I think I prefer the latter.

> +
> +       /*
> +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> +        * ocr value to rocr.
> +        */
> +       if (!rocr) {
> +               if (host->ocr_avail) {
> +                       rocr = host->ocr_avail;

host->ocr_avail should really be checked in when the host driver calls
mmc_add_host(). It must not be zero, then we should let mmc_add_host()
return an error code. I look into this and send a patch for this
separately.

In other words, you should not need to check it here, but just trust that's set.

> +               } else {
> +                       pr_err("%s: %s: there is no valid OCR.\n",
> +                              mmc_hostname(host), __func__);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       /* Wait SD power on ready */
> +       ocr = rocr;
> +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> +       if (err) {
> +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
> +                      __func__);
> +               return err;
> +       }
> +
> +       err = mmc_send_cid(host, cid);
> +       if (err) {
> +               pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
> +                      __func__);
> +               return err;
> +       }
> +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> +
> +       /*
> +        * Call the optional HC's init_card function to handle quirks.
> +        */
> +       if (host->ops->init_card)
> +               host->ops->init_card(host, card);

This can be removed, as it's only for the legacy interface, I think.

> +
> +       /*
> +        * For native busses:  get card RCA and quit open drain mode.
> +        */
> +       err = mmc_send_relative_addr(host, &card->rca);
> +       if (err) {
> +               pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
> +                      __func__);
> +               return err;
> +       }
> +
> +       err = mmc_sd_get_csd(card);
> +       if (err) {
> +               pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
> +                      __func__);
> +               return err;
> +       }
> +
> +       /*
> +        * Select card, as all following commands rely on that.
> +        */
> +       err = mmc_select_card(card);
> +       if (err) {
> +               pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
> +                      __func__);
> +               return err;
> +       }
> +
> +       /*
> +        * Check if read-only switch is active.
> +        */
> +       ro = sd_uhs2_get_ro(host);
> +       if (ro < 0) {
> +               pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> +                       mmc_hostname(host));
> +       } else if (ro > 0) {
> +               mmc_card_set_readonly(card);
> +       }
> +
>         return 0;
>  }
>
> +static void sd_uhs2_remove(struct mmc_host *host)
> +{
> +       mmc_remove_card(host->card);
> +       host->card = NULL;
> +}
> +
>  /*
>   * Allocate the data structure for the mmc_card and run the UHS-II specific
>   * initialization sequence.
> @@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
>         int err;
>
>         err = sd_uhs2_dev_init(host);
> -       if (err)
> +       if (err) {
> +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
>                 return err;
> +       }
>
>         err = sd_uhs2_enum(host, &node_id);
> -       if (err)
> +       if (err) {
> +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
>                 return err;
> +       }
>
>         card = mmc_alloc_card(host, &sd_type);
>         if (IS_ERR(card))
>                 return PTR_ERR(card);
> +       host->card = card;
>
>         card->uhs2_config.node_id = node_id;
>         card->type = MMC_TYPE_SD;
> @@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
>         if (err)
>                 goto err;
>
> +       /* Change to Speed Range B if it is supported */
> +       if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
> +               err = sd_uhs2_change_speed(host, node_id);
> +               if (err) {
> +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> +                              mmc_hostname(host), __func__);
> +                       return err;
> +               }
> +       }
> +
>         err = sd_uhs2_config_write(host, card);
>         if (err)
>                 goto err;
> @@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
>         if (err)
>                 goto err;
>
> -       host->card = card;
>         return 0;
>
>  err:
> -       mmc_remove_card(card);
> +       sd_uhs2_remove(host);
>         return err;
>  }

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 7/7] mmc: core: Support UHS-II card access
  2022-02-22  3:39 ` [PATCH V3 7/7] mmc: core: Support UHS-II card access Jason Lai
@ 2022-03-23 16:23   ` Ulf Hansson
  2022-04-07 11:00     ` Lai Jason
  0 siblings, 1 reply; 27+ messages in thread
From: Ulf Hansson @ 2022-03-23 16:23 UTC (permalink / raw)
  To: Jason Lai
  Cc: takahiro.akashi, adrian.hunter, linux-mmc, dlunev, ben.chuang,
	greg.tu, Jason.Lai, otis.wu

On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
>
> From: Jason Lai <jason.lai@genesyslogic.com.tw>
>
> Embed UHS-II access functionality into the MMC request processing flow.
>
> Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> ---
>  drivers/mmc/core/core.c | 26 ++++++++++++++++++++++++--
>  1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index fc3d8d61a97c..d2dcaa64bf27 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -31,6 +31,7 @@
>  #include <linux/mmc/mmc.h>
>  #include <linux/mmc/sd.h>
>  #include <linux/mmc/slot-gpio.h>
> +#include <linux/mmc/sd_uhs2.h>
>
>  #define CREATE_TRACE_POINTS
>  #include <trace/events/mmc.h>
> @@ -42,6 +43,7 @@
>  #include "host.h"
>  #include "sdio_bus.h"
>  #include "pwrseq.h"
> +#include "sd_uhs2.h"
>
>  #include "mmc_ops.h"
>  #include "sd_ops.h"
> @@ -335,6 +337,8 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
>
>  int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
>  {
> +       struct uhs2_command uhs2_cmd;
> +       u32 payload[4]; /* for maximum size */
>         int err;
>
>         init_completion(&mrq->cmd_completion);
> @@ -352,6 +356,13 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
>         if (err)
>                 return err;
>
> +       if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
> +           host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
> +               uhs2_cmd.payload = payload;
> +               mrq->cmd->uhs2_cmd = &uhs2_cmd;
> +               sd_uhs2_prepare_cmd(host, mrq);
> +       }
> +
>         led_trigger_event(host->led, LED_FULL);
>         __mmc_start_request(host, mrq);
>
> @@ -431,6 +442,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
>   */
>  int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
>  {
> +       struct uhs2_command uhs2_cmd;
> +       u32 payload[4]; /* for maximum size */
>         int err;
>
>         /*
> @@ -451,6 +464,13 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
>         if (err)
>                 goto out_err;
>
> +       if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
> +           host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
> +               uhs2_cmd.payload = payload;
> +               mrq->cmd->uhs2_cmd = &uhs2_cmd;
> +               sd_uhs2_prepare_cmd(host, mrq);
> +       }
> +
>         err = host->cqe_ops->cqe_request(host, mrq);
>         if (err)
>                 goto out_err;
> @@ -899,8 +919,10 @@ static inline void mmc_set_ios(struct mmc_host *host)
>   */
>  void mmc_set_chip_select(struct mmc_host *host, int mode)
>  {
> -       host->ios.chip_select = mode;
> -       mmc_set_ios(host);
> +       if (!(host->uhs2_caps.flags & MMC_UHS2_INITIALIZED)) {
> +               host->ios.chip_select = mode;
> +               mmc_set_ios(host);
> +       }

As I stated for patch6, rather than having these hacks spread out into
the legacy code, we must not allow the ->set_ios() ops to be invoked
when UHS-II is being used/initialized.

>  }
>

Moreover, I think $subject patch should be squashed into patch6. There
is really no reason to have them split up like this. Or is there?

I have now walked through the series - and it seems like there are a
few more pieces that we need to sort out, but it's certainly getting
better and better.

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-03-23 16:15   ` Ulf Hansson
@ 2022-03-24  1:29     ` AKASHI Takahiro
  2022-03-24  6:09       ` Lai Jason
  2022-03-24 10:22       ` Ulf Hansson
  2022-04-07 10:45     ` Lai Jason
  1 sibling, 2 replies; 27+ messages in thread
From: AKASHI Takahiro @ 2022-03-24  1:29 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Jason Lai, adrian.hunter, linux-mmc, dlunev, ben.chuang, greg.tu,
	Jason.Lai, otis.wu

On Wed, Mar 23, 2022 at 05:15:59PM +0100, Ulf Hansson wrote:
> On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> >
> > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> >
> > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > Part 1 - PHY Initialization:
> >          Every host controller may need their own avtivation operation to
> >          establish LINK between controller and card. So we add a new member
> >          function(uhs2_detect_init) in struct mmc_host_ops for host
> >          controller use.
> > Part 2 - Card Initialization:
> >          This part can be divided into 6 substeps.
> >          1. Send UHS-II CCMD DEVICE_INIT to card.
> >          2. Send UHS-II CCMD ENUMERATE to card.
> >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> >             of card.
> >          4. Host compares capabilities of host controller and card, then
> >             write the negotiated values to Setting field in CFG_REG of card
> >             through UHS-II Native Write CCMD.
> >          5. Switch host controller's clock to Range B if it is supported by
> >             both host controller and card.
> >          6. Execute legacy SD initialization flow.
> > Part 3 - Provide a function to tranaform legacy SD command packet into
> >          UHS-II SD-TRAN DCMD packet.
> >
> > Most of the code added above came from Intel's original patch[3].
> >
> > [3]
> > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > git-send-email-yi.y.sun@intel.com/

To honor the original work, we should add Intel's copyright notice here
as I did before.

-Takahiro Akashi


> > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > ---
> >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 817 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > index 800957f74632..f1e8e30301eb 100644
> > --- a/drivers/mmc/core/sd_uhs2.c
> > +++ b/drivers/mmc/core/sd_uhs2.c
> > @@ -3,6 +3,7 @@
> >   * Copyright (C) 2021 Linaro Ltd
> >   *
> >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> >   *
> >   * Support for SD UHS-II cards
> >   */
> > @@ -10,19 +11,31 @@
> >
> >  #include <linux/mmc/host.h>
> >  #include <linux/mmc/card.h>
> > +#include <linux/mmc/mmc.h>
> > +#include <linux/mmc/sd_uhs2.h>
> >
> >  #include "core.h"
> >  #include "bus.h"
> > +#include "card.h"
> >  #include "sd.h"
> > +#include "sd_ops.h"
> >  #include "mmc_ops.h"
> > +#include "sd_uhs2.h"
> >
> >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> >
> >  static int sd_uhs2_set_ios(struct mmc_host *host)
> >  {
> >         struct mmc_ios *ios = &host->ios;
> > +       int err = 0;
> >
> > -       return host->ops->uhs2_set_ios(host, ios);
> > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > +                ios->timing);
> > +
> > +       host->ops->set_ios(host, ios);
> 
> We discussed using the ->set_ios() callback in a previous version. To
> repeat myself, I don't think it's a good idea. UHS-II needs an
> entirely different power sequence than the legacy interface(s), hence
> I think it's simply cleaner to separate them.
> 
> To move forward, I see two options.
> 1) Use only the ->uhs2_host_operation() ops.
> 2) Use a combination of the ->uhs2_set_ios() ops and the
> ->uhs2_host_operation() ops.
> 
> Both options work for me. However, perhaps if you could incorporate
> the changes done on the host driver at next submission, it becomes
> easier for me to understand what makes best sense.
> 
> > +
> > +       return err;
> >  }
> >
> >  static int sd_uhs2_power_up(struct mmc_host *host)
> > @@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> >         sd_uhs2_set_ios(host);
> >  }
> 
> [...]
> 
> >
> >  /*
> > @@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> >   */
> >  static int sd_uhs2_dev_init(struct mmc_host *host)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u32 cnt;
> > +       u32 dap, gap, resp_gap;
> > +       u16 header = 0, arg = 0;
> 
> No need to initiate these.
> 
> > +       u32 payload[1];
> 
> u32?
> 
> > +       u8 plen = 1;
> > +       u8 gd = 0, cf = 1;
> > +       u8 resp[6] = {0};
> > +       u8 resp_len = 6;
> 
> Many of these variables are just constant numbers. If it makes sense
> to add definitions for them, then please do that instead. If not, just
> give the value directly in the code.
> 
> For example: plen = 1; (I assume that is payload length). This can
> just be given as an in-parameter to sd_uhs2_cmd_assemble(), without
> further explanation.
> 
> The point is, sd_uhs2_cmd_assemble() should have a well described
> description of its in-parameters, so no need for further descriptions,
> I think.
> 
> This comment applies to all the new code/functions that are added in
> the $subject patch. Please go through all of the code and fix this.
> 
> 
> > +       int err;
> > +
> > +       dap = host->uhs2_caps.dap;
> > +       gap = host->uhs2_caps.gap;
> > +
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> > +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_4B |
> > +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> > +
> > +       /*
> > +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > +        * 1 second.
> > +        */
> > +       cmd.busy_timeout = 1000;
> > +
> > +       /*
> > +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > +        * When the number of the DEVICE_INIT commands is reach to
> > +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > +        * and regard it as an error.
> > +        */
> > +       for (cnt = 0; cnt < 30; cnt++) {
> > +               payload[0] = ((dap & 0xF) << 12) |
> > +                             (cf << 11)         |
> > +                             ((gd & 0xF) << 4)  |
> > +                             (gap & 0xF);
> 
> To me, it looks like the payload data deserves to be explained a bit.
> Perhaps you can add a comment explaining what pieces it consists of so
> this becomes more clear?
> 
> > +
> > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
> > +
> > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > +
> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                              mmc_hostname(host), __func__, err);
> > +                       return -EIO;
> 
> Why do you override the original error code that was returned from
> mmc_wait_for_cmd()?
> 
> Normally it's preferred to keep the error code, unless there is good
> reason not to.
> 
> Again, I won't add more comments like this in the code from the
> $subject patch. But please go through it all to avoid this kind of
> thing.
> 
> > +               }
> > +
> > +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> > +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> > +                              mmc_hostname(host));
> > +                       return -EIO;
> > +               }
> > +
> > +               if (resp[5] & 0x8) {
> > +                       host->uhs2_caps.group_desc = gd;
> > +                       break;
> 
> I suggest you do a return 0 here. In this way you can skip the check
> "if (cnt == 30)" below and just return an error code instead.
> 
> > +               }
> > +               resp_gap = resp[4] & 0x0F;
> > +               if (gap == resp_gap)
> > +                       gd++;
> > +       }
> > +       if (cnt == 30) {
> > +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> > +                      mmc_hostname(host));
> > +               return -EIO;
> > +       }
> > +
> >         return 0;
> >  }
> >
> 
> >  static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       u32 cap;
> > +       int err;
> > +
> > +       header = UHS2_NATIVE_PACKET |
> > +                UHS2_PACKET_TYPE_CCMD |
> > +                card->uhs2_config.node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_READ |
> > +              UHS2_NATIVE_CMD_PLEN_4B |
> > +              (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
> > +
> > +       /* There is no payload because per spec, there should be
> > +        * no payload field for read CCMD.
> > +        * Plen is set in arg. Per spec, plen for read CCMD
> > +        * represents the len of read data which is assigned in payload
> > +        * of following RES (p136).
> > +        */
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> 
> We are reading the configuration data here and onwards, piece by
> piece. Perhaps if you can add a small comment about each piece we are
> reading, before each call to mmc_wait_for_cmd(), that can help to
> easier understand what goes on.
> 
> [...]
> 
> >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       u32 payload[2];
> > +       u8 nMinDataGap;
> > +       u8 plen;
> > +       int err;
> > +       u8 resp[5] = {0};
> > +       u8 resp_len = 5;
> > +
> > +       header = UHS2_NATIVE_PACKET |
> > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_8B |
> > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +
> > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > +               /* Support HD */
> > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> 
> How is the uhs2_caps.flags field intended to be used? To me it looks
> like a way for the mmc core to exchange status/configuration
> information about the initialization process of the card, with the mmc
> host driver. Perhaps there is more too. Is that correct?
> 
> If so, I think it looks quite similar for what we have in the struct
> mmc_ios, for the legacy interface(s). I am not saying we should use
> that, just trying to understand what would suit best here.
> 
> > +               nMinDataGap = 1;
> > +       } else {
> > +               /* Only support 2L-FD so far */
> > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > +               nMinDataGap = 3;
> > +       }
> > +
> > +       /*
> > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > +        * defined in UHS-II addendem Ver1.01 are optional.
> > +        */
> > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> 
> [...]
> 
> > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > +{
> 
> Looks like the in-parameter "hibernate" is superfluous, as it's always
> set to "false" by the caller.
> 
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       u32 payload[1];
> > +       u8 plen = 1;
> > +       int err;
> > +
> > +       /* Disable Normal INT */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > +
> > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > +               UHS2_NATIVE_CMD_WRITE |
> > +               UHS2_NATIVE_CMD_PLEN_4B |
> > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > +
> > +       if (hibernate)
> > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > +
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > +
> > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return -EIO;
> > +       }
> > +
> > +       /* Check Dormant State in Present */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > +
> >         return 0;
> >  }
> >
> > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > +{
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       int err;
> > +       int timeout = 100;
> > +
> > +       /* Change Speed Range at controller side. */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
> > +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       err = sd_uhs2_go_dormant(host, false, node_id);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return -EIO;
> > +       }
> > +
> > +       /* restore sd clock */
> > +       mmc_delay(5);
> > +       host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);
> 
> I think the code can be a bit better structured here. More precisely,
> since sd_uhs2_go_dormant() is the one that calls
> ->uhs2_host_operation(host, UHS2_DISABLE_INT) and
> ->uhs2_host_operation(host, UHS2_DISABLE_CLK), it's then up to
> sd_uhs2_change_speed() to restore these changes.
> 
> To me, it would be more clear if both enabling and disabling of the
> clock /interrupt are managed in sd_uhs2_change_speed().
> 
> > +
> > +       /* Enable Normal INT */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
> > +               pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       /*
> > +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> > +        * switch to Active State
> > +        */
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +               UHS2_NATIVE_CMD_READ |
> > +               UHS2_NATIVE_CMD_PLEN_8B |
> > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +       do {
> > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                              mmc_hostname(host), __func__, err);
> > +                       return -EIO;
> > +               }
> > +
> > +               if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> > +                       break;
> > +
> > +               timeout--;
> > +               if (timeout == 0) {
> > +                       pr_err("%s: %s: Not switch to Active in 100 ms\n",
> > +                              mmc_hostname(host), __func__);
> > +                       return -EIO;
> > +               }
> > +
> > +               mmc_delay(1);
> > +       } while (1);
> 
> We really want to avoid these kinds of polling loops, for several
> reasons. Please convert into using __mmc_poll_for_busy() instead.
> 
> > +
> > +       return 0;
> > +}
> > +
> > +static int sd_uhs2_get_ro(struct mmc_host *host)
> > +{
> > +       int ro;
> > +
> > +       /*
> > +        * Some systems don't feature a write-protect pin and don't need one.
> > +        * E.g. because they only have micro-SD card slot. For those systems
> > +        * assume that the SD card is always read-write.
> > +        */
> > +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> > +               return 0;
> > +
> > +       if (!host->ops->get_ro)
> > +               return -1;
> > +
> > +       ro = host->ops->get_ro(host);
> > +
> > +       return ro;
> 
> This can be replaced with mmc_sd_get_ro(). Let's avoid the open coding
> and make that function being shared instead.
> 
> > +}
> > +
> >  /*
> >   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> >   * commands/requests to be backwards compatible through the legacy SD protocol.
> > @@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> >   */
> >  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> >  {
> > +       int err;
> > +       u32 cid[4];
> > +       u32 ocr;
> > +       u32 rocr = 0;
> > +       int ro;
> > +
> > +       WARN_ON(!host->claimed);
> 
> Drop this, it's an internal function, we should know that the host is
> claimed before calling sd_uhs2_legacy_init().
> 
> > +
> > +       /* Send CMD0 to reset SD card */
> > +       mmc_go_idle(host);
> > +
> > +       /* Send CMD8 to communicate SD interface operation condition */
> > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > +       if (err) {
> > +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> > +                      mmc_hostname(host), __func__);
> 
> Please drop these prints for every command/operation that fails. We
> already have trace/debug options for commands/requests.
> 
> This applies to all the below code as well (perhaps there are few
> cases not covered by the existing trace/debug support, those may be
> converted to pr_debug().
> 
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Probe SD card working voltage.
> > +        */
> > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return err;
> > +       }
> > +       card->ocr = ocr;
> > +
> > +       /*
> > +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > +        * these bits as being in-valid and especially also bit7.
> > +        */
> > +       ocr &= ~0x7FFF;
> > +       rocr = mmc_select_voltage(host, ocr);
> 
> If the host has MMC_CAP2_FULL_PWR_CYCLE set, mmc_select_voltage() may
> end up calling mmc_power_cycle(). This is not going to work for
> UHS-II.
> 
> Either we need to modify mmc_select_voltage() so it becomes aware that
> it can be called for UHS-II initialization, allowing it to avoid the
> path to mmc_power_cycle() - or simply open code the part from
> mmc_select_voltage() for UHS-II here. I think I prefer the latter.
> 
> > +
> > +       /*
> > +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > +        * ocr value to rocr.
> > +        */
> > +       if (!rocr) {
> > +               if (host->ocr_avail) {
> > +                       rocr = host->ocr_avail;
> 
> host->ocr_avail should really be checked in when the host driver calls
> mmc_add_host(). It must not be zero, then we should let mmc_add_host()
> return an error code. I look into this and send a patch for this
> separately.
> 
> In other words, you should not need to check it here, but just trust that's set.
> 
> > +               } else {
> > +                       pr_err("%s: %s: there is no valid OCR.\n",
> > +                              mmc_hostname(host), __func__);
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> > +       /* Wait SD power on ready */
> > +       ocr = rocr;
> > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       err = mmc_send_cid(host, cid);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > +
> > +       /*
> > +        * Call the optional HC's init_card function to handle quirks.
> > +        */
> > +       if (host->ops->init_card)
> > +               host->ops->init_card(host, card);
> 
> This can be removed, as it's only for the legacy interface, I think.
> 
> > +
> > +       /*
> > +        * For native busses:  get card RCA and quit open drain mode.
> > +        */
> > +       err = mmc_send_relative_addr(host, &card->rca);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       err = mmc_sd_get_csd(card);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Select card, as all following commands rely on that.
> > +        */
> > +       err = mmc_select_card(card);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Check if read-only switch is active.
> > +        */
> > +       ro = sd_uhs2_get_ro(host);
> > +       if (ro < 0) {
> > +               pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> > +                       mmc_hostname(host));
> > +       } else if (ro > 0) {
> > +               mmc_card_set_readonly(card);
> > +       }
> > +
> >         return 0;
> >  }
> >
> > +static void sd_uhs2_remove(struct mmc_host *host)
> > +{
> > +       mmc_remove_card(host->card);
> > +       host->card = NULL;
> > +}
> > +
> >  /*
> >   * Allocate the data structure for the mmc_card and run the UHS-II specific
> >   * initialization sequence.
> > @@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         int err;
> >
> >         err = sd_uhs2_dev_init(host);
> > -       if (err)
> > +       if (err) {
> > +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
> >                 return err;
> > +       }
> >
> >         err = sd_uhs2_enum(host, &node_id);
> > -       if (err)
> > +       if (err) {
> > +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
> >                 return err;
> > +       }
> >
> >         card = mmc_alloc_card(host, &sd_type);
> >         if (IS_ERR(card))
> >                 return PTR_ERR(card);
> > +       host->card = card;
> >
> >         card->uhs2_config.node_id = node_id;
> >         card->type = MMC_TYPE_SD;
> > @@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > +       /* Change to Speed Range B if it is supported */
> > +       if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
> > +               err = sd_uhs2_change_speed(host, node_id);
> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> > +                              mmc_hostname(host), __func__);
> > +                       return err;
> > +               }
> > +       }
> > +
> >         err = sd_uhs2_config_write(host, card);
> >         if (err)
> >                 goto err;
> > @@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > -       host->card = card;
> >         return 0;
> >
> >  err:
> > -       mmc_remove_card(card);
> > +       sd_uhs2_remove(host);
> >         return err;
> >  }
> 
> [...]
> 
> Kind regards
> Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-03-24  1:29     ` AKASHI Takahiro
@ 2022-03-24  6:09       ` Lai Jason
  2022-03-24 10:22       ` Ulf Hansson
  1 sibling, 0 replies; 27+ messages in thread
From: Lai Jason @ 2022-03-24  6:09 UTC (permalink / raw)
  To: AKASHI Takahiro, Ulf Hansson, Jason Lai, Adrian Hunter,
	linux-mmc, dlunev, Ben Chuang, GregTu[杜啟軒],
	Jason Lai, otis.wu

On Thu, Mar 24, 2022 at 9:29 AM AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
>
> On Wed, Mar 23, 2022 at 05:15:59PM +0100, Ulf Hansson wrote:
> > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > >
> > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > >
> > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > Part 1 - PHY Initialization:
> > >          Every host controller may need their own avtivation operation to
> > >          establish LINK between controller and card. So we add a new member
> > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > >          controller use.
> > > Part 2 - Card Initialization:
> > >          This part can be divided into 6 substeps.
> > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > >          2. Send UHS-II CCMD ENUMERATE to card.
> > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > >             of card.
> > >          4. Host compares capabilities of host controller and card, then
> > >             write the negotiated values to Setting field in CFG_REG of card
> > >             through UHS-II Native Write CCMD.
> > >          5. Switch host controller's clock to Range B if it is supported by
> > >             both host controller and card.
> > >          6. Execute legacy SD initialization flow.
> > > Part 3 - Provide a function to tranaform legacy SD command packet into
> > >          UHS-II SD-TRAN DCMD packet.
> > >
> > > Most of the code added above came from Intel's original patch[3].
> > >
> > > [3]
> > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > git-send-email-yi.y.sun@intel.com/
>
> To honor the original work, we should add Intel's copyright notice here
> as I did before.
>
> -Takahiro Akashi
>
>
> > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > ---
> > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > index 800957f74632..f1e8e30301eb 100644
> > > --- a/drivers/mmc/core/sd_uhs2.c
> > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > @@ -3,6 +3,7 @@
> > >   * Copyright (C) 2021 Linaro Ltd
> > >   *
> > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>

Do you mean to add copyright information listed below here?
* Copyright (C) 2020 Linaro Limited
* Author: Ulf Hansson <ulf.hansson@linaro.org>
* Copyright (C) 2014 Intel Corp, All Rights Reserved.
* Copyright (C) 2020 Genesys Logic, Inc.
* Authors: Jason Lai <jason.lai@genesyslogic.com.tw>

-Jason Lai

> > >   *
> > >   * Support for SD UHS-II cards
> > >   */
> > > @@ -10,19 +11,31 @@
> > >
> > >  #include <linux/mmc/host.h>
> > >  #include <linux/mmc/card.h>
> > > +#include <linux/mmc/mmc.h>
> > > +#include <linux/mmc/sd_uhs2.h>
> > >
> > >  #include "core.h"
> > >  #include "bus.h"
> > > +#include "card.h"
> > >  #include "sd.h"
> > > +#include "sd_ops.h"
> > >  #include "mmc_ops.h"
> > > +#include "sd_uhs2.h"
> > >
> > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > >
> > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > >  {
> > >         struct mmc_ios *ios = &host->ios;
> > > +       int err = 0;
> > >
> > > -       return host->ops->uhs2_set_ios(host, ios);
> > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > +                ios->timing);
> > > +
> > > +       host->ops->set_ios(host, ios);
> >
> > We discussed using the ->set_ios() callback in a previous version. To
> > repeat myself, I don't think it's a good idea. UHS-II needs an
> > entirely different power sequence than the legacy interface(s), hence
> > I think it's simply cleaner to separate them.
> >
> > To move forward, I see two options.
> > 1) Use only the ->uhs2_host_operation() ops.
> > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > ->uhs2_host_operation() ops.
> >
> > Both options work for me. However, perhaps if you could incorporate
> > the changes done on the host driver at next submission, it becomes
> > easier for me to understand what makes best sense.
> >
> > > +
> > > +       return err;
> > >  }
> > >
> > >  static int sd_uhs2_power_up(struct mmc_host *host)
> > > @@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> > >         sd_uhs2_set_ios(host);
> > >  }
> >
> > [...]
> >
> > >
> > >  /*
> > > @@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> > >   */
> > >  static int sd_uhs2_dev_init(struct mmc_host *host)
> > >  {
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u32 cnt;
> > > +       u32 dap, gap, resp_gap;
> > > +       u16 header = 0, arg = 0;
> >
> > No need to initiate these.
> >
> > > +       u32 payload[1];
> >
> > u32?
> >
> > > +       u8 plen = 1;
> > > +       u8 gd = 0, cf = 1;
> > > +       u8 resp[6] = {0};
> > > +       u8 resp_len = 6;
> >
> > Many of these variables are just constant numbers. If it makes sense
> > to add definitions for them, then please do that instead. If not, just
> > give the value directly in the code.
> >
> > For example: plen = 1; (I assume that is payload length). This can
> > just be given as an in-parameter to sd_uhs2_cmd_assemble(), without
> > further explanation.
> >
> > The point is, sd_uhs2_cmd_assemble() should have a well described
> > description of its in-parameters, so no need for further descriptions,
> > I think.
> >
> > This comment applies to all the new code/functions that are added in
> > the $subject patch. Please go through all of the code and fix this.
> >
> >
> > > +       int err;
> > > +
> > > +       dap = host->uhs2_caps.dap;
> > > +       gap = host->uhs2_caps.gap;
> > > +
> > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> > > +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> > > +              UHS2_NATIVE_CMD_WRITE |
> > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> > > +
> > > +       /*
> > > +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > > +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > > +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > > +        * 1 second.
> > > +        */
> > > +       cmd.busy_timeout = 1000;
> > > +
> > > +       /*
> > > +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > > +        * When the number of the DEVICE_INIT commands is reach to
> > > +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > > +        * and regard it as an error.
> > > +        */
> > > +       for (cnt = 0; cnt < 30; cnt++) {
> > > +               payload[0] = ((dap & 0xF) << 12) |
> > > +                             (cf << 11)         |
> > > +                             ((gd & 0xF) << 4)  |
> > > +                             (gap & 0xF);
> >
> > To me, it looks like the payload data deserves to be explained a bit.
> > Perhaps you can add a comment explaining what pieces it consists of so
> > this becomes more clear?
> >
> > > +
> > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
> > > +
> > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +
> > > +               if (err) {
> > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > +                              mmc_hostname(host), __func__, err);
> > > +                       return -EIO;
> >
> > Why do you override the original error code that was returned from
> > mmc_wait_for_cmd()?
> >
> > Normally it's preferred to keep the error code, unless there is good
> > reason not to.
> >
> > Again, I won't add more comments like this in the code from the
> > $subject patch. But please go through it all to avoid this kind of
> > thing.
> >
> > > +               }
> > > +
> > > +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> > > +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> > > +                              mmc_hostname(host));
> > > +                       return -EIO;
> > > +               }
> > > +
> > > +               if (resp[5] & 0x8) {
> > > +                       host->uhs2_caps.group_desc = gd;
> > > +                       break;
> >
> > I suggest you do a return 0 here. In this way you can skip the check
> > "if (cnt == 30)" below and just return an error code instead.
> >
> > > +               }
> > > +               resp_gap = resp[4] & 0x0F;
> > > +               if (gap == resp_gap)
> > > +                       gd++;
> > > +       }
> > > +       if (cnt == 30) {
> > > +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> > > +                      mmc_hostname(host));
> > > +               return -EIO;
> > > +       }
> > > +
> > >         return 0;
> > >  }
> > >
> >
> > >  static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
> > >  {
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 cap;
> > > +       int err;
> > > +
> > > +       header = UHS2_NATIVE_PACKET |
> > > +                UHS2_PACKET_TYPE_CCMD |
> > > +                card->uhs2_config.node_id;
> > > +       arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
> > > +              UHS2_NATIVE_CMD_READ |
> > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > +              (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
> > > +
> > > +       /* There is no payload because per spec, there should be
> > > +        * no payload field for read CCMD.
> > > +        * Plen is set in arg. Per spec, plen for read CCMD
> > > +        * represents the len of read data which is assigned in payload
> > > +        * of following RES (p136).
> > > +        */
> > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> >
> > We are reading the configuration data here and onwards, piece by
> > piece. Perhaps if you can add a small comment about each piece we are
> > reading, before each call to mmc_wait_for_cmd(), that can help to
> > easier understand what goes on.
> >
> > [...]
> >
> > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > >  {
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 payload[2];
> > > +       u8 nMinDataGap;
> > > +       u8 plen;
> > > +       int err;
> > > +       u8 resp[5] = {0};
> > > +       u8 resp_len = 5;
> > > +
> > > +       header = UHS2_NATIVE_PACKET |
> > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > +              UHS2_NATIVE_CMD_WRITE |
> > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > +
> > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > +               /* Support HD */
> > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> >
> > How is the uhs2_caps.flags field intended to be used? To me it looks
> > like a way for the mmc core to exchange status/configuration
> > information about the initialization process of the card, with the mmc
> > host driver. Perhaps there is more too. Is that correct?
> >
> > If so, I think it looks quite similar for what we have in the struct
> > mmc_ios, for the legacy interface(s). I am not saying we should use
> > that, just trying to understand what would suit best here.
> >
> > > +               nMinDataGap = 1;
> > > +       } else {
> > > +               /* Only support 2L-FD so far */
> > > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > > +               nMinDataGap = 3;
> > > +       }
> > > +
> > > +       /*
> > > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > > +        * defined in UHS-II addendem Ver1.01 are optional.
> > > +        */
> > > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> >
> > [...]
> >
> > > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > > +{
> >
> > Looks like the in-parameter "hibernate" is superfluous, as it's always
> > set to "false" by the caller.
> >
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 payload[1];
> > > +       u8 plen = 1;
> > > +       int err;
> > > +
> > > +       /* Disable Normal INT */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > +
> > > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > > +               UHS2_NATIVE_CMD_WRITE |
> > > +               UHS2_NATIVE_CMD_PLEN_4B |
> > > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > > +
> > > +       if (hibernate)
> > > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > > +
> > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > > +
> > > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +       if (err) {
> > > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > +                      mmc_hostname(host), __func__, err);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       /* Check Dormant State in Present */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > > +
> > >         return 0;
> > >  }
> > >
> > > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > > +{
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       int err;
> > > +       int timeout = 100;
> > > +
> > > +       /* Change Speed Range at controller side. */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
> > > +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       err = sd_uhs2_go_dormant(host, false, node_id);
> > > +       if (err) {
> > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > > +                      mmc_hostname(host), __func__, err);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       /* restore sd clock */
> > > +       mmc_delay(5);
> > > +       host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);
> >
> > I think the code can be a bit better structured here. More precisely,
> > since sd_uhs2_go_dormant() is the one that calls
> > ->uhs2_host_operation(host, UHS2_DISABLE_INT) and
> > ->uhs2_host_operation(host, UHS2_DISABLE_CLK), it's then up to
> > sd_uhs2_change_speed() to restore these changes.
> >
> > To me, it would be more clear if both enabling and disabling of the
> > clock /interrupt are managed in sd_uhs2_change_speed().
> >
> > > +
> > > +       /* Enable Normal INT */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
> > > +               pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       /*
> > > +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> > > +        * switch to Active State
> > > +        */
> > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > +               UHS2_NATIVE_CMD_READ |
> > > +               UHS2_NATIVE_CMD_PLEN_8B |
> > > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > +       do {
> > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +               if (err) {
> > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > +                              mmc_hostname(host), __func__, err);
> > > +                       return -EIO;
> > > +               }
> > > +
> > > +               if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> > > +                       break;
> > > +
> > > +               timeout--;
> > > +               if (timeout == 0) {
> > > +                       pr_err("%s: %s: Not switch to Active in 100 ms\n",
> > > +                              mmc_hostname(host), __func__);
> > > +                       return -EIO;
> > > +               }
> > > +
> > > +               mmc_delay(1);
> > > +       } while (1);
> >
> > We really want to avoid these kinds of polling loops, for several
> > reasons. Please convert into using __mmc_poll_for_busy() instead.
> >
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int sd_uhs2_get_ro(struct mmc_host *host)
> > > +{
> > > +       int ro;
> > > +
> > > +       /*
> > > +        * Some systems don't feature a write-protect pin and don't need one.
> > > +        * E.g. because they only have micro-SD card slot. For those systems
> > > +        * assume that the SD card is always read-write.
> > > +        */
> > > +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> > > +               return 0;
> > > +
> > > +       if (!host->ops->get_ro)
> > > +               return -1;
> > > +
> > > +       ro = host->ops->get_ro(host);
> > > +
> > > +       return ro;
> >
> > This can be replaced with mmc_sd_get_ro(). Let's avoid the open coding
> > and make that function being shared instead.
> >
> > > +}
> > > +
> > >  /*
> > >   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> > >   * commands/requests to be backwards compatible through the legacy SD protocol.
> > > @@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > >   */
> > >  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> > >  {
> > > +       int err;
> > > +       u32 cid[4];
> > > +       u32 ocr;
> > > +       u32 rocr = 0;
> > > +       int ro;
> > > +
> > > +       WARN_ON(!host->claimed);
> >
> > Drop this, it's an internal function, we should know that the host is
> > claimed before calling sd_uhs2_legacy_init().
> >
> > > +
> > > +       /* Send CMD0 to reset SD card */
> > > +       mmc_go_idle(host);
> > > +
> > > +       /* Send CMD8 to communicate SD interface operation condition */
> > > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> > > +                      mmc_hostname(host), __func__);
> >
> > Please drop these prints for every command/operation that fails. We
> > already have trace/debug options for commands/requests.
> >
> > This applies to all the below code as well (perhaps there are few
> > cases not covered by the existing trace/debug support, those may be
> > converted to pr_debug().
> >
> > > +               return err;
> > > +       }
> > > +
> > > +       /*
> > > +        * Probe SD card working voltage.
> > > +        */
> > > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return err;
> > > +       }
> > > +       card->ocr = ocr;
> > > +
> > > +       /*
> > > +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > > +        * these bits as being in-valid and especially also bit7.
> > > +        */
> > > +       ocr &= ~0x7FFF;
> > > +       rocr = mmc_select_voltage(host, ocr);
> >
> > If the host has MMC_CAP2_FULL_PWR_CYCLE set, mmc_select_voltage() may
> > end up calling mmc_power_cycle(). This is not going to work for
> > UHS-II.
> >
> > Either we need to modify mmc_select_voltage() so it becomes aware that
> > it can be called for UHS-II initialization, allowing it to avoid the
> > path to mmc_power_cycle() - or simply open code the part from
> > mmc_select_voltage() for UHS-II here. I think I prefer the latter.
> >
> > > +
> > > +       /*
> > > +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > > +        * ocr value to rocr.
> > > +        */
> > > +       if (!rocr) {
> > > +               if (host->ocr_avail) {
> > > +                       rocr = host->ocr_avail;
> >
> > host->ocr_avail should really be checked in when the host driver calls
> > mmc_add_host(). It must not be zero, then we should let mmc_add_host()
> > return an error code. I look into this and send a patch for this
> > separately.
> >
> > In other words, you should not need to check it here, but just trust that's set.
> >
> > > +               } else {
> > > +                       pr_err("%s: %s: there is no valid OCR.\n",
> > > +                              mmc_hostname(host), __func__);
> > > +                       return -EINVAL;
> > > +               }
> > > +       }
> > > +
> > > +       /* Wait SD power on ready */
> > > +       ocr = rocr;
> > > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       err = mmc_send_cid(host, cid);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > > +
> > > +       /*
> > > +        * Call the optional HC's init_card function to handle quirks.
> > > +        */
> > > +       if (host->ops->init_card)
> > > +               host->ops->init_card(host, card);
> >
> > This can be removed, as it's only for the legacy interface, I think.
> >
> > > +
> > > +       /*
> > > +        * For native busses:  get card RCA and quit open drain mode.
> > > +        */
> > > +       err = mmc_send_relative_addr(host, &card->rca);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       err = mmc_sd_get_csd(card);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       /*
> > > +        * Select card, as all following commands rely on that.
> > > +        */
> > > +       err = mmc_select_card(card);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       /*
> > > +        * Check if read-only switch is active.
> > > +        */
> > > +       ro = sd_uhs2_get_ro(host);
> > > +       if (ro < 0) {
> > > +               pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> > > +                       mmc_hostname(host));
> > > +       } else if (ro > 0) {
> > > +               mmc_card_set_readonly(card);
> > > +       }
> > > +
> > >         return 0;
> > >  }
> > >
> > > +static void sd_uhs2_remove(struct mmc_host *host)
> > > +{
> > > +       mmc_remove_card(host->card);
> > > +       host->card = NULL;
> > > +}
> > > +
> > >  /*
> > >   * Allocate the data structure for the mmc_card and run the UHS-II specific
> > >   * initialization sequence.
> > > @@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > >         int err;
> > >
> > >         err = sd_uhs2_dev_init(host);
> > > -       if (err)
> > > +       if (err) {
> > > +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
> > >                 return err;
> > > +       }
> > >
> > >         err = sd_uhs2_enum(host, &node_id);
> > > -       if (err)
> > > +       if (err) {
> > > +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
> > >                 return err;
> > > +       }
> > >
> > >         card = mmc_alloc_card(host, &sd_type);
> > >         if (IS_ERR(card))
> > >                 return PTR_ERR(card);
> > > +       host->card = card;
> > >
> > >         card->uhs2_config.node_id = node_id;
> > >         card->type = MMC_TYPE_SD;
> > > @@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > >         if (err)
> > >                 goto err;
> > >
> > > +       /* Change to Speed Range B if it is supported */
> > > +       if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
> > > +               err = sd_uhs2_change_speed(host, node_id);
> > > +               if (err) {
> > > +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> > > +                              mmc_hostname(host), __func__);
> > > +                       return err;
> > > +               }
> > > +       }
> > > +
> > >         err = sd_uhs2_config_write(host, card);
> > >         if (err)
> > >                 goto err;
> > > @@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > >         if (err)
> > >                 goto err;
> > >
> > > -       host->card = card;
> > >         return 0;
> > >
> > >  err:
> > > -       mmc_remove_card(card);
> > > +       sd_uhs2_remove(host);
> > >         return err;
> > >  }
> >
> > [...]
> >
> > Kind regards
> > Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-03-24  1:29     ` AKASHI Takahiro
  2022-03-24  6:09       ` Lai Jason
@ 2022-03-24 10:22       ` Ulf Hansson
  2022-03-24 10:50         ` AKASHI Takahiro
  1 sibling, 1 reply; 27+ messages in thread
From: Ulf Hansson @ 2022-03-24 10:22 UTC (permalink / raw)
  To: AKASHI Takahiro, Ulf Hansson, Jason Lai, adrian.hunter,
	linux-mmc, dlunev, ben.chuang, greg.tu, Jason.Lai, otis.wu

On Thu, 24 Mar 2022 at 02:29, AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
>
> On Wed, Mar 23, 2022 at 05:15:59PM +0100, Ulf Hansson wrote:
> > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > >
> > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > >
> > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > Part 1 - PHY Initialization:
> > >          Every host controller may need their own avtivation operation to
> > >          establish LINK between controller and card. So we add a new member
> > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > >          controller use.
> > > Part 2 - Card Initialization:
> > >          This part can be divided into 6 substeps.
> > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > >          2. Send UHS-II CCMD ENUMERATE to card.
> > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > >             of card.
> > >          4. Host compares capabilities of host controller and card, then
> > >             write the negotiated values to Setting field in CFG_REG of card
> > >             through UHS-II Native Write CCMD.
> > >          5. Switch host controller's clock to Range B if it is supported by
> > >             both host controller and card.
> > >          6. Execute legacy SD initialization flow.
> > > Part 3 - Provide a function to tranaform legacy SD command packet into
> > >          UHS-II SD-TRAN DCMD packet.
> > >
> > > Most of the code added above came from Intel's original patch[3].
> > >
> > > [3]
> > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > git-send-email-yi.y.sun@intel.com/
>
> To honor the original work, we should add Intel's copyright notice here
> as I did before.

I think Jason already did, at least for some of the files in this series.

Note also that, the initial code that was posted, is very far from
what code that is going to be merged. Simply because the quality was
very poor and not acceptable for the upstream kernel. That said, I am
not sure we need to keep the copyrights for this, but I leave that
call to Jason to decide.

>
> -Takahiro Akashi
>

Kind regards
Uffe

>
> > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > ---
> > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > index 800957f74632..f1e8e30301eb 100644
> > > --- a/drivers/mmc/core/sd_uhs2.c
> > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > @@ -3,6 +3,7 @@
> > >   * Copyright (C) 2021 Linaro Ltd
> > >   *
> > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> > >   *
> > >   * Support for SD UHS-II cards
> > >   */
> > > @@ -10,19 +11,31 @@
> > >
> > >  #include <linux/mmc/host.h>
> > >  #include <linux/mmc/card.h>
> > > +#include <linux/mmc/mmc.h>
> > > +#include <linux/mmc/sd_uhs2.h>
> > >
> > >  #include "core.h"
> > >  #include "bus.h"
> > > +#include "card.h"
> > >  #include "sd.h"
> > > +#include "sd_ops.h"
> > >  #include "mmc_ops.h"
> > > +#include "sd_uhs2.h"
> > >
> > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > >
> > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > >  {
> > >         struct mmc_ios *ios = &host->ios;
> > > +       int err = 0;
> > >
> > > -       return host->ops->uhs2_set_ios(host, ios);
> > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > +                ios->timing);
> > > +
> > > +       host->ops->set_ios(host, ios);
> >
> > We discussed using the ->set_ios() callback in a previous version. To
> > repeat myself, I don't think it's a good idea. UHS-II needs an
> > entirely different power sequence than the legacy interface(s), hence
> > I think it's simply cleaner to separate them.
> >
> > To move forward, I see two options.
> > 1) Use only the ->uhs2_host_operation() ops.
> > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > ->uhs2_host_operation() ops.
> >
> > Both options work for me. However, perhaps if you could incorporate
> > the changes done on the host driver at next submission, it becomes
> > easier for me to understand what makes best sense.
> >
> > > +
> > > +       return err;
> > >  }
> > >
> > >  static int sd_uhs2_power_up(struct mmc_host *host)
> > > @@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> > >         sd_uhs2_set_ios(host);
> > >  }
> >
> > [...]
> >
> > >
> > >  /*
> > > @@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> > >   */
> > >  static int sd_uhs2_dev_init(struct mmc_host *host)
> > >  {
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u32 cnt;
> > > +       u32 dap, gap, resp_gap;
> > > +       u16 header = 0, arg = 0;
> >
> > No need to initiate these.
> >
> > > +       u32 payload[1];
> >
> > u32?
> >
> > > +       u8 plen = 1;
> > > +       u8 gd = 0, cf = 1;
> > > +       u8 resp[6] = {0};
> > > +       u8 resp_len = 6;
> >
> > Many of these variables are just constant numbers. If it makes sense
> > to add definitions for them, then please do that instead. If not, just
> > give the value directly in the code.
> >
> > For example: plen = 1; (I assume that is payload length). This can
> > just be given as an in-parameter to sd_uhs2_cmd_assemble(), without
> > further explanation.
> >
> > The point is, sd_uhs2_cmd_assemble() should have a well described
> > description of its in-parameters, so no need for further descriptions,
> > I think.
> >
> > This comment applies to all the new code/functions that are added in
> > the $subject patch. Please go through all of the code and fix this.
> >
> >
> > > +       int err;
> > > +
> > > +       dap = host->uhs2_caps.dap;
> > > +       gap = host->uhs2_caps.gap;
> > > +
> > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> > > +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> > > +              UHS2_NATIVE_CMD_WRITE |
> > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> > > +
> > > +       /*
> > > +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > > +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > > +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > > +        * 1 second.
> > > +        */
> > > +       cmd.busy_timeout = 1000;
> > > +
> > > +       /*
> > > +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > > +        * When the number of the DEVICE_INIT commands is reach to
> > > +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > > +        * and regard it as an error.
> > > +        */
> > > +       for (cnt = 0; cnt < 30; cnt++) {
> > > +               payload[0] = ((dap & 0xF) << 12) |
> > > +                             (cf << 11)         |
> > > +                             ((gd & 0xF) << 4)  |
> > > +                             (gap & 0xF);
> >
> > To me, it looks like the payload data deserves to be explained a bit.
> > Perhaps you can add a comment explaining what pieces it consists of so
> > this becomes more clear?
> >
> > > +
> > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
> > > +
> > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +
> > > +               if (err) {
> > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > +                              mmc_hostname(host), __func__, err);
> > > +                       return -EIO;
> >
> > Why do you override the original error code that was returned from
> > mmc_wait_for_cmd()?
> >
> > Normally it's preferred to keep the error code, unless there is good
> > reason not to.
> >
> > Again, I won't add more comments like this in the code from the
> > $subject patch. But please go through it all to avoid this kind of
> > thing.
> >
> > > +               }
> > > +
> > > +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> > > +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> > > +                              mmc_hostname(host));
> > > +                       return -EIO;
> > > +               }
> > > +
> > > +               if (resp[5] & 0x8) {
> > > +                       host->uhs2_caps.group_desc = gd;
> > > +                       break;
> >
> > I suggest you do a return 0 here. In this way you can skip the check
> > "if (cnt == 30)" below and just return an error code instead.
> >
> > > +               }
> > > +               resp_gap = resp[4] & 0x0F;
> > > +               if (gap == resp_gap)
> > > +                       gd++;
> > > +       }
> > > +       if (cnt == 30) {
> > > +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> > > +                      mmc_hostname(host));
> > > +               return -EIO;
> > > +       }
> > > +
> > >         return 0;
> > >  }
> > >
> >
> > >  static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
> > >  {
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 cap;
> > > +       int err;
> > > +
> > > +       header = UHS2_NATIVE_PACKET |
> > > +                UHS2_PACKET_TYPE_CCMD |
> > > +                card->uhs2_config.node_id;
> > > +       arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
> > > +              UHS2_NATIVE_CMD_READ |
> > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > +              (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
> > > +
> > > +       /* There is no payload because per spec, there should be
> > > +        * no payload field for read CCMD.
> > > +        * Plen is set in arg. Per spec, plen for read CCMD
> > > +        * represents the len of read data which is assigned in payload
> > > +        * of following RES (p136).
> > > +        */
> > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> >
> > We are reading the configuration data here and onwards, piece by
> > piece. Perhaps if you can add a small comment about each piece we are
> > reading, before each call to mmc_wait_for_cmd(), that can help to
> > easier understand what goes on.
> >
> > [...]
> >
> > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > >  {
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 payload[2];
> > > +       u8 nMinDataGap;
> > > +       u8 plen;
> > > +       int err;
> > > +       u8 resp[5] = {0};
> > > +       u8 resp_len = 5;
> > > +
> > > +       header = UHS2_NATIVE_PACKET |
> > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > +              UHS2_NATIVE_CMD_WRITE |
> > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > +
> > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > +               /* Support HD */
> > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> >
> > How is the uhs2_caps.flags field intended to be used? To me it looks
> > like a way for the mmc core to exchange status/configuration
> > information about the initialization process of the card, with the mmc
> > host driver. Perhaps there is more too. Is that correct?
> >
> > If so, I think it looks quite similar for what we have in the struct
> > mmc_ios, for the legacy interface(s). I am not saying we should use
> > that, just trying to understand what would suit best here.
> >
> > > +               nMinDataGap = 1;
> > > +       } else {
> > > +               /* Only support 2L-FD so far */
> > > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > > +               nMinDataGap = 3;
> > > +       }
> > > +
> > > +       /*
> > > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > > +        * defined in UHS-II addendem Ver1.01 are optional.
> > > +        */
> > > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> >
> > [...]
> >
> > > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > > +{
> >
> > Looks like the in-parameter "hibernate" is superfluous, as it's always
> > set to "false" by the caller.
> >
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 payload[1];
> > > +       u8 plen = 1;
> > > +       int err;
> > > +
> > > +       /* Disable Normal INT */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > +
> > > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > > +               UHS2_NATIVE_CMD_WRITE |
> > > +               UHS2_NATIVE_CMD_PLEN_4B |
> > > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > > +
> > > +       if (hibernate)
> > > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > > +
> > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > > +
> > > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +       if (err) {
> > > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > +                      mmc_hostname(host), __func__, err);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       /* Check Dormant State in Present */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > > +
> > >         return 0;
> > >  }
> > >
> > > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > > +{
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       int err;
> > > +       int timeout = 100;
> > > +
> > > +       /* Change Speed Range at controller side. */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
> > > +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       err = sd_uhs2_go_dormant(host, false, node_id);
> > > +       if (err) {
> > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > > +                      mmc_hostname(host), __func__, err);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       /* restore sd clock */
> > > +       mmc_delay(5);
> > > +       host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);
> >
> > I think the code can be a bit better structured here. More precisely,
> > since sd_uhs2_go_dormant() is the one that calls
> > ->uhs2_host_operation(host, UHS2_DISABLE_INT) and
> > ->uhs2_host_operation(host, UHS2_DISABLE_CLK), it's then up to
> > sd_uhs2_change_speed() to restore these changes.
> >
> > To me, it would be more clear if both enabling and disabling of the
> > clock /interrupt are managed in sd_uhs2_change_speed().
> >
> > > +
> > > +       /* Enable Normal INT */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
> > > +               pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       /*
> > > +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> > > +        * switch to Active State
> > > +        */
> > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > +               UHS2_NATIVE_CMD_READ |
> > > +               UHS2_NATIVE_CMD_PLEN_8B |
> > > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > +       do {
> > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +               if (err) {
> > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > +                              mmc_hostname(host), __func__, err);
> > > +                       return -EIO;
> > > +               }
> > > +
> > > +               if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> > > +                       break;
> > > +
> > > +               timeout--;
> > > +               if (timeout == 0) {
> > > +                       pr_err("%s: %s: Not switch to Active in 100 ms\n",
> > > +                              mmc_hostname(host), __func__);
> > > +                       return -EIO;
> > > +               }
> > > +
> > > +               mmc_delay(1);
> > > +       } while (1);
> >
> > We really want to avoid these kinds of polling loops, for several
> > reasons. Please convert into using __mmc_poll_for_busy() instead.
> >
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int sd_uhs2_get_ro(struct mmc_host *host)
> > > +{
> > > +       int ro;
> > > +
> > > +       /*
> > > +        * Some systems don't feature a write-protect pin and don't need one.
> > > +        * E.g. because they only have micro-SD card slot. For those systems
> > > +        * assume that the SD card is always read-write.
> > > +        */
> > > +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> > > +               return 0;
> > > +
> > > +       if (!host->ops->get_ro)
> > > +               return -1;
> > > +
> > > +       ro = host->ops->get_ro(host);
> > > +
> > > +       return ro;
> >
> > This can be replaced with mmc_sd_get_ro(). Let's avoid the open coding
> > and make that function being shared instead.
> >
> > > +}
> > > +
> > >  /*
> > >   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> > >   * commands/requests to be backwards compatible through the legacy SD protocol.
> > > @@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > >   */
> > >  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> > >  {
> > > +       int err;
> > > +       u32 cid[4];
> > > +       u32 ocr;
> > > +       u32 rocr = 0;
> > > +       int ro;
> > > +
> > > +       WARN_ON(!host->claimed);
> >
> > Drop this, it's an internal function, we should know that the host is
> > claimed before calling sd_uhs2_legacy_init().
> >
> > > +
> > > +       /* Send CMD0 to reset SD card */
> > > +       mmc_go_idle(host);
> > > +
> > > +       /* Send CMD8 to communicate SD interface operation condition */
> > > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> > > +                      mmc_hostname(host), __func__);
> >
> > Please drop these prints for every command/operation that fails. We
> > already have trace/debug options for commands/requests.
> >
> > This applies to all the below code as well (perhaps there are few
> > cases not covered by the existing trace/debug support, those may be
> > converted to pr_debug().
> >
> > > +               return err;
> > > +       }
> > > +
> > > +       /*
> > > +        * Probe SD card working voltage.
> > > +        */
> > > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return err;
> > > +       }
> > > +       card->ocr = ocr;
> > > +
> > > +       /*
> > > +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > > +        * these bits as being in-valid and especially also bit7.
> > > +        */
> > > +       ocr &= ~0x7FFF;
> > > +       rocr = mmc_select_voltage(host, ocr);
> >
> > If the host has MMC_CAP2_FULL_PWR_CYCLE set, mmc_select_voltage() may
> > end up calling mmc_power_cycle(). This is not going to work for
> > UHS-II.
> >
> > Either we need to modify mmc_select_voltage() so it becomes aware that
> > it can be called for UHS-II initialization, allowing it to avoid the
> > path to mmc_power_cycle() - or simply open code the part from
> > mmc_select_voltage() for UHS-II here. I think I prefer the latter.
> >
> > > +
> > > +       /*
> > > +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > > +        * ocr value to rocr.
> > > +        */
> > > +       if (!rocr) {
> > > +               if (host->ocr_avail) {
> > > +                       rocr = host->ocr_avail;
> >
> > host->ocr_avail should really be checked in when the host driver calls
> > mmc_add_host(). It must not be zero, then we should let mmc_add_host()
> > return an error code. I look into this and send a patch for this
> > separately.
> >
> > In other words, you should not need to check it here, but just trust that's set.
> >
> > > +               } else {
> > > +                       pr_err("%s: %s: there is no valid OCR.\n",
> > > +                              mmc_hostname(host), __func__);
> > > +                       return -EINVAL;
> > > +               }
> > > +       }
> > > +
> > > +       /* Wait SD power on ready */
> > > +       ocr = rocr;
> > > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       err = mmc_send_cid(host, cid);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > > +
> > > +       /*
> > > +        * Call the optional HC's init_card function to handle quirks.
> > > +        */
> > > +       if (host->ops->init_card)
> > > +               host->ops->init_card(host, card);
> >
> > This can be removed, as it's only for the legacy interface, I think.
> >
> > > +
> > > +       /*
> > > +        * For native busses:  get card RCA and quit open drain mode.
> > > +        */
> > > +       err = mmc_send_relative_addr(host, &card->rca);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       err = mmc_sd_get_csd(card);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       /*
> > > +        * Select card, as all following commands rely on that.
> > > +        */
> > > +       err = mmc_select_card(card);
> > > +       if (err) {
> > > +               pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
> > > +                      __func__);
> > > +               return err;
> > > +       }
> > > +
> > > +       /*
> > > +        * Check if read-only switch is active.
> > > +        */
> > > +       ro = sd_uhs2_get_ro(host);
> > > +       if (ro < 0) {
> > > +               pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> > > +                       mmc_hostname(host));
> > > +       } else if (ro > 0) {
> > > +               mmc_card_set_readonly(card);
> > > +       }
> > > +
> > >         return 0;
> > >  }
> > >
> > > +static void sd_uhs2_remove(struct mmc_host *host)
> > > +{
> > > +       mmc_remove_card(host->card);
> > > +       host->card = NULL;
> > > +}
> > > +
> > >  /*
> > >   * Allocate the data structure for the mmc_card and run the UHS-II specific
> > >   * initialization sequence.
> > > @@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > >         int err;
> > >
> > >         err = sd_uhs2_dev_init(host);
> > > -       if (err)
> > > +       if (err) {
> > > +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
> > >                 return err;
> > > +       }
> > >
> > >         err = sd_uhs2_enum(host, &node_id);
> > > -       if (err)
> > > +       if (err) {
> > > +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
> > >                 return err;
> > > +       }
> > >
> > >         card = mmc_alloc_card(host, &sd_type);
> > >         if (IS_ERR(card))
> > >                 return PTR_ERR(card);
> > > +       host->card = card;
> > >
> > >         card->uhs2_config.node_id = node_id;
> > >         card->type = MMC_TYPE_SD;
> > > @@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > >         if (err)
> > >                 goto err;
> > >
> > > +       /* Change to Speed Range B if it is supported */
> > > +       if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
> > > +               err = sd_uhs2_change_speed(host, node_id);
> > > +               if (err) {
> > > +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> > > +                              mmc_hostname(host), __func__);
> > > +                       return err;
> > > +               }
> > > +       }
> > > +
> > >         err = sd_uhs2_config_write(host, card);
> > >         if (err)
> > >                 goto err;
> > > @@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > >         if (err)
> > >                 goto err;
> > >
> > > -       host->card = card;
> > >         return 0;
> > >
> > >  err:
> > > -       mmc_remove_card(card);
> > > +       sd_uhs2_remove(host);
> > >         return err;
> > >  }
> >
> > [...]
> >
> > Kind regards
> > Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-03-24 10:22       ` Ulf Hansson
@ 2022-03-24 10:50         ` AKASHI Takahiro
  2022-03-25  3:53           ` Lai Jason
  0 siblings, 1 reply; 27+ messages in thread
From: AKASHI Takahiro @ 2022-03-24 10:50 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Jason Lai, adrian.hunter, linux-mmc, dlunev, ben.chuang, greg.tu,
	Jason.Lai, otis.wu

On Thu, Mar 24, 2022 at 11:22:16AM +0100, Ulf Hansson wrote:
> On Thu, 24 Mar 2022 at 02:29, AKASHI Takahiro
> <takahiro.akashi@linaro.org> wrote:
> >
> > On Wed, Mar 23, 2022 at 05:15:59PM +0100, Ulf Hansson wrote:
> > > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > > >
> > > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > >
> > > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > > Part 1 - PHY Initialization:
> > > >          Every host controller may need their own avtivation operation to
> > > >          establish LINK between controller and card. So we add a new member
> > > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > > >          controller use.
> > > > Part 2 - Card Initialization:
> > > >          This part can be divided into 6 substeps.
> > > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > > >          2. Send UHS-II CCMD ENUMERATE to card.
> > > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > > >             of card.
> > > >          4. Host compares capabilities of host controller and card, then
> > > >             write the negotiated values to Setting field in CFG_REG of card
> > > >             through UHS-II Native Write CCMD.
> > > >          5. Switch host controller's clock to Range B if it is supported by
> > > >             both host controller and card.
> > > >          6. Execute legacy SD initialization flow.
> > > > Part 3 - Provide a function to tranaform legacy SD command packet into
> > > >          UHS-II SD-TRAN DCMD packet.
> > > >
> > > > Most of the code added above came from Intel's original patch[3].
> > > >
> > > > [3]
> > > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > > git-send-email-yi.y.sun@intel.com/
> >
> > To honor the original work, we should add Intel's copyright notice here
> > as I did before.
> 
> I think Jason already did, at least for some of the files in this series.
> 
> Note also that, the initial code that was posted, is very far from
> what code that is going to be merged. Simply because the quality was
> very poor and not acceptable for the upstream kernel.

I don't think so.
I don't see much difference from my modified version[1] which is also
based on Intel's original work.

[1] https://www.spinics.net/lists/linux-mmc/msg57321.html

-Takahiro Akashi

> That said, I am
> not sure we need to keep the copyrights for this, but I leave that
> call to Jason to decide.
> 
> >
> > -Takahiro Akashi
> >
> 
> Kind regards
> Uffe
> 
> >
> > > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > ---
> > > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > > index 800957f74632..f1e8e30301eb 100644
> > > > --- a/drivers/mmc/core/sd_uhs2.c
> > > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > > @@ -3,6 +3,7 @@
> > > >   * Copyright (C) 2021 Linaro Ltd
> > > >   *
> > > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > >   *
> > > >   * Support for SD UHS-II cards
> > > >   */
> > > > @@ -10,19 +11,31 @@
> > > >
> > > >  #include <linux/mmc/host.h>
> > > >  #include <linux/mmc/card.h>
> > > > +#include <linux/mmc/mmc.h>
> > > > +#include <linux/mmc/sd_uhs2.h>
> > > >
> > > >  #include "core.h"
> > > >  #include "bus.h"
> > > > +#include "card.h"
> > > >  #include "sd.h"
> > > > +#include "sd_ops.h"
> > > >  #include "mmc_ops.h"
> > > > +#include "sd_uhs2.h"
> > > >
> > > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > > >
> > > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > > >  {
> > > >         struct mmc_ios *ios = &host->ios;
> > > > +       int err = 0;
> > > >
> > > > -       return host->ops->uhs2_set_ios(host, ios);
> > > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > > +                ios->timing);
> > > > +
> > > > +       host->ops->set_ios(host, ios);
> > >
> > > We discussed using the ->set_ios() callback in a previous version. To
> > > repeat myself, I don't think it's a good idea. UHS-II needs an
> > > entirely different power sequence than the legacy interface(s), hence
> > > I think it's simply cleaner to separate them.
> > >
> > > To move forward, I see two options.
> > > 1) Use only the ->uhs2_host_operation() ops.
> > > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > > ->uhs2_host_operation() ops.
> > >
> > > Both options work for me. However, perhaps if you could incorporate
> > > the changes done on the host driver at next submission, it becomes
> > > easier for me to understand what makes best sense.
> > >
> > > > +
> > > > +       return err;
> > > >  }
> > > >
> > > >  static int sd_uhs2_power_up(struct mmc_host *host)
> > > > @@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> > > >         sd_uhs2_set_ios(host);
> > > >  }
> > >
> > > [...]
> > >
> > > >
> > > >  /*
> > > > @@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> > > >   */
> > > >  static int sd_uhs2_dev_init(struct mmc_host *host)
> > > >  {
> > > > +       struct mmc_command cmd = {0};
> > > > +       struct uhs2_command uhs2_cmd = {};
> > > > +       u32 cnt;
> > > > +       u32 dap, gap, resp_gap;
> > > > +       u16 header = 0, arg = 0;
> > >
> > > No need to initiate these.
> > >
> > > > +       u32 payload[1];
> > >
> > > u32?
> > >
> > > > +       u8 plen = 1;
> > > > +       u8 gd = 0, cf = 1;
> > > > +       u8 resp[6] = {0};
> > > > +       u8 resp_len = 6;
> > >
> > > Many of these variables are just constant numbers. If it makes sense
> > > to add definitions for them, then please do that instead. If not, just
> > > give the value directly in the code.
> > >
> > > For example: plen = 1; (I assume that is payload length). This can
> > > just be given as an in-parameter to sd_uhs2_cmd_assemble(), without
> > > further explanation.
> > >
> > > The point is, sd_uhs2_cmd_assemble() should have a well described
> > > description of its in-parameters, so no need for further descriptions,
> > > I think.
> > >
> > > This comment applies to all the new code/functions that are added in
> > > the $subject patch. Please go through all of the code and fix this.
> > >
> > >
> > > > +       int err;
> > > > +
> > > > +       dap = host->uhs2_caps.dap;
> > > > +       gap = host->uhs2_caps.gap;
> > > > +
> > > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> > > > +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > > +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> > > > +
> > > > +       /*
> > > > +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > > > +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > > > +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > > > +        * 1 second.
> > > > +        */
> > > > +       cmd.busy_timeout = 1000;
> > > > +
> > > > +       /*
> > > > +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > > > +        * When the number of the DEVICE_INIT commands is reach to
> > > > +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > > > +        * and regard it as an error.
> > > > +        */
> > > > +       for (cnt = 0; cnt < 30; cnt++) {
> > > > +               payload[0] = ((dap & 0xF) << 12) |
> > > > +                             (cf << 11)         |
> > > > +                             ((gd & 0xF) << 4)  |
> > > > +                             (gap & 0xF);
> > >
> > > To me, it looks like the payload data deserves to be explained a bit.
> > > Perhaps you can add a comment explaining what pieces it consists of so
> > > this becomes more clear?
> > >
> > > > +
> > > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
> > > > +
> > > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > > +
> > > > +               if (err) {
> > > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > > +                              mmc_hostname(host), __func__, err);
> > > > +                       return -EIO;
> > >
> > > Why do you override the original error code that was returned from
> > > mmc_wait_for_cmd()?
> > >
> > > Normally it's preferred to keep the error code, unless there is good
> > > reason not to.
> > >
> > > Again, I won't add more comments like this in the code from the
> > > $subject patch. But please go through it all to avoid this kind of
> > > thing.
> > >
> > > > +               }
> > > > +
> > > > +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> > > > +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> > > > +                              mmc_hostname(host));
> > > > +                       return -EIO;
> > > > +               }
> > > > +
> > > > +               if (resp[5] & 0x8) {
> > > > +                       host->uhs2_caps.group_desc = gd;
> > > > +                       break;
> > >
> > > I suggest you do a return 0 here. In this way you can skip the check
> > > "if (cnt == 30)" below and just return an error code instead.
> > >
> > > > +               }
> > > > +               resp_gap = resp[4] & 0x0F;
> > > > +               if (gap == resp_gap)
> > > > +                       gd++;
> > > > +       }
> > > > +       if (cnt == 30) {
> > > > +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> > > > +                      mmc_hostname(host));
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > >         return 0;
> > > >  }
> > > >
> > >
> > > >  static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
> > > >  {
> > > > +       struct mmc_command cmd = {0};
> > > > +       struct uhs2_command uhs2_cmd = {};
> > > > +       u16 header = 0, arg = 0;
> > > > +       u32 cap;
> > > > +       int err;
> > > > +
> > > > +       header = UHS2_NATIVE_PACKET |
> > > > +                UHS2_PACKET_TYPE_CCMD |
> > > > +                card->uhs2_config.node_id;
> > > > +       arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
> > > > +              UHS2_NATIVE_CMD_READ |
> > > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > > +              (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
> > > > +
> > > > +       /* There is no payload because per spec, there should be
> > > > +        * no payload field for read CCMD.
> > > > +        * Plen is set in arg. Per spec, plen for read CCMD
> > > > +        * represents the len of read data which is assigned in payload
> > > > +        * of following RES (p136).
> > > > +        */
> > > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > >
> > > We are reading the configuration data here and onwards, piece by
> > > piece. Perhaps if you can add a small comment about each piece we are
> > > reading, before each call to mmc_wait_for_cmd(), that can help to
> > > easier understand what goes on.
> > >
> > > [...]
> > >
> > > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > >  {
> > > > +       struct mmc_command cmd = {0};
> > > > +       struct uhs2_command uhs2_cmd = {};
> > > > +       u16 header = 0, arg = 0;
> > > > +       u32 payload[2];
> > > > +       u8 nMinDataGap;
> > > > +       u8 plen;
> > > > +       int err;
> > > > +       u8 resp[5] = {0};
> > > > +       u8 resp_len = 5;
> > > > +
> > > > +       header = UHS2_NATIVE_PACKET |
> > > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > +
> > > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > > +               /* Support HD */
> > > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> > >
> > > How is the uhs2_caps.flags field intended to be used? To me it looks
> > > like a way for the mmc core to exchange status/configuration
> > > information about the initialization process of the card, with the mmc
> > > host driver. Perhaps there is more too. Is that correct?
> > >
> > > If so, I think it looks quite similar for what we have in the struct
> > > mmc_ios, for the legacy interface(s). I am not saying we should use
> > > that, just trying to understand what would suit best here.
> > >
> > > > +               nMinDataGap = 1;
> > > > +       } else {
> > > > +               /* Only support 2L-FD so far */
> > > > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > > > +               nMinDataGap = 3;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > > > +        * defined in UHS-II addendem Ver1.01 are optional.
> > > > +        */
> > > > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > > > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > >
> > > [...]
> > >
> > > > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > > > +{
> > >
> > > Looks like the in-parameter "hibernate" is superfluous, as it's always
> > > set to "false" by the caller.
> > >
> > > > +       struct mmc_command cmd = {0};
> > > > +       struct uhs2_command uhs2_cmd = {};
> > > > +       u16 header = 0, arg = 0;
> > > > +       u32 payload[1];
> > > > +       u8 plen = 1;
> > > > +       int err;
> > > > +
> > > > +       /* Disable Normal INT */
> > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > > > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > > +
> > > > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > > > +               UHS2_NATIVE_CMD_WRITE |
> > > > +               UHS2_NATIVE_CMD_PLEN_4B |
> > > > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > > > +
> > > > +       if (hibernate)
> > > > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > > > +
> > > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > > > +
> > > > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > > +                      mmc_hostname(host), __func__, err);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       /* Check Dormant State in Present */
> > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > > > +
> > > >         return 0;
> > > >  }
> > > >
> > > > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > > > +{
> > > > +       struct mmc_command cmd = {0};
> > > > +       struct uhs2_command uhs2_cmd = {};
> > > > +       u16 header = 0, arg = 0;
> > > > +       int err;
> > > > +       int timeout = 100;
> > > > +
> > > > +       /* Change Speed Range at controller side. */
> > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
> > > > +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       err = sd_uhs2_go_dormant(host, false, node_id);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > > > +                      mmc_hostname(host), __func__, err);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       /* restore sd clock */
> > > > +       mmc_delay(5);
> > > > +       host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);
> > >
> > > I think the code can be a bit better structured here. More precisely,
> > > since sd_uhs2_go_dormant() is the one that calls
> > > ->uhs2_host_operation(host, UHS2_DISABLE_INT) and
> > > ->uhs2_host_operation(host, UHS2_DISABLE_CLK), it's then up to
> > > sd_uhs2_change_speed() to restore these changes.
> > >
> > > To me, it would be more clear if both enabling and disabling of the
> > > clock /interrupt are managed in sd_uhs2_change_speed().
> > >
> > > > +
> > > > +       /* Enable Normal INT */
> > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
> > > > +               pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> > > > +        * switch to Active State
> > > > +        */
> > > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > +               UHS2_NATIVE_CMD_READ |
> > > > +               UHS2_NATIVE_CMD_PLEN_8B |
> > > > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > +       do {
> > > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > > +               if (err) {
> > > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > > +                              mmc_hostname(host), __func__, err);
> > > > +                       return -EIO;
> > > > +               }
> > > > +
> > > > +               if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> > > > +                       break;
> > > > +
> > > > +               timeout--;
> > > > +               if (timeout == 0) {
> > > > +                       pr_err("%s: %s: Not switch to Active in 100 ms\n",
> > > > +                              mmc_hostname(host), __func__);
> > > > +                       return -EIO;
> > > > +               }
> > > > +
> > > > +               mmc_delay(1);
> > > > +       } while (1);
> > >
> > > We really want to avoid these kinds of polling loops, for several
> > > reasons. Please convert into using __mmc_poll_for_busy() instead.
> > >
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int sd_uhs2_get_ro(struct mmc_host *host)
> > > > +{
> > > > +       int ro;
> > > > +
> > > > +       /*
> > > > +        * Some systems don't feature a write-protect pin and don't need one.
> > > > +        * E.g. because they only have micro-SD card slot. For those systems
> > > > +        * assume that the SD card is always read-write.
> > > > +        */
> > > > +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> > > > +               return 0;
> > > > +
> > > > +       if (!host->ops->get_ro)
> > > > +               return -1;
> > > > +
> > > > +       ro = host->ops->get_ro(host);
> > > > +
> > > > +       return ro;
> > >
> > > This can be replaced with mmc_sd_get_ro(). Let's avoid the open coding
> > > and make that function being shared instead.
> > >
> > > > +}
> > > > +
> > > >  /*
> > > >   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> > > >   * commands/requests to be backwards compatible through the legacy SD protocol.
> > > > @@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > >   */
> > > >  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> > > >  {
> > > > +       int err;
> > > > +       u32 cid[4];
> > > > +       u32 ocr;
> > > > +       u32 rocr = 0;
> > > > +       int ro;
> > > > +
> > > > +       WARN_ON(!host->claimed);
> > >
> > > Drop this, it's an internal function, we should know that the host is
> > > claimed before calling sd_uhs2_legacy_init().
> > >
> > > > +
> > > > +       /* Send CMD0 to reset SD card */
> > > > +       mmc_go_idle(host);
> > > > +
> > > > +       /* Send CMD8 to communicate SD interface operation condition */
> > > > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > >
> > > Please drop these prints for every command/operation that fails. We
> > > already have trace/debug options for commands/requests.
> > >
> > > This applies to all the below code as well (perhaps there are few
> > > cases not covered by the existing trace/debug support, those may be
> > > converted to pr_debug().
> > >
> > > > +               return err;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * Probe SD card working voltage.
> > > > +        */
> > > > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > > > +               return err;
> > > > +       }
> > > > +       card->ocr = ocr;
> > > > +
> > > > +       /*
> > > > +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > > > +        * these bits as being in-valid and especially also bit7.
> > > > +        */
> > > > +       ocr &= ~0x7FFF;
> > > > +       rocr = mmc_select_voltage(host, ocr);
> > >
> > > If the host has MMC_CAP2_FULL_PWR_CYCLE set, mmc_select_voltage() may
> > > end up calling mmc_power_cycle(). This is not going to work for
> > > UHS-II.
> > >
> > > Either we need to modify mmc_select_voltage() so it becomes aware that
> > > it can be called for UHS-II initialization, allowing it to avoid the
> > > path to mmc_power_cycle() - or simply open code the part from
> > > mmc_select_voltage() for UHS-II here. I think I prefer the latter.
> > >
> > > > +
> > > > +       /*
> > > > +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > > > +        * ocr value to rocr.
> > > > +        */
> > > > +       if (!rocr) {
> > > > +               if (host->ocr_avail) {
> > > > +                       rocr = host->ocr_avail;
> > >
> > > host->ocr_avail should really be checked in when the host driver calls
> > > mmc_add_host(). It must not be zero, then we should let mmc_add_host()
> > > return an error code. I look into this and send a patch for this
> > > separately.
> > >
> > > In other words, you should not need to check it here, but just trust that's set.
> > >
> > > > +               } else {
> > > > +                       pr_err("%s: %s: there is no valid OCR.\n",
> > > > +                              mmc_hostname(host), __func__);
> > > > +                       return -EINVAL;
> > > > +               }
> > > > +       }
> > > > +
> > > > +       /* Wait SD power on ready */
> > > > +       ocr = rocr;
> > > > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
> > > > +                      __func__);
> > > > +               return err;
> > > > +       }
> > > > +
> > > > +       err = mmc_send_cid(host, cid);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
> > > > +                      __func__);
> > > > +               return err;
> > > > +       }
> > > > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > > > +
> > > > +       /*
> > > > +        * Call the optional HC's init_card function to handle quirks.
> > > > +        */
> > > > +       if (host->ops->init_card)
> > > > +               host->ops->init_card(host, card);
> > >
> > > This can be removed, as it's only for the legacy interface, I think.
> > >
> > > > +
> > > > +       /*
> > > > +        * For native busses:  get card RCA and quit open drain mode.
> > > > +        */
> > > > +       err = mmc_send_relative_addr(host, &card->rca);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
> > > > +                      __func__);
> > > > +               return err;
> > > > +       }
> > > > +
> > > > +       err = mmc_sd_get_csd(card);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
> > > > +                      __func__);
> > > > +               return err;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * Select card, as all following commands rely on that.
> > > > +        */
> > > > +       err = mmc_select_card(card);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
> > > > +                      __func__);
> > > > +               return err;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * Check if read-only switch is active.
> > > > +        */
> > > > +       ro = sd_uhs2_get_ro(host);
> > > > +       if (ro < 0) {
> > > > +               pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> > > > +                       mmc_hostname(host));
> > > > +       } else if (ro > 0) {
> > > > +               mmc_card_set_readonly(card);
> > > > +       }
> > > > +
> > > >         return 0;
> > > >  }
> > > >
> > > > +static void sd_uhs2_remove(struct mmc_host *host)
> > > > +{
> > > > +       mmc_remove_card(host->card);
> > > > +       host->card = NULL;
> > > > +}
> > > > +
> > > >  /*
> > > >   * Allocate the data structure for the mmc_card and run the UHS-II specific
> > > >   * initialization sequence.
> > > > @@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > > >         int err;
> > > >
> > > >         err = sd_uhs2_dev_init(host);
> > > > -       if (err)
> > > > +       if (err) {
> > > > +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
> > > >                 return err;
> > > > +       }
> > > >
> > > >         err = sd_uhs2_enum(host, &node_id);
> > > > -       if (err)
> > > > +       if (err) {
> > > > +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
> > > >                 return err;
> > > > +       }
> > > >
> > > >         card = mmc_alloc_card(host, &sd_type);
> > > >         if (IS_ERR(card))
> > > >                 return PTR_ERR(card);
> > > > +       host->card = card;
> > > >
> > > >         card->uhs2_config.node_id = node_id;
> > > >         card->type = MMC_TYPE_SD;
> > > > @@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > > >         if (err)
> > > >                 goto err;
> > > >
> > > > +       /* Change to Speed Range B if it is supported */
> > > > +       if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
> > > > +               err = sd_uhs2_change_speed(host, node_id);
> > > > +               if (err) {
> > > > +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> > > > +                              mmc_hostname(host), __func__);
> > > > +                       return err;
> > > > +               }
> > > > +       }
> > > > +
> > > >         err = sd_uhs2_config_write(host, card);
> > > >         if (err)
> > > >                 goto err;
> > > > @@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > > >         if (err)
> > > >                 goto err;
> > > >
> > > > -       host->card = card;
> > > >         return 0;
> > > >
> > > >  err:
> > > > -       mmc_remove_card(card);
> > > > +       sd_uhs2_remove(host);
> > > >         return err;
> > > >  }
> > >
> > > [...]
> > >
> > > Kind regards
> > > Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-03-24 10:50         ` AKASHI Takahiro
@ 2022-03-25  3:53           ` Lai Jason
  2022-03-25  8:10             ` Ulf Hansson
  0 siblings, 1 reply; 27+ messages in thread
From: Lai Jason @ 2022-03-25  3:53 UTC (permalink / raw)
  To: AKASHI Takahiro, Ulf Hansson, Jason Lai, Adrian Hunter,
	linux-mmc, dlunev, Ben Chuang, GregTu[杜啟軒],
	Jason Lai, otis.wu

On Thu, Mar 24, 2022 at 6:50 PM AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
>
> On Thu, Mar 24, 2022 at 11:22:16AM +0100, Ulf Hansson wrote:
> > On Thu, 24 Mar 2022 at 02:29, AKASHI Takahiro
> > <takahiro.akashi@linaro.org> wrote:
> > >
> > > On Wed, Mar 23, 2022 at 05:15:59PM +0100, Ulf Hansson wrote:
> > > > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > > > >
> > > > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > >
> > > > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > > > Part 1 - PHY Initialization:
> > > > >          Every host controller may need their own private operation to
> > > > >          establish LINK between controller and card. So we add a new member
> > > > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > > > >          controller use.
> > > > > Part 2 - Card Initialization:
> > > > >          This part can be divided into 6 substeps.
> > > > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > > > >          2. Send UHS-II CCMD ENUMERATE to card.
> > > > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > > > >             of card.
> > > > >          4. Host compares capabilities of host controller and card, then
> > > > >             write the negotiated values to Setting field in CFG_REG of card
> > > > >             through UHS-II Native Write CCMD.
> > > > >          5. Switch host controller's clock to Range B if it is supported by
> > > > >             both host controller and card.
> > > > >          6. Execute legacy SD initialization flow.
> > > > > Part 3 - Provide a function to transform legacy SD command packet into
> > > > >          UHS-II SD-TRAN DCMD packet.
> > > > >
> > > > > Most of the code added above came from Intel's original patch[3].
> > > > >
> > > > > [3]
> > > > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > > > git-send-email-yi.y.sun@intel.com/
> > >
> > > To honor the original work, we should add Intel's copyright notice here
> > > as I did before.
> >
> > I think Jason already did, at least for some of the files in this series.
> >
> > Note also that, the initial code that was posted, is very far from
> > what code that is going to be merged. Simply because the quality was
> > very poor and not acceptable for the upstream kernel.
>
> I don't think so.
> I don't see much difference from my modified version[1] which is also
> based on Intel's original work.
>
> [1] https://www.spinics.net/lists/linux-mmc/msg57321.html
>

I agree with most of what Takahiro said. The data structure and command
packet for UHS-II followed Intel's original work. But Intel's original work
cannot actually work. UHS-II SD card was supported successfully after Ben
and Takahiro's hard working on patch set [RFC V3.1] Add support UHS-II for
GL9755: https://patchwork.kernel.org/project/linux-mmc/list/?series=378627&archive=both

In my opinion, keeping Intel's copyright information at head of sd_uhs2.c and
sd_uhs2.h like listed below is a good idea. What do you think?

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  linux/drivers/mmc/core/uhs2.c - UHS-II driver
 *
 *  Copyright (C) 2020 Linaro Limited
 *  Author: Ulf Hansson <ulf.hansson@linaro.org>
 *  Copyright (C) 2014 Intel Corp, All Rights Reserved.
 *  Copyright (C) 2020 Genesys Logic, Inc.
 *  Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
 *  Authors: Jason Lai <jason.lai@genesyslogic.com.tw>
 *  Copyright (C) 2020 Linaro Limited
 *  Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
 */

kind regards,
Jason Lai

> -Takahiro Akashi
>
> > That said, I am
> > not sure we need to keep the copyrights for this, but I leave that
> > call to Jason to decide.
> >
> > >
> > > -Takahiro Akashi
> > >
> >
> > Kind regards
> > Uffe
> >
> > >
> > > > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > > ---
> > > > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > > > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > > > index 800957f74632..f1e8e30301eb 100644
> > > > > --- a/drivers/mmc/core/sd_uhs2.c
> > > > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > > > @@ -3,6 +3,7 @@
> > > > >   * Copyright (C) 2021 Linaro Ltd
> > > > >   *
> > > > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > >   *
> > > > >   * Support for SD UHS-II cards
> > > > >   */
> > > > > @@ -10,19 +11,31 @@
> > > > >
> > > > >  #include <linux/mmc/host.h>
> > > > >  #include <linux/mmc/card.h>
> > > > > +#include <linux/mmc/mmc.h>
> > > > > +#include <linux/mmc/sd_uhs2.h>
> > > > >
> > > > >  #include "core.h"
> > > > >  #include "bus.h"
> > > > > +#include "card.h"
> > > > >  #include "sd.h"
> > > > > +#include "sd_ops.h"
> > > > >  #include "mmc_ops.h"
> > > > > +#include "sd_uhs2.h"
> > > > >
> > > > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > > > >
> > > > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > > > >  {
> > > > >         struct mmc_ios *ios = &host->ios;
> > > > > +       int err = 0;
> > > > >
> > > > > -       return host->ops->uhs2_set_ios(host, ios);
> > > > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > > > +                ios->timing);
> > > > > +
> > > > > +       host->ops->set_ios(host, ios);
> > > >
> > > > We discussed using the ->set_ios() callback in a previous version. To
> > > > repeat myself, I don't think it's a good idea. UHS-II needs an
> > > > entirely different power sequence than the legacy interface(s), hence
> > > > I think it's simply cleaner to separate them.
> > > >
> > > > To move forward, I see two options.
> > > > 1) Use only the ->uhs2_host_operation() ops.
> > > > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > > > ->uhs2_host_operation() ops.
> > > >
> > > > Both options work for me. However, perhaps if you could incorporate
> > > > the changes done on the host driver at next submission, it becomes
> > > > easier for me to understand what makes best sense.
> > > >
> > > > > +
> > > > > +       return err;
> > > > >  }
> > > > >
> > > > >  static int sd_uhs2_power_up(struct mmc_host *host)
> > > > > @@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> > > > >         sd_uhs2_set_ios(host);
> > > > >  }
> > > >
> > > > [...]
> > > >
> > > > >
> > > > >  /*
> > > > > @@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> > > > >   */
> > > > >  static int sd_uhs2_dev_init(struct mmc_host *host)
> > > > >  {
> > > > > +       struct mmc_command cmd = {0};
> > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > +       u32 cnt;
> > > > > +       u32 dap, gap, resp_gap;
> > > > > +       u16 header = 0, arg = 0;
> > > >
> > > > No need to initiate these.
> > > >
> > > > > +       u32 payload[1];
> > > >
> > > > u32?
> > > >
> > > > > +       u8 plen = 1;
> > > > > +       u8 gd = 0, cf = 1;
> > > > > +       u8 resp[6] = {0};
> > > > > +       u8 resp_len = 6;
> > > >
> > > > Many of these variables are just constant numbers. If it makes sense
> > > > to add definitions for them, then please do that instead. If not, just
> > > > give the value directly in the code.
> > > >
> > > > For example: plen = 1; (I assume that is payload length). This can
> > > > just be given as an in-parameter to sd_uhs2_cmd_assemble(), without
> > > > further explanation.
> > > >
> > > > The point is, sd_uhs2_cmd_assemble() should have a well described
> > > > description of its in-parameters, so no need for further descriptions,
> > > > I think.
> > > >
> > > > This comment applies to all the new code/functions that are added in
> > > > the $subject patch. Please go through all of the code and fix this.
> > > >
> > > >
> > > > > +       int err;
> > > > > +
> > > > > +       dap = host->uhs2_caps.dap;
> > > > > +       gap = host->uhs2_caps.gap;
> > > > > +
> > > > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> > > > > +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> > > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > > > +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> > > > > +
> > > > > +       /*
> > > > > +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > > > > +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > > > > +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > > > > +        * 1 second.
> > > > > +        */
> > > > > +       cmd.busy_timeout = 1000;
> > > > > +
> > > > > +       /*
> > > > > +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > > > > +        * When the number of the DEVICE_INIT commands is reach to
> > > > > +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > > > > +        * and regard it as an error.
> > > > > +        */
> > > > > +       for (cnt = 0; cnt < 30; cnt++) {
> > > > > +               payload[0] = ((dap & 0xF) << 12) |
> > > > > +                             (cf << 11)         |
> > > > > +                             ((gd & 0xF) << 4)  |
> > > > > +                             (gap & 0xF);
> > > >
> > > > To me, it looks like the payload data deserves to be explained a bit.
> > > > Perhaps you can add a comment explaining what pieces it consists of so
> > > > this becomes more clear?
> > > >
> > > > > +
> > > > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
> > > > > +
> > > > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > > > +
> > > > > +               if (err) {
> > > > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > > > +                              mmc_hostname(host), __func__, err);
> > > > > +                       return -EIO;
> > > >
> > > > Why do you override the original error code that was returned from
> > > > mmc_wait_for_cmd()?
> > > >
> > > > Normally it's preferred to keep the error code, unless there is good
> > > > reason not to.
> > > >
> > > > Again, I won't add more comments like this in the code from the
> > > > $subject patch. But please go through it all to avoid this kind of
> > > > thing.
> > > >
> > > > > +               }
> > > > > +
> > > > > +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> > > > > +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> > > > > +                              mmc_hostname(host));
> > > > > +                       return -EIO;
> > > > > +               }
> > > > > +
> > > > > +               if (resp[5] & 0x8) {
> > > > > +                       host->uhs2_caps.group_desc = gd;
> > > > > +                       break;
> > > >
> > > > I suggest you do a return 0 here. In this way you can skip the check
> > > > "if (cnt == 30)" below and just return an error code instead.
> > > >
> > > > > +               }
> > > > > +               resp_gap = resp[4] & 0x0F;
> > > > > +               if (gap == resp_gap)
> > > > > +                       gd++;
> > > > > +       }
> > > > > +       if (cnt == 30) {
> > > > > +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> > > > > +                      mmc_hostname(host));
> > > > > +               return -EIO;
> > > > > +       }
> > > > > +
> > > > >         return 0;
> > > > >  }
> > > > >
> > > >
> > > > >  static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
> > > > >  {
> > > > > +       struct mmc_command cmd = {0};
> > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > +       u16 header = 0, arg = 0;
> > > > > +       u32 cap;
> > > > > +       int err;
> > > > > +
> > > > > +       header = UHS2_NATIVE_PACKET |
> > > > > +                UHS2_PACKET_TYPE_CCMD |
> > > > > +                card->uhs2_config.node_id;
> > > > > +       arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
> > > > > +              UHS2_NATIVE_CMD_READ |
> > > > > +              UHS2_NATIVE_CMD_PLEN_4B |
> > > > > +              (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
> > > > > +
> > > > > +       /* There is no payload because per spec, there should be
> > > > > +        * no payload field for read CCMD.
> > > > > +        * Plen is set in arg. Per spec, plen for read CCMD
> > > > > +        * represents the len of read data which is assigned in payload
> > > > > +        * of following RES (p136).
> > > > > +        */
> > > > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > > >
> > > > We are reading the configuration data here and onwards, piece by
> > > > piece. Perhaps if you can add a small comment about each piece we are
> > > > reading, before each call to mmc_wait_for_cmd(), that can help to
> > > > easier understand what goes on.
> > > >
> > > > [...]
> > > >
> > > > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > > >  {
> > > > > +       struct mmc_command cmd = {0};
> > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > +       u16 header = 0, arg = 0;
> > > > > +       u32 payload[2];
> > > > > +       u8 nMinDataGap;
> > > > > +       u8 plen;
> > > > > +       int err;
> > > > > +       u8 resp[5] = {0};
> > > > > +       u8 resp_len = 5;
> > > > > +
> > > > > +       header = UHS2_NATIVE_PACKET |
> > > > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > > +
> > > > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > > > +               /* Support HD */
> > > > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> > > >
> > > > How is the uhs2_caps.flags field intended to be used? To me it looks
> > > > like a way for the mmc core to exchange status/configuration
> > > > information about the initialization process of the card, with the mmc
> > > > host driver. Perhaps there is more too. Is that correct?
> > > >
> > > > If so, I think it looks quite similar for what we have in the struct
> > > > mmc_ios, for the legacy interface(s). I am not saying we should use
> > > > that, just trying to understand what would suit best here.
> > > >
> > > > > +               nMinDataGap = 1;
> > > > > +       } else {
> > > > > +               /* Only support 2L-FD so far */
> > > > > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > > > > +               nMinDataGap = 3;
> > > > > +       }
> > > > > +
> > > > > +       /*
> > > > > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > > > > +        * defined in UHS-II addendem Ver1.01 are optional.
> > > > > +        */
> > > > > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > > > > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > > >
> > > > [...]
> > > >
> > > > > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > > > > +{
> > > >
> > > > Looks like the in-parameter "hibernate" is superfluous, as it's always
> > > > set to "false" by the caller.
> > > >
> > > > > +       struct mmc_command cmd = {0};
> > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > +       u16 header = 0, arg = 0;
> > > > > +       u32 payload[1];
> > > > > +       u8 plen = 1;
> > > > > +       int err;
> > > > > +
> > > > > +       /* Disable Normal INT */
> > > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > > > > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > > > > +                      mmc_hostname(host), __func__);
> > > > > +               return -EIO;
> > > > > +       }
> > > > > +
> > > > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > > > +
> > > > > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > > > > +               UHS2_NATIVE_CMD_WRITE |
> > > > > +               UHS2_NATIVE_CMD_PLEN_4B |
> > > > > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > > > > +
> > > > > +       if (hibernate)
> > > > > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > > > > +
> > > > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > > > > +
> > > > > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > > > +                      mmc_hostname(host), __func__, err);
> > > > > +               return -EIO;
> > > > > +       }
> > > > > +
> > > > > +       /* Check Dormant State in Present */
> > > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > > > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > > > > +                      mmc_hostname(host), __func__);
> > > > > +               return -EIO;
> > > > > +       }
> > > > > +
> > > > > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > > > > +
> > > > >         return 0;
> > > > >  }
> > > > >
> > > > > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > > > > +{
> > > > > +       struct mmc_command cmd = {0};
> > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > +       u16 header = 0, arg = 0;
> > > > > +       int err;
> > > > > +       int timeout = 100;
> > > > > +
> > > > > +       /* Change Speed Range at controller side. */
> > > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
> > > > > +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
> > > > > +                      mmc_hostname(host), __func__);
> > > > > +               return -EIO;
> > > > > +       }
> > > > > +
> > > > > +       err = sd_uhs2_go_dormant(host, false, node_id);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > > > > +                      mmc_hostname(host), __func__, err);
> > > > > +               return -EIO;
> > > > > +       }
> > > > > +
> > > > > +       /* restore sd clock */
> > > > > +       mmc_delay(5);
> > > > > +       host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);
> > > >
> > > > I think the code can be a bit better structured here. More precisely,
> > > > since sd_uhs2_go_dormant() is the one that calls
> > > > ->uhs2_host_operation(host, UHS2_DISABLE_INT) and
> > > > ->uhs2_host_operation(host, UHS2_DISABLE_CLK), it's then up to
> > > > sd_uhs2_change_speed() to restore these changes.
> > > >
> > > > To me, it would be more clear if both enabling and disabling of the
> > > > clock /interrupt are managed in sd_uhs2_change_speed().
> > > >
> > > > > +
> > > > > +       /* Enable Normal INT */
> > > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
> > > > > +               pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
> > > > > +                      mmc_hostname(host), __func__);
> > > > > +               return -EIO;
> > > > > +       }
> > > > > +
> > > > > +       /*
> > > > > +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> > > > > +        * switch to Active State
> > > > > +        */
> > > > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > > +               UHS2_NATIVE_CMD_READ |
> > > > > +               UHS2_NATIVE_CMD_PLEN_8B |
> > > > > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > > +       do {
> > > > > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > > > > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > > > > +               if (err) {
> > > > > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > > > +                              mmc_hostname(host), __func__, err);
> > > > > +                       return -EIO;
> > > > > +               }
> > > > > +
> > > > > +               if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> > > > > +                       break;
> > > > > +
> > > > > +               timeout--;
> > > > > +               if (timeout == 0) {
> > > > > +                       pr_err("%s: %s: Not switch to Active in 100 ms\n",
> > > > > +                              mmc_hostname(host), __func__);
> > > > > +                       return -EIO;
> > > > > +               }
> > > > > +
> > > > > +               mmc_delay(1);
> > > > > +       } while (1);
> > > >
> > > > We really want to avoid these kinds of polling loops, for several
> > > > reasons. Please convert into using __mmc_poll_for_busy() instead.
> > > >
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static int sd_uhs2_get_ro(struct mmc_host *host)
> > > > > +{
> > > > > +       int ro;
> > > > > +
> > > > > +       /*
> > > > > +        * Some systems don't feature a write-protect pin and don't need one.
> > > > > +        * E.g. because they only have micro-SD card slot. For those systems
> > > > > +        * assume that the SD card is always read-write.
> > > > > +        */
> > > > > +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> > > > > +               return 0;
> > > > > +
> > > > > +       if (!host->ops->get_ro)
> > > > > +               return -1;
> > > > > +
> > > > > +       ro = host->ops->get_ro(host);
> > > > > +
> > > > > +       return ro;
> > > >
> > > > This can be replaced with mmc_sd_get_ro(). Let's avoid the open coding
> > > > and make that function being shared instead.
> > > >
> > > > > +}
> > > > > +
> > > > >  /*
> > > > >   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> > > > >   * commands/requests to be backwards compatible through the legacy SD protocol.
> > > > > @@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > > >   */
> > > > >  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> > > > >  {
> > > > > +       int err;
> > > > > +       u32 cid[4];
> > > > > +       u32 ocr;
> > > > > +       u32 rocr = 0;
> > > > > +       int ro;
> > > > > +
> > > > > +       WARN_ON(!host->claimed);
> > > >
> > > > Drop this, it's an internal function, we should know that the host is
> > > > claimed before calling sd_uhs2_legacy_init().
> > > >
> > > > > +
> > > > > +       /* Send CMD0 to reset SD card */
> > > > > +       mmc_go_idle(host);
> > > > > +
> > > > > +       /* Send CMD8 to communicate SD interface operation condition */
> > > > > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> > > > > +                      mmc_hostname(host), __func__);
> > > >
> > > > Please drop these prints for every command/operation that fails. We
> > > > already have trace/debug options for commands/requests.
> > > >
> > > > This applies to all the below code as well (perhaps there are few
> > > > cases not covered by the existing trace/debug support, those may be
> > > > converted to pr_debug().
> > > >
> > > > > +               return err;
> > > > > +       }
> > > > > +
> > > > > +       /*
> > > > > +        * Probe SD card working voltage.
> > > > > +        */
> > > > > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> > > > > +                      mmc_hostname(host), __func__);
> > > > > +               return err;
> > > > > +       }
> > > > > +       card->ocr = ocr;
> > > > > +
> > > > > +       /*
> > > > > +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > > > > +        * these bits as being in-valid and especially also bit7.
> > > > > +        */
> > > > > +       ocr &= ~0x7FFF;
> > > > > +       rocr = mmc_select_voltage(host, ocr);
> > > >
> > > > If the host has MMC_CAP2_FULL_PWR_CYCLE set, mmc_select_voltage() may
> > > > end up calling mmc_power_cycle(). This is not going to work for
> > > > UHS-II.
> > > >
> > > > Either we need to modify mmc_select_voltage() so it becomes aware that
> > > > it can be called for UHS-II initialization, allowing it to avoid the
> > > > path to mmc_power_cycle() - or simply open code the part from
> > > > mmc_select_voltage() for UHS-II here. I think I prefer the latter.
> > > >
> > > > > +
> > > > > +       /*
> > > > > +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > > > > +        * ocr value to rocr.
> > > > > +        */
> > > > > +       if (!rocr) {
> > > > > +               if (host->ocr_avail) {
> > > > > +                       rocr = host->ocr_avail;
> > > >
> > > > host->ocr_avail should really be checked in when the host driver calls
> > > > mmc_add_host(). It must not be zero, then we should let mmc_add_host()
> > > > return an error code. I look into this and send a patch for this
> > > > separately.
> > > >
> > > > In other words, you should not need to check it here, but just trust that's set.
> > > >
> > > > > +               } else {
> > > > > +                       pr_err("%s: %s: there is no valid OCR.\n",
> > > > > +                              mmc_hostname(host), __func__);
> > > > > +                       return -EINVAL;
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > > +       /* Wait SD power on ready */
> > > > > +       ocr = rocr;
> > > > > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
> > > > > +                      __func__);
> > > > > +               return err;
> > > > > +       }
> > > > > +
> > > > > +       err = mmc_send_cid(host, cid);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
> > > > > +                      __func__);
> > > > > +               return err;
> > > > > +       }
> > > > > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > > > > +
> > > > > +       /*
> > > > > +        * Call the optional HC's init_card function to handle quirks.
> > > > > +        */
> > > > > +       if (host->ops->init_card)
> > > > > +               host->ops->init_card(host, card);
> > > >
> > > > This can be removed, as it's only for the legacy interface, I think.
> > > >
> > > > > +
> > > > > +       /*
> > > > > +        * For native busses:  get card RCA and quit open drain mode.
> > > > > +        */
> > > > > +       err = mmc_send_relative_addr(host, &card->rca);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
> > > > > +                      __func__);
> > > > > +               return err;
> > > > > +       }
> > > > > +
> > > > > +       err = mmc_sd_get_csd(card);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
> > > > > +                      __func__);
> > > > > +               return err;
> > > > > +       }
> > > > > +
> > > > > +       /*
> > > > > +        * Select card, as all following commands rely on that.
> > > > > +        */
> > > > > +       err = mmc_select_card(card);
> > > > > +       if (err) {
> > > > > +               pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
> > > > > +                      __func__);
> > > > > +               return err;
> > > > > +       }
> > > > > +
> > > > > +       /*
> > > > > +        * Check if read-only switch is active.
> > > > > +        */
> > > > > +       ro = sd_uhs2_get_ro(host);
> > > > > +       if (ro < 0) {
> > > > > +               pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> > > > > +                       mmc_hostname(host));
> > > > > +       } else if (ro > 0) {
> > > > > +               mmc_card_set_readonly(card);
> > > > > +       }
> > > > > +
> > > > >         return 0;
> > > > >  }
> > > > >
> > > > > +static void sd_uhs2_remove(struct mmc_host *host)
> > > > > +{
> > > > > +       mmc_remove_card(host->card);
> > > > > +       host->card = NULL;
> > > > > +}
> > > > > +
> > > > >  /*
> > > > >   * Allocate the data structure for the mmc_card and run the UHS-II specific
> > > > >   * initialization sequence.
> > > > > @@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > > > >         int err;
> > > > >
> > > > >         err = sd_uhs2_dev_init(host);
> > > > > -       if (err)
> > > > > +       if (err) {
> > > > > +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
> > > > >                 return err;
> > > > > +       }
> > > > >
> > > > >         err = sd_uhs2_enum(host, &node_id);
> > > > > -       if (err)
> > > > > +       if (err) {
> > > > > +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
> > > > >                 return err;
> > > > > +       }
> > > > >
> > > > >         card = mmc_alloc_card(host, &sd_type);
> > > > >         if (IS_ERR(card))
> > > > >                 return PTR_ERR(card);
> > > > > +       host->card = card;
> > > > >
> > > > >         card->uhs2_config.node_id = node_id;
> > > > >         card->type = MMC_TYPE_SD;
> > > > > @@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > > > >         if (err)
> > > > >                 goto err;
> > > > >
> > > > > +       /* Change to Speed Range B if it is supported */
> > > > > +       if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
> > > > > +               err = sd_uhs2_change_speed(host, node_id);
> > > > > +               if (err) {
> > > > > +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> > > > > +                              mmc_hostname(host), __func__);
> > > > > +                       return err;
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > >         err = sd_uhs2_config_write(host, card);
> > > > >         if (err)
> > > > >                 goto err;
> > > > > @@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> > > > >         if (err)
> > > > >                 goto err;
> > > > >
> > > > > -       host->card = card;
> > > > >         return 0;
> > > > >
> > > > >  err:
> > > > > -       mmc_remove_card(card);
> > > > > +       sd_uhs2_remove(host);
> > > > >         return err;
> > > > >  }
> > > >
> > > > [...]
> > > >
> > > > Kind regards
> > > > Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-03-25  3:53           ` Lai Jason
@ 2022-03-25  8:10             ` Ulf Hansson
  0 siblings, 0 replies; 27+ messages in thread
From: Ulf Hansson @ 2022-03-25  8:10 UTC (permalink / raw)
  To: Lai Jason
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

On Fri, 25 Mar 2022 at 04:54, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
>
> On Thu, Mar 24, 2022 at 6:50 PM AKASHI Takahiro
> <takahiro.akashi@linaro.org> wrote:
> >
> > On Thu, Mar 24, 2022 at 11:22:16AM +0100, Ulf Hansson wrote:
> > > On Thu, 24 Mar 2022 at 02:29, AKASHI Takahiro
> > > <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > On Wed, Mar 23, 2022 at 05:15:59PM +0100, Ulf Hansson wrote:
> > > > > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > > > > >
> > > > > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > > >
> > > > > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > > > > Part 1 - PHY Initialization:
> > > > > >          Every host controller may need their own private operation to
> > > > > >          establish LINK between controller and card. So we add a new member
> > > > > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > > > > >          controller use.
> > > > > > Part 2 - Card Initialization:
> > > > > >          This part can be divided into 6 substeps.
> > > > > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > > > > >          2. Send UHS-II CCMD ENUMERATE to card.
> > > > > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > > > > >             of card.
> > > > > >          4. Host compares capabilities of host controller and card, then
> > > > > >             write the negotiated values to Setting field in CFG_REG of card
> > > > > >             through UHS-II Native Write CCMD.
> > > > > >          5. Switch host controller's clock to Range B if it is supported by
> > > > > >             both host controller and card.
> > > > > >          6. Execute legacy SD initialization flow.
> > > > > > Part 3 - Provide a function to transform legacy SD command packet into
> > > > > >          UHS-II SD-TRAN DCMD packet.
> > > > > >
> > > > > > Most of the code added above came from Intel's original patch[3].
> > > > > >
> > > > > > [3]
> > > > > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > > > > git-send-email-yi.y.sun@intel.com/
> > > >
> > > > To honor the original work, we should add Intel's copyright notice here
> > > > as I did before.
> > >
> > > I think Jason already did, at least for some of the files in this series.
> > >
> > > Note also that, the initial code that was posted, is very far from
> > > what code that is going to be merged. Simply because the quality was
> > > very poor and not acceptable for the upstream kernel.
> >
> > I don't think so.
> > I don't see much difference from my modified version[1] which is also
> > based on Intel's original work.
> >
> > [1] https://www.spinics.net/lists/linux-mmc/msg57321.html
> >
>
> I agree with most of what Takahiro said. The data structure and command
> packet for UHS-II followed Intel's original work. But Intel's original work
> cannot actually work. UHS-II SD card was supported successfully after Ben
> and Takahiro's hard working on patch set [RFC V3.1] Add support UHS-II for
> GL9755: https://patchwork.kernel.org/project/linux-mmc/list/?series=378627&archive=both
>
> In my opinion, keeping Intel's copyright information at head of sd_uhs2.c and
> sd_uhs2.h like listed below is a good idea. What do you think?

I have no problem with that.

However, I have to admit that I still think we have some more work to
do with the patches, before I am ready to queue them. In other words,
the decision is probably best taken a bit further down the road.

Note also that we have other ways of giving companies/people credit,
without using the copyright thing. Like the co-developed by tag and
mentioning companies/people in file-headers and commit messages, for
example.

>
> // SPDX-License-Identifier: GPL-2.0-or-later
> /*
>  *  linux/drivers/mmc/core/uhs2.c - UHS-II driver
>  *
>  *  Copyright (C) 2020 Linaro Limited
>  *  Author: Ulf Hansson <ulf.hansson@linaro.org>
>  *  Copyright (C) 2014 Intel Corp, All Rights Reserved.
>  *  Copyright (C) 2020 Genesys Logic, Inc.
>  *  Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
>  *  Authors: Jason Lai <jason.lai@genesyslogic.com.tw>
>  *  Copyright (C) 2020 Linaro Limited
>  *  Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
>  */
>
> kind regards,
> Jason Lai

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-03-23 16:15   ` Ulf Hansson
  2022-03-24  1:29     ` AKASHI Takahiro
@ 2022-04-07 10:45     ` Lai Jason
  2022-04-07 15:00       ` Ulf Hansson
  1 sibling, 1 reply; 27+ messages in thread
From: Lai Jason @ 2022-04-07 10:45 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

Hi Uffe,

On Thu, Mar 24, 2022 at 12:16 AM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> >
> > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> >
> > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > Part 1 - PHY Initialization:
> >          Every host controller may need their own avtivation operation to
> >          establish LINK between controller and card. So we add a new member
> >          function(uhs2_detect_init) in struct mmc_host_ops for host
> >          controller use.
> > Part 2 - Card Initialization:
> >          This part can be divided into 6 substeps.
> >          1. Send UHS-II CCMD DEVICE_INIT to card.
> >          2. Send UHS-II CCMD ENUMERATE to card.
> >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> >             of card.
> >          4. Host compares capabilities of host controller and card, then
> >             write the negotiated values to Setting field in CFG_REG of card
> >             through UHS-II Native Write CCMD.
> >          5. Switch host controller's clock to Range B if it is supported by
> >             both host controller and card.
> >          6. Execute legacy SD initialization flow.
> > Part 3 - Provide a function to tranaform legacy SD command packet into
> >          UHS-II SD-TRAN DCMD packet.
> >
> > Most of the code added above came from Intel's original patch[3].
> >
> > [3]
> > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > git-send-email-yi.y.sun@intel.com/
> >
> > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > ---
> >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 817 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > index 800957f74632..f1e8e30301eb 100644
> > --- a/drivers/mmc/core/sd_uhs2.c
> > +++ b/drivers/mmc/core/sd_uhs2.c
> > @@ -3,6 +3,7 @@
> >   * Copyright (C) 2021 Linaro Ltd
> >   *
> >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> >   *
> >   * Support for SD UHS-II cards
> >   */
> > @@ -10,19 +11,31 @@
> >
> >  #include <linux/mmc/host.h>
> >  #include <linux/mmc/card.h>
> > +#include <linux/mmc/mmc.h>
> > +#include <linux/mmc/sd_uhs2.h>
> >
> >  #include "core.h"
> >  #include "bus.h"
> > +#include "card.h"
> >  #include "sd.h"
> > +#include "sd_ops.h"
> >  #include "mmc_ops.h"
> > +#include "sd_uhs2.h"
> >
> >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> >
> >  static int sd_uhs2_set_ios(struct mmc_host *host)
> >  {
> >         struct mmc_ios *ios = &host->ios;
> > +       int err = 0;
> >
> > -       return host->ops->uhs2_set_ios(host, ios);
> > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > +                ios->timing);
> > +
> > +       host->ops->set_ios(host, ios);
>
> We discussed using the ->set_ios() callback in a previous version. To
> repeat myself, I don't think it's a good idea. UHS-II needs an
> entirely different power sequence than the legacy interface(s), hence
> I think it's simply cleaner to separate them.
>
> To move forward, I see two options.
> 1) Use only the ->uhs2_host_operation() ops.
> 2) Use a combination of the ->uhs2_set_ios() ops and the
> ->uhs2_host_operation() ops.
>

I referred to the usage of "host->ops->set_ios" in core.c, it is called in
mmc_set_ios() and ".set_ios" is directed to sdhci_set_ios(), which is
located in mmc/host/sdhci.c. So I created sd_uhs2_set_ios() and call
host->ops->uhs2_set_ios() inside it. The ".uhs2_set_ios" is left to host
part to implement it.

> Both options work for me. However, perhaps if you could incorporate
> the changes done on the host driver at next submission, it becomes
> easier for me to understand what makes best sense.
>
> > +
> > +       return err;
> >  }
> >
> >  static int sd_uhs2_power_up(struct mmc_host *host)
> > @@ -45,6 +58,43 @@ static void sd_uhs2_power_off(struct mmc_host *host)
> >         sd_uhs2_set_ios(host);
> >  }
>
> [...]
>
> >
> >  /*
> > @@ -61,6 +119,77 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
> >   */
> >  static int sd_uhs2_dev_init(struct mmc_host *host)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u32 cnt;
> > +       u32 dap, gap, resp_gap;
> > +       u16 header = 0, arg = 0;
>
> No need to initiate these.
>
> > +       u32 payload[1];
>
> u32?
>

I will change it to __be32.

> > +       u8 plen = 1;
> > +       u8 gd = 0, cf = 1;
> > +       u8 resp[6] = {0};
> > +       u8 resp_len = 6;
>
> Many of these variables are just constant numbers. If it makes sense
> to add definitions for them, then please do that instead. If not, just
> give the value directly in the code.
>
> For example: plen = 1; (I assume that is payload length). This can
> just be given as an in-parameter to sd_uhs2_cmd_assemble(), without
> further explanation.
>
> The point is, sd_uhs2_cmd_assemble() should have a well described
> description of its in-parameters, so no need for further descriptions,
> I think.
>
> This comment applies to all the new code/functions that are added in
> the $subject patch. Please go through all of the code and fix this.
>

OK. I will remove these variables and use constant definitions to replace them.

>
> > +       int err;
> > +
> > +       dap = host->uhs2_caps.dap;
> > +       gap = host->uhs2_caps.gap;
> > +
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
> > +       arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_4B |
> > +              (UHS2_DEV_CMD_DEVICE_INIT >> 8);
> > +
> > +       /*
> > +        * Refer to UHS-II Addendum Version 1.02 section 6.3.1.
> > +        * Max. time from DEVICE_INIT CCMD EOP reception on Device
> > +        * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is
> > +        * 1 second.
> > +        */
> > +       cmd.busy_timeout = 1000;
> > +
> > +       /*
> > +        * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3.
> > +        * When the number of the DEVICE_INIT commands is reach to
> > +        * 30 tiems, Host shall stop issuing DEVICE_INIT command
> > +        * and regard it as an error.
> > +        */
> > +       for (cnt = 0; cnt < 30; cnt++) {
> > +               payload[0] = ((dap & 0xF) << 12) |
> > +                             (cf << 11)         |
> > +                             ((gd & 0xF) << 4)  |
> > +                             (gap & 0xF);
>
> To me, it looks like the payload data deserves to be explained a bit.
> Perhaps you can add a comment explaining what pieces it consists of so
> this becomes more clear?
>

The format of Header and Payload will be listed in include/linux/mmc/sd_uhs2.h

> > +
> > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg,  payload, plen, resp, resp_len);
> > +
> > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > +
> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                              mmc_hostname(host), __func__, err);
> > +                       return -EIO;
>
> Why do you override the original error code that was returned from
> mmc_wait_for_cmd()?
>
> Normally it's preferred to keep the error code, unless there is good
> reason not to.
>
> Again, I won't add more comments like this in the code from the
> $subject patch. But please go through it all to avoid this kind of
> thing.
>

OK.

> > +               }
> > +
> > +               if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
> > +                       pr_err("%s: DEVICE_INIT response is wrong!\n",
> > +                              mmc_hostname(host));
> > +                       return -EIO;
> > +               }
> > +
> > +               if (resp[5] & 0x8) {
> > +                       host->uhs2_caps.group_desc = gd;
> > +                       break;
>
> I suggest you do a return 0 here. In this way you can skip the check
> "if (cnt == 30)" below and just return an error code instead.
>

OK.

> > +               }
> > +               resp_gap = resp[4] & 0x0F;
> > +               if (gap == resp_gap)
> > +                       gd++;
> > +       }
> > +       if (cnt == 30) {
> > +               pr_err("%s: DEVICE_INIT fail, already 30 times!\n",
> > +                      mmc_hostname(host));
> > +               return -EIO;
> > +       }
> > +
> >         return 0;
> >  }
> >
>
> >  static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       u32 cap;
> > +       int err;
> > +
> > +       header = UHS2_NATIVE_PACKET |
> > +                UHS2_PACKET_TYPE_CCMD |
> > +                card->uhs2_config.node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_READ |
> > +              UHS2_NATIVE_CMD_PLEN_4B |
> > +              (UHS2_DEV_CONFIG_GEN_CAPS >> 8);
> > +
> > +       /* There is no payload because per spec, there should be
> > +        * no payload field for read CCMD.
> > +        * Plen is set in arg. Per spec, plen for read CCMD
> > +        * represents the len of read data which is assigned in payload
> > +        * of following RES (p136).
> > +        */
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
>
> We are reading the configuration data here and onwards, piece by
> piece. Perhaps if you can add a small comment about each piece we are
> reading, before each call to mmc_wait_for_cmd(), that can help to
> easier understand what goes on.
>

The response read back is the content of UHS-II Register.
I will put comments about the bit fields in that register.

> [...]
>
> >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> >  {
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       u32 payload[2];
> > +       u8 nMinDataGap;
> > +       u8 plen;
> > +       int err;
> > +       u8 resp[5] = {0};
> > +       u8 resp_len = 5;
> > +
> > +       header = UHS2_NATIVE_PACKET |
> > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +              UHS2_NATIVE_CMD_WRITE |
> > +              UHS2_NATIVE_CMD_PLEN_8B |
> > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +
> > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > +               /* Support HD */
> > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
>
> How is the uhs2_caps.flags field intended to be used? To me it looks
> like a way for the mmc core to exchange status/configuration
> information about the initialization process of the card, with the mmc
> host driver. Perhaps there is more too. Is that correct?
>
> If so, I think it looks quite similar for what we have in the struct
> mmc_ios, for the legacy interface(s). I am not saying we should use
> that, just trying to understand what would suit best here.
>

The usage of uhs2_caps.flags is spread out through core and host.
All operations related to it cannot be integrated into uhs2_set_ios()
simply. I recommend maintaining the status quo.

> > +               nMinDataGap = 1;
> > +       } else {
> > +               /* Only support 2L-FD so far */
> > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > +               nMinDataGap = 3;
> > +       }
> > +
> > +       /*
> > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > +        * defined in UHS-II addendem Ver1.01 are optional.
> > +        */
> > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
>
> [...]
>
> > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > +{
>
> Looks like the in-parameter "hibernate" is superfluous, as it's always
> set to "false" by the caller.
>

The in-parameter "hibernate" is designed according to UHS-II
specification. We did not use
it for now. But we are not sure if it will be set to true in future
use. So I suggest keeping it.

> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       u32 payload[1];
> > +       u8 plen = 1;
> > +       int err;
> > +
> > +       /* Disable Normal INT */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > +
> > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > +               UHS2_NATIVE_CMD_WRITE |
> > +               UHS2_NATIVE_CMD_PLEN_4B |
> > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > +
> > +       if (hibernate)
> > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > +
> > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > +
> > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return -EIO;
> > +       }
> > +
> > +       /* Check Dormant State in Present */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > +
> >         return 0;
> >  }
> >
> > +static int sd_uhs2_change_speed(struct mmc_host *host, u32 node_id)
> > +{
> > +       struct mmc_command cmd = {0};
> > +       struct uhs2_command uhs2_cmd = {};
> > +       u16 header = 0, arg = 0;
> > +       int err;
> > +       int timeout = 100;
> > +
> > +       /* Change Speed Range at controller side. */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_SET_SPEED_B)) {
> > +               pr_err("%s: %s: UHS2 SET_SPEED fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       err = sd_uhs2_go_dormant(host, false, node_id);
> > +       if (err) {
> > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
> > +                      mmc_hostname(host), __func__, err);
> > +               return -EIO;
> > +       }
> > +
> > +       /* restore sd clock */
> > +       mmc_delay(5);
> > +       host->ops->uhs2_host_operation(host, UHS2_ENABLE_CLK);
>
> I think the code can be a bit better structured here. More precisely,
> since sd_uhs2_go_dormant() is the one that calls
> ->uhs2_host_operation(host, UHS2_DISABLE_INT) and
> ->uhs2_host_operation(host, UHS2_DISABLE_CLK), it's then up to
> sd_uhs2_change_speed() to restore these changes.
>
> To me, it would be more clear if both enabling and disabling of the
> clock /interrupt are managed in sd_uhs2_change_speed().
>

OK.

> > +
> > +       /* Enable Normal INT */
> > +       if (!host->ops->uhs2_host_operation(host, UHS2_ENABLE_INT)) {
> > +               pr_err("%s: %s: UHS2 ENABLE_INT fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return -EIO;
> > +       }
> > +
> > +       /*
> > +        * According to UHS-II Addendum Version 1.01, chapter 6.2.3, wait card
> > +        * switch to Active State
> > +        */
> > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > +               UHS2_NATIVE_CMD_READ |
> > +               UHS2_NATIVE_CMD_PLEN_8B |
> > +               (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > +       do {
> > +               sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0);
> > +               err = mmc_wait_for_cmd(host, &cmd, 0);
> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > +                              mmc_hostname(host), __func__, err);
> > +                       return -EIO;
> > +               }
> > +
> > +               if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
> > +                       break;
> > +
> > +               timeout--;
> > +               if (timeout == 0) {
> > +                       pr_err("%s: %s: Not switch to Active in 100 ms\n",
> > +                              mmc_hostname(host), __func__);
> > +                       return -EIO;
> > +               }
> > +
> > +               mmc_delay(1);
> > +       } while (1);
>
> We really want to avoid these kinds of polling loops, for several
> reasons. Please convert into using __mmc_poll_for_busy() instead.
>

OK.

> > +
> > +       return 0;
> > +}
> > +
> > +static int sd_uhs2_get_ro(struct mmc_host *host)
> > +{
> > +       int ro;
> > +
> > +       /*
> > +        * Some systems don't feature a write-protect pin and don't need one.
> > +        * E.g. because they only have micro-SD card slot. For those systems
> > +        * assume that the SD card is always read-write.
> > +        */
> > +       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
> > +               return 0;
> > +
> > +       if (!host->ops->get_ro)
> > +               return -1;
> > +
> > +       ro = host->ops->get_ro(host);
> > +
> > +       return ro;
>
> This can be replaced with mmc_sd_get_ro(). Let's avoid the open coding
> and make that function being shared instead.
>

OK.

> > +}
> > +
> >  /*
> >   * Initialize the UHS-II card through the SD-TRAN transport layer. This enables
> >   * commands/requests to be backwards compatible through the legacy SD protocol.
> > @@ -107,9 +696,127 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> >   */
> >  static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card)
> >  {
> > +       int err;
> > +       u32 cid[4];
> > +       u32 ocr;
> > +       u32 rocr = 0;
> > +       int ro;
> > +
> > +       WARN_ON(!host->claimed);
>
> Drop this, it's an internal function, we should know that the host is
> claimed before calling sd_uhs2_legacy_init().
>

OK.

> > +
> > +       /* Send CMD0 to reset SD card */
> > +       mmc_go_idle(host);
> > +
> > +       /* Send CMD8 to communicate SD interface operation condition */
> > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > +       if (err) {
> > +               pr_err("%s: %s: SEND_IF_COND fail!\n",
> > +                      mmc_hostname(host), __func__);
>
> Please drop these prints for every command/operation that fails. We
> already have trace/debug options for commands/requests.
>
> This applies to all the below code as well (perhaps there are few
> cases not covered by the existing trace/debug support, those may be
> converted to pr_debug().
>

OK.

> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Probe SD card working voltage.
> > +        */
> > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n",
> > +                      mmc_hostname(host), __func__);
> > +               return err;
> > +       }
> > +       card->ocr = ocr;
> > +
> > +       /*
> > +        * Some SD cards claims an out of spec VDD voltage range. Let's treat
> > +        * these bits as being in-valid and especially also bit7.
> > +        */
> > +       ocr &= ~0x7FFF;
> > +       rocr = mmc_select_voltage(host, ocr);
>
> If the host has MMC_CAP2_FULL_PWR_CYCLE set, mmc_select_voltage() may
> end up calling mmc_power_cycle(). This is not going to work for
> UHS-II.
>
> Either we need to modify mmc_select_voltage() so it becomes aware that
> it can be called for UHS-II initialization, allowing it to avoid the
> path to mmc_power_cycle() - or simply open code the part from
> mmc_select_voltage() for UHS-II here. I think I prefer the latter.
>

I will implement sd_uhs2_select_voltage() to replace mmc_select_voltage().

> > +
> > +       /*
> > +        * Some cards have zero value of rocr in UHS-II mode. Assign host's
> > +        * ocr value to rocr.
> > +        */
> > +       if (!rocr) {
> > +               if (host->ocr_avail) {
> > +                       rocr = host->ocr_avail;
>
> host->ocr_avail should really be checked in when the host driver calls
> mmc_add_host(). It must not be zero, then we should let mmc_add_host()
> return an error code. I look into this and send a patch for this
> separately.
>
> In other words, you should not need to check it here, but just trust that's set.
>

OK.

> > +               } else {
> > +                       pr_err("%s: %s: there is no valid OCR.\n",
> > +                              mmc_hostname(host), __func__);
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> > +       /* Wait SD power on ready */
> > +       ocr = rocr;
> > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_OP_COND fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       err = mmc_send_cid(host, cid);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_CID fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > +
> > +       /*
> > +        * Call the optional HC's init_card function to handle quirks.
> > +        */
> > +       if (host->ops->init_card)
> > +               host->ops->init_card(host, card);
>
> This can be removed, as it's only for the legacy interface, I think.
>

OK.

Kind regards,
Jason


> > +
> > +       /*
> > +        * For native busses:  get card RCA and quit open drain mode.
> > +        */
> > +       err = mmc_send_relative_addr(host, &card->rca);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_RCA fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       err = mmc_sd_get_csd(card);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEND_CSD fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Select card, as all following commands rely on that.
> > +        */
> > +       err = mmc_select_card(card);
> > +       if (err) {
> > +               pr_err("%s: %s: SD_SEL_DSEL fail!\n", mmc_hostname(host),
> > +                      __func__);
> > +               return err;
> > +       }
> > +
> > +       /*
> > +        * Check if read-only switch is active.
> > +        */
> > +       ro = sd_uhs2_get_ro(host);
> > +       if (ro < 0) {
> > +               pr_warn("%s: host does not support read-only switch, assuming write-enable\n",
> > +                       mmc_hostname(host));
> > +       } else if (ro > 0) {
> > +               mmc_card_set_readonly(card);
> > +       }
> > +
> >         return 0;
> >  }
> >
> > +static void sd_uhs2_remove(struct mmc_host *host)
> > +{
> > +       mmc_remove_card(host->card);
> > +       host->card = NULL;
> > +}
> > +
> >  /*
> >   * Allocate the data structure for the mmc_card and run the UHS-II specific
> >   * initialization sequence.
> > @@ -121,16 +828,21 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         int err;
> >
> >         err = sd_uhs2_dev_init(host);
> > -       if (err)
> > +       if (err) {
> > +               pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host));
> >                 return err;
> > +       }
> >
> >         err = sd_uhs2_enum(host, &node_id);
> > -       if (err)
> > +       if (err) {
> > +               pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host));
> >                 return err;
> > +       }
> >
> >         card = mmc_alloc_card(host, &sd_type);
> >         if (IS_ERR(card))
> >                 return PTR_ERR(card);
> > +       host->card = card;
> >
> >         card->uhs2_config.node_id = node_id;
> >         card->type = MMC_TYPE_SD;
> > @@ -139,6 +851,16 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > +       /* Change to Speed Range B if it is supported */
> > +       if (host->uhs2_caps.flags & MMC_UHS2_SPEED_B) {
> > +               err = sd_uhs2_change_speed(host, node_id);
> > +               if (err) {
> > +                       pr_err("%s: %s: UHS2 sd_uhs2_change_speed() fail!\n",
> > +                              mmc_hostname(host), __func__);
> > +                       return err;
> > +               }
> > +       }
> > +
> >         err = sd_uhs2_config_write(host, card);
> >         if (err)
> >                 goto err;
> > @@ -147,20 +869,13 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > -       host->card = card;
> >         return 0;
> >
> >  err:
> > -       mmc_remove_card(card);
> > +       sd_uhs2_remove(host);
> >         return err;
> >  }
>
> [...]
>
> Kind regards
> Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 7/7] mmc: core: Support UHS-II card access
  2022-03-23 16:23   ` Ulf Hansson
@ 2022-04-07 11:00     ` Lai Jason
  2022-04-07 15:21       ` Ulf Hansson
  0 siblings, 1 reply; 27+ messages in thread
From: Lai Jason @ 2022-04-07 11:00 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

Hi Uffe,

On Thu, Mar 24, 2022 at 12:23 AM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> >
> > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> >
> > Embed UHS-II access functionality into the MMC request processing flow.
> >
> > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > ---
> >  drivers/mmc/core/core.c | 26 ++++++++++++++++++++++++--
> >  1 file changed, 24 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index fc3d8d61a97c..d2dcaa64bf27 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -31,6 +31,7 @@
> >  #include <linux/mmc/mmc.h>
> >  #include <linux/mmc/sd.h>
> >  #include <linux/mmc/slot-gpio.h>
> > +#include <linux/mmc/sd_uhs2.h>
> >
> >  #define CREATE_TRACE_POINTS
> >  #include <trace/events/mmc.h>
> > @@ -42,6 +43,7 @@
> >  #include "host.h"
> >  #include "sdio_bus.h"
> >  #include "pwrseq.h"
> > +#include "sd_uhs2.h"
> >
> >  #include "mmc_ops.h"
> >  #include "sd_ops.h"
> > @@ -335,6 +337,8 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
> >
> >  int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> >  {
> > +       struct uhs2_command uhs2_cmd;
> > +       u32 payload[4]; /* for maximum size */
> >         int err;
> >
> >         init_completion(&mrq->cmd_completion);
> > @@ -352,6 +356,13 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 return err;
> >
> > +       if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
> > +           host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
> > +               uhs2_cmd.payload = payload;
> > +               mrq->cmd->uhs2_cmd = &uhs2_cmd;
> > +               sd_uhs2_prepare_cmd(host, mrq);
> > +       }
> > +
> >         led_trigger_event(host->led, LED_FULL);
> >         __mmc_start_request(host, mrq);
> >
> > @@ -431,6 +442,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
> >   */
> >  int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> >  {
> > +       struct uhs2_command uhs2_cmd;
> > +       u32 payload[4]; /* for maximum size */
> >         int err;
> >
> >         /*
> > @@ -451,6 +464,13 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 goto out_err;
> >
> > +       if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
> > +           host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
> > +               uhs2_cmd.payload = payload;
> > +               mrq->cmd->uhs2_cmd = &uhs2_cmd;
> > +               sd_uhs2_prepare_cmd(host, mrq);
> > +       }
> > +
> >         err = host->cqe_ops->cqe_request(host, mrq);
> >         if (err)
> >                 goto out_err;
> > @@ -899,8 +919,10 @@ static inline void mmc_set_ios(struct mmc_host *host)
> >   */
> >  void mmc_set_chip_select(struct mmc_host *host, int mode)
> >  {
> > -       host->ios.chip_select = mode;
> > -       mmc_set_ios(host);
> > +       if (!(host->uhs2_caps.flags & MMC_UHS2_INITIALIZED)) {
> > +               host->ios.chip_select = mode;
> > +               mmc_set_ios(host);
> > +       }
>
> As I stated for patch6, rather than having these hacks spread out into
> the legacy code, we must not allow the ->set_ios() ops to be invoked
> when UHS-II is being used/initialized.
>

After checking sd_uhs2.c, mmc_set_chip_select() is called in mmc_go_idle()
and mmc_go_idle() is called in sd_uhs2_legacy_init(). Hence, I issue CMD0
directly in sd_uhs2_legacy_init().

> >  }
> >
>
> Moreover, I think $subject patch should be squashed into patch6. There
> is really no reason to have them split up like this. Or is there?
>

OK.

Kind regards,
Jason


> I have now walked through the series - and it seems like there are a
> few more pieces that we need to sort out, but it's certainly getting
> better and better.
>
> Kind regards
> Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-04-07 10:45     ` Lai Jason
@ 2022-04-07 15:00       ` Ulf Hansson
  2022-04-12  3:32         ` Lai Jason
  0 siblings, 1 reply; 27+ messages in thread
From: Ulf Hansson @ 2022-04-07 15:00 UTC (permalink / raw)
  To: Lai Jason
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

On Thu, 7 Apr 2022 at 12:45, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
>
> Hi Uffe,
>
> On Thu, Mar 24, 2022 at 12:16 AM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> >
> > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > >
> > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > >
> > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > Part 1 - PHY Initialization:
> > >          Every host controller may need their own avtivation operation to
> > >          establish LINK between controller and card. So we add a new member
> > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > >          controller use.
> > > Part 2 - Card Initialization:
> > >          This part can be divided into 6 substeps.
> > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > >          2. Send UHS-II CCMD ENUMERATE to card.
> > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > >             of card.
> > >          4. Host compares capabilities of host controller and card, then
> > >             write the negotiated values to Setting field in CFG_REG of card
> > >             through UHS-II Native Write CCMD.
> > >          5. Switch host controller's clock to Range B if it is supported by
> > >             both host controller and card.
> > >          6. Execute legacy SD initialization flow.
> > > Part 3 - Provide a function to tranaform legacy SD command packet into
> > >          UHS-II SD-TRAN DCMD packet.
> > >
> > > Most of the code added above came from Intel's original patch[3].
> > >
> > > [3]
> > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > git-send-email-yi.y.sun@intel.com/
> > >
> > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > ---
> > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > index 800957f74632..f1e8e30301eb 100644
> > > --- a/drivers/mmc/core/sd_uhs2.c
> > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > @@ -3,6 +3,7 @@
> > >   * Copyright (C) 2021 Linaro Ltd
> > >   *
> > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> > >   *
> > >   * Support for SD UHS-II cards
> > >   */
> > > @@ -10,19 +11,31 @@
> > >
> > >  #include <linux/mmc/host.h>
> > >  #include <linux/mmc/card.h>
> > > +#include <linux/mmc/mmc.h>
> > > +#include <linux/mmc/sd_uhs2.h>
> > >
> > >  #include "core.h"
> > >  #include "bus.h"
> > > +#include "card.h"
> > >  #include "sd.h"
> > > +#include "sd_ops.h"
> > >  #include "mmc_ops.h"
> > > +#include "sd_uhs2.h"
> > >
> > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > >
> > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > >  {
> > >         struct mmc_ios *ios = &host->ios;
> > > +       int err = 0;
> > >
> > > -       return host->ops->uhs2_set_ios(host, ios);
> > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > +                ios->timing);
> > > +
> > > +       host->ops->set_ios(host, ios);
> >
> > We discussed using the ->set_ios() callback in a previous version. To
> > repeat myself, I don't think it's a good idea. UHS-II needs an
> > entirely different power sequence than the legacy interface(s), hence
> > I think it's simply cleaner to separate them.
> >
> > To move forward, I see two options.
> > 1) Use only the ->uhs2_host_operation() ops.
> > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > ->uhs2_host_operation() ops.
> >
>
> I referred to the usage of "host->ops->set_ios" in core.c, it is called in
> mmc_set_ios() and ".set_ios" is directed to sdhci_set_ios(), which is
> located in mmc/host/sdhci.c. So I created sd_uhs2_set_ios() and call
> host->ops->uhs2_set_ios() inside it. The ".uhs2_set_ios" is left to host
> part to implement it.

I see. In that case, what you are looking for is an sdhci specific
callback, this wouldn't belong as part of the generic mmc host ops.

That said, I still think we need to choose between the two options I
suggested above. Otherwise, I fear that it will turn into a nightmare
for the mmc host drivers to support both UHS-II and the legacy
interface.

In other words, I strongly suggest that we must not call ->set_ios()
to manage the UHS-II interface.

>
> > Both options work for me. However, perhaps if you could incorporate
> > the changes done on the host driver at next submission, it becomes
> > easier for me to understand what makes best sense.
> >
> > > +
> > > +       return err;
> > >  }
> > >

[...]

> > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > >  {
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 payload[2];
> > > +       u8 nMinDataGap;
> > > +       u8 plen;
> > > +       int err;
> > > +       u8 resp[5] = {0};
> > > +       u8 resp_len = 5;
> > > +
> > > +       header = UHS2_NATIVE_PACKET |
> > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > +              UHS2_NATIVE_CMD_WRITE |
> > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > +
> > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > +               /* Support HD */
> > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> >
> > How is the uhs2_caps.flags field intended to be used? To me it looks
> > like a way for the mmc core to exchange status/configuration
> > information about the initialization process of the card, with the mmc
> > host driver. Perhaps there is more too. Is that correct?
> >
> > If so, I think it looks quite similar for what we have in the struct
> > mmc_ios, for the legacy interface(s). I am not saying we should use
> > that, just trying to understand what would suit best here.
> >
>
> The usage of uhs2_caps.flags is spread out through core and host.
> All operations related to it cannot be integrated into uhs2_set_ios()
> simply. I recommend maintaining the status quo.

What is puzzling to me, is that the data is stored below uhs2_caps.*
and that it's called "flags". It's not self-explanatory and it's not
consistent with the way we use the ->set_ios() callback, for the
legacy interface.

It looks to me that we should rather add a new variable to the struct
mmc_host and perhaps name it "uhs2_ios", to keep this data. Whether we
need to create a new struct for "uhs2_ios" or if it's better to extend
struct mmc_ios, I am not sure. I guess exploring this by writing the
code would tell us what is best suited.

>
> > > +               nMinDataGap = 1;
> > > +       } else {
> > > +               /* Only support 2L-FD so far */
> > > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > > +               nMinDataGap = 3;
> > > +       }
> > > +
> > > +       /*
> > > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > > +        * defined in UHS-II addendem Ver1.01 are optional.
> > > +        */
> > > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> >
> > [...]
> >
> > > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > > +{
> >
> > Looks like the in-parameter "hibernate" is superfluous, as it's always
> > set to "false" by the caller.
> >
>
> The in-parameter "hibernate" is designed according to UHS-II
> specification. We did not use
> it for now. But we are not sure if it will be set to true in future
> use. So I suggest keeping it.

I understand your point, but I don't agree, sorry. We don't want dead
code around in the kernel, so please remove it.

Perhaps what we can do, is to add a comment in sd_uhs2_go_dormant(),
somewhere we default to not use hibernate, we could simply explain
that hibernation is currently not supported.

>
> > > +       struct mmc_command cmd = {0};
> > > +       struct uhs2_command uhs2_cmd = {};
> > > +       u16 header = 0, arg = 0;
> > > +       u32 payload[1];
> > > +       u8 plen = 1;
> > > +       int err;
> > > +
> > > +       /* Disable Normal INT */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > +
> > > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > > +               UHS2_NATIVE_CMD_WRITE |
> > > +               UHS2_NATIVE_CMD_PLEN_4B |
> > > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > > +
> > > +       if (hibernate)
> > > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > > +
> > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > > +
> > > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > > +       if (err) {
> > > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > +                      mmc_hostname(host), __func__, err);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       /* Check Dormant State in Present */
> > > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > > +                      mmc_hostname(host), __func__);
> > > +               return -EIO;
> > > +       }
> > > +
> > > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > > +
> > >         return 0;
> > >  }

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 7/7] mmc: core: Support UHS-II card access
  2022-04-07 11:00     ` Lai Jason
@ 2022-04-07 15:21       ` Ulf Hansson
  0 siblings, 0 replies; 27+ messages in thread
From: Ulf Hansson @ 2022-04-07 15:21 UTC (permalink / raw)
  To: Lai Jason
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

On Thu, 7 Apr 2022 at 13:00, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
>
> Hi Uffe,
>
> On Thu, Mar 24, 2022 at 12:23 AM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> >
> > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > >
> > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > >
> > > Embed UHS-II access functionality into the MMC request processing flow.
> > >
> > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > ---
> > >  drivers/mmc/core/core.c | 26 ++++++++++++++++++++++++--
> > >  1 file changed, 24 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > > index fc3d8d61a97c..d2dcaa64bf27 100644
> > > --- a/drivers/mmc/core/core.c
> > > +++ b/drivers/mmc/core/core.c
> > > @@ -31,6 +31,7 @@
> > >  #include <linux/mmc/mmc.h>
> > >  #include <linux/mmc/sd.h>
> > >  #include <linux/mmc/slot-gpio.h>
> > > +#include <linux/mmc/sd_uhs2.h>
> > >
> > >  #define CREATE_TRACE_POINTS
> > >  #include <trace/events/mmc.h>
> > > @@ -42,6 +43,7 @@
> > >  #include "host.h"
> > >  #include "sdio_bus.h"
> > >  #include "pwrseq.h"
> > > +#include "sd_uhs2.h"
> > >
> > >  #include "mmc_ops.h"
> > >  #include "sd_ops.h"
> > > @@ -335,6 +337,8 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
> > >
> > >  int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> > >  {
> > > +       struct uhs2_command uhs2_cmd;
> > > +       u32 payload[4]; /* for maximum size */
> > >         int err;
> > >
> > >         init_completion(&mrq->cmd_completion);
> > > @@ -352,6 +356,13 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> > >         if (err)
> > >                 return err;
> > >
> > > +       if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
> > > +           host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
> > > +               uhs2_cmd.payload = payload;
> > > +               mrq->cmd->uhs2_cmd = &uhs2_cmd;
> > > +               sd_uhs2_prepare_cmd(host, mrq);
> > > +       }
> > > +
> > >         led_trigger_event(host->led, LED_FULL);
> > >         __mmc_start_request(host, mrq);
> > >
> > > @@ -431,6 +442,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done);
> > >   */
> > >  int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> > >  {
> > > +       struct uhs2_command uhs2_cmd;
> > > +       u32 payload[4]; /* for maximum size */
> > >         int err;
> > >
> > >         /*
> > > @@ -451,6 +464,13 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> > >         if (err)
> > >                 goto out_err;
> > >
> > > +       if (host->uhs2_caps.flags & MMC_UHS2_SUPPORT &&
> > > +           host->uhs2_caps.flags & MMC_UHS2_INITIALIZED) {
> > > +               uhs2_cmd.payload = payload;
> > > +               mrq->cmd->uhs2_cmd = &uhs2_cmd;
> > > +               sd_uhs2_prepare_cmd(host, mrq);
> > > +       }
> > > +
> > >         err = host->cqe_ops->cqe_request(host, mrq);
> > >         if (err)
> > >                 goto out_err;
> > > @@ -899,8 +919,10 @@ static inline void mmc_set_ios(struct mmc_host *host)
> > >   */
> > >  void mmc_set_chip_select(struct mmc_host *host, int mode)
> > >  {
> > > -       host->ios.chip_select = mode;
> > > -       mmc_set_ios(host);
> > > +       if (!(host->uhs2_caps.flags & MMC_UHS2_INITIALIZED)) {
> > > +               host->ios.chip_select = mode;
> > > +               mmc_set_ios(host);
> > > +       }
> >
> > As I stated for patch6, rather than having these hacks spread out into
> > the legacy code, we must not allow the ->set_ios() ops to be invoked
> > when UHS-II is being used/initialized.
> >
>
> After checking sd_uhs2.c, mmc_set_chip_select() is called in mmc_go_idle()
> and mmc_go_idle() is called in sd_uhs2_legacy_init(). Hence, I issue CMD0
> directly in sd_uhs2_legacy_init().

That works as a simple solution. But an even better option would be to
re-factor mmc_go_idle(), which then can avoid the open coding. Along
the lines like this:

1) Add a new function, _mmc_go_idle(struct mmc_host *host, bool
chip_select) - which is in principle the same as mmc_go_idle() as
today.
2) From mmc_go_idle() call _mmc_go_idle(host, true).
3) From uhs2 specific code, call  _mmc_go_idle(host, false).

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-04-07 15:00       ` Ulf Hansson
@ 2022-04-12  3:32         ` Lai Jason
  2022-04-12 11:57           ` Ulf Hansson
  0 siblings, 1 reply; 27+ messages in thread
From: Lai Jason @ 2022-04-12  3:32 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

On Thu, Apr 7, 2022 at 11:01 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Thu, 7 Apr 2022 at 12:45, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
> >
> > Hi Uffe,
> >
> > On Thu, Mar 24, 2022 at 12:16 AM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > >
> > > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > > >
> > > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > >
> > > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > > Part 1 - PHY Initialization:
> > > >          Every host controller may need their own avtivation operation to
> > > >          establish LINK between controller and card. So we add a new member
> > > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > > >          controller use.
> > > > Part 2 - Card Initialization:
> > > >          This part can be divided into 6 substeps.
> > > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > > >          2. Send UHS-II CCMD ENUMERATE to card.
> > > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > > >             of card.
> > > >          4. Host compares capabilities of host controller and card, then
> > > >             write the negotiated values to Setting field in CFG_REG of card
> > > >             through UHS-II Native Write CCMD.
> > > >          5. Switch host controller's clock to Range B if it is supported by
> > > >             both host controller and card.
> > > >          6. Execute legacy SD initialization flow.
> > > > Part 3 - Provide a function to tranaform legacy SD command packet into
> > > >          UHS-II SD-TRAN DCMD packet.
> > > >
> > > > Most of the code added above came from Intel's original patch[3].
> > > >
> > > > [3]
> > > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > > git-send-email-yi.y.sun@intel.com/
> > > >
> > > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > ---
> > > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > > index 800957f74632..f1e8e30301eb 100644
> > > > --- a/drivers/mmc/core/sd_uhs2.c
> > > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > > @@ -3,6 +3,7 @@
> > > >   * Copyright (C) 2021 Linaro Ltd
> > > >   *
> > > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > >   *
> > > >   * Support for SD UHS-II cards
> > > >   */
> > > > @@ -10,19 +11,31 @@
> > > >
> > > >  #include <linux/mmc/host.h>
> > > >  #include <linux/mmc/card.h>
> > > > +#include <linux/mmc/mmc.h>
> > > > +#include <linux/mmc/sd_uhs2.h>
> > > >
> > > >  #include "core.h"
> > > >  #include "bus.h"
> > > > +#include "card.h"
> > > >  #include "sd.h"
> > > > +#include "sd_ops.h"
> > > >  #include "mmc_ops.h"
> > > > +#include "sd_uhs2.h"
> > > >
> > > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > > >
> > > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > > >  {
> > > >         struct mmc_ios *ios = &host->ios;
> > > > +       int err = 0;
> > > >
> > > > -       return host->ops->uhs2_set_ios(host, ios);
> > > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > > +                ios->timing);
> > > > +
> > > > +       host->ops->set_ios(host, ios);
> > >
> > > We discussed using the ->set_ios() callback in a previous version. To
> > > repeat myself, I don't think it's a good idea. UHS-II needs an
> > > entirely different power sequence than the legacy interface(s), hence
> > > I think it's simply cleaner to separate them.
> > >
> > > To move forward, I see two options.
> > > 1) Use only the ->uhs2_host_operation() ops.
> > > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > > ->uhs2_host_operation() ops.
> > >
> >
> > I referred to the usage of "host->ops->set_ios" in core.c, it is called in
> > mmc_set_ios() and ".set_ios" is directed to sdhci_set_ios(), which is
> > located in mmc/host/sdhci.c. So I created sd_uhs2_set_ios() and call
> > host->ops->uhs2_set_ios() inside it. The ".uhs2_set_ios" is left to host
> > part to implement it.
>
> I see. In that case, what you are looking for is an sdhci specific
> callback, this wouldn't belong as part of the generic mmc host ops.
>
> That said, I still think we need to choose between the two options I
> suggested above. Otherwise, I fear that it will turn into a nightmare
> for the mmc host drivers to support both UHS-II and the legacy
> interface.
>
> In other words, I strongly suggest that we must not call ->set_ios()
> to manage the UHS-II interface.
>

I will defer to you. But it's a bit complicated so I need more time to think
about how to organize these operations.  Therefore, I'll submit PATCH V4
before sd_uhs2_set_ios() being implemented.

> >
> > > Both options work for me. However, perhaps if you could incorporate
> > > the changes done on the host driver at next submission, it becomes
> > > easier for me to understand what makes best sense.
> > >
> > > > +
> > > > +       return err;
> > > >  }
> > > >
>
> [...]
>
> > > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > >  {
> > > > +       struct mmc_command cmd = {0};
> > > > +       struct uhs2_command uhs2_cmd = {};
> > > > +       u16 header = 0, arg = 0;
> > > > +       u32 payload[2];
> > > > +       u8 nMinDataGap;
> > > > +       u8 plen;
> > > > +       int err;
> > > > +       u8 resp[5] = {0};
> > > > +       u8 resp_len = 5;
> > > > +
> > > > +       header = UHS2_NATIVE_PACKET |
> > > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > +
> > > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > > +               /* Support HD */
> > > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> > >
> > > How is the uhs2_caps.flags field intended to be used? To me it looks
> > > like a way for the mmc core to exchange status/configuration
> > > information about the initialization process of the card, with the mmc
> > > host driver. Perhaps there is more too. Is that correct?
> > >
> > > If so, I think it looks quite similar for what we have in the struct
> > > mmc_ios, for the legacy interface(s). I am not saying we should use
> > > that, just trying to understand what would suit best here.
> > >
> >
> > The usage of uhs2_caps.flags is spread out through core and host.
> > All operations related to it cannot be integrated into uhs2_set_ios()
> > simply. I recommend maintaining the status quo.
>
> What is puzzling to me, is that the data is stored below uhs2_caps.*
> and that it's called "flags". It's not self-explanatory and it's not
> consistent with the way we use the ->set_ios() callback, for the
> legacy interface.
>

Sorry,  I did not understand why uhs2_caps.flag is related to set_ios().
The bit flags are used to indicate current status of UHS2 card:
  - bit 0: MMC_UHS2_SUPPORT
             This flag indicates if current sd card  supports UHS2 mode.
  - bit 1: MMC_UHS2_INITIALIZED
             This flag indicates if current uhs2 sd card  had been initialized.
  - bit 2: MMC_UHS2_2L_HD
             This flag indicates the speed mode of current uhs2 sd card is
             2L-HD mode. The host sets DCMD argument[0] bit 6
             according to this flag. That means this flag was checked in
             every MMC request.
  - bit 3: MMC_UHS2_APP_CMD
             This flag indicates if the current SD_TRAN command is an APP
             command. The host sets DCMD argument[0] bit 14  according to
             this flag.  That means this flag was checked in every MMC
             request.
  - bit 4: MMC_UHS2_SPEED_B
             This flag indicates the speed range of current UHS2 sd card. This
             flag is used only during uhs2 sd card initialization.

> It looks to me that we should rather add a new variable to the struct
> mmc_host and perhaps name it "uhs2_ios", to keep this data. Whether we
> need to create a new struct for "uhs2_ios" or if it's better to extend
> struct mmc_ios, I am not sure. I guess exploring this by writing the
> code would tell us what is best suited.
>

I will add a new variable to the struct mmc_host and name it "uhs2_ios".
In UHS2 CCMD/DCMD command packet, DM in TMODE(bit 6 in Argument)
and bit APP(bit 15 in Argument) are set according to MMC_UHS2_2L_HD
and MMC_UHS2_APP_CMD. Hence, I plan to define struct uhs2_ios as:
struct sd_uhs2_ios {
        bool                  is_2L_HD_mode;    /* DM bit in TMODE in UHS2 DCDM
                                                                  *
argument will be set to 1 when
                                                                  *
this field is true. */
        bool                  is_APP_CMD;         /* APP bit in UHS2 CCMD/DCDM
                                                                  *
argument will be set to 1 when
                                                                  *
this field is true. */
        unsigned int     power_delay_ms;    /* waiting for stable power */
};

As for MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B, I plan to add a variable
in struct mmc_card and name it "uhs2_state", to keep current UHS2 card states.
struct mmc_card {
        struct mmc_host *host; /* the host this device belongs to */
        [...]
        struct  sd_uhs2_config  uhs2_config;   /* SD UHS-II config */
        u8                                  uhs2_state;     /* SD
UHS-II states */
#define MMC_UHS2_INITIALIZED    BIT(1)
#define MMC_UHS2_SPEED_B        BIT(2)
        [...]
};

Kind regards,
Jason

> >
> > > > +               nMinDataGap = 1;
> > > > +       } else {
> > > > +               /* Only support 2L-FD so far */
> > > > +               host->uhs2_caps.flags &= ~MMC_UHS2_2L_HD;
> > > > +               nMinDataGap = 3;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers
> > > > +        * defined in UHS-II addendem Ver1.01 are optional.
> > > > +        */
> > > > +       host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > > > +       card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD;
> > >
> > > [...]
> > >
> > > > +static int sd_uhs2_go_dormant(struct mmc_host *host, bool hibernate, u32 node_id)
> > > > +{
> > >
> > > Looks like the in-parameter "hibernate" is superfluous, as it's always
> > > set to "false" by the caller.
> > >
> >
> > The in-parameter "hibernate" is designed according to UHS-II
> > specification. We did not use
> > it for now. But we are not sure if it will be set to true in future
> > use. So I suggest keeping it.
>
> I understand your point, but I don't agree, sorry. We don't want dead
> code around in the kernel, so please remove it.
>
> Perhaps what we can do, is to add a comment in sd_uhs2_go_dormant(),
> somewhere we default to not use hibernate, we could simply explain
> that hibernation is currently not supported.
>
> >
> > > > +       struct mmc_command cmd = {0};
> > > > +       struct uhs2_command uhs2_cmd = {};
> > > > +       u16 header = 0, arg = 0;
> > > > +       u32 payload[1];
> > > > +       u8 plen = 1;
> > > > +       int err;
> > > > +
> > > > +       /* Disable Normal INT */
> > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_DISABLE_INT)) {
> > > > +               pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
> > > > +
> > > > +       arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) |
> > > > +               UHS2_NATIVE_CMD_WRITE |
> > > > +               UHS2_NATIVE_CMD_PLEN_4B |
> > > > +               (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8);
> > > > +
> > > > +       if (hibernate)
> > > > +               payload[0] = UHS2_DEV_CMD_DORMANT_HIBER;
> > > > +
> > > > +       sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0);
> > > > +
> > > > +       err = mmc_wait_for_cmd(host, &cmd, 0);
> > > > +       if (err) {
> > > > +               pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
> > > > +                      mmc_hostname(host), __func__, err);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       /* Check Dormant State in Present */
> > > > +       if (!host->ops->uhs2_host_operation(host, UHS2_CHECK_DORMANT)) {
> > > > +               pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n",
> > > > +                      mmc_hostname(host), __func__);
> > > > +               return -EIO;
> > > > +       }
> > > > +
> > > > +       host->ops->uhs2_host_operation(host, UHS2_DISABLE_CLK);
> > > > +
> > > >         return 0;
> > > >  }
>
> [...]
>
> Kind regards
> Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-04-12  3:32         ` Lai Jason
@ 2022-04-12 11:57           ` Ulf Hansson
  2022-04-13  7:18             ` Lai Jason
  0 siblings, 1 reply; 27+ messages in thread
From: Ulf Hansson @ 2022-04-12 11:57 UTC (permalink / raw)
  To: Lai Jason
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

On Tue, 12 Apr 2022 at 05:32, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
>
> On Thu, Apr 7, 2022 at 11:01 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> >
> > On Thu, 7 Apr 2022 at 12:45, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
> > >
> > > Hi Uffe,
> > >
> > > On Thu, Mar 24, 2022 at 12:16 AM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > > >
> > > > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > > > >
> > > > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > >
> > > > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > > > Part 1 - PHY Initialization:
> > > > >          Every host controller may need their own avtivation operation to
> > > > >          establish LINK between controller and card. So we add a new member
> > > > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > > > >          controller use.
> > > > > Part 2 - Card Initialization:
> > > > >          This part can be divided into 6 substeps.
> > > > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > > > >          2. Send UHS-II CCMD ENUMERATE to card.
> > > > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > > > >             of card.
> > > > >          4. Host compares capabilities of host controller and card, then
> > > > >             write the negotiated values to Setting field in CFG_REG of card
> > > > >             through UHS-II Native Write CCMD.
> > > > >          5. Switch host controller's clock to Range B if it is supported by
> > > > >             both host controller and card.
> > > > >          6. Execute legacy SD initialization flow.
> > > > > Part 3 - Provide a function to tranaform legacy SD command packet into
> > > > >          UHS-II SD-TRAN DCMD packet.
> > > > >
> > > > > Most of the code added above came from Intel's original patch[3].
> > > > >
> > > > > [3]
> > > > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > > > git-send-email-yi.y.sun@intel.com/
> > > > >
> > > > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > > ---
> > > > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > > > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > > > index 800957f74632..f1e8e30301eb 100644
> > > > > --- a/drivers/mmc/core/sd_uhs2.c
> > > > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > > > @@ -3,6 +3,7 @@
> > > > >   * Copyright (C) 2021 Linaro Ltd
> > > > >   *
> > > > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > >   *
> > > > >   * Support for SD UHS-II cards
> > > > >   */
> > > > > @@ -10,19 +11,31 @@
> > > > >
> > > > >  #include <linux/mmc/host.h>
> > > > >  #include <linux/mmc/card.h>
> > > > > +#include <linux/mmc/mmc.h>
> > > > > +#include <linux/mmc/sd_uhs2.h>
> > > > >
> > > > >  #include "core.h"
> > > > >  #include "bus.h"
> > > > > +#include "card.h"
> > > > >  #include "sd.h"
> > > > > +#include "sd_ops.h"
> > > > >  #include "mmc_ops.h"
> > > > > +#include "sd_uhs2.h"
> > > > >
> > > > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > > > >
> > > > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > > > >  {
> > > > >         struct mmc_ios *ios = &host->ios;
> > > > > +       int err = 0;
> > > > >
> > > > > -       return host->ops->uhs2_set_ios(host, ios);
> > > > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > > > +                ios->timing);
> > > > > +
> > > > > +       host->ops->set_ios(host, ios);
> > > >
> > > > We discussed using the ->set_ios() callback in a previous version. To
> > > > repeat myself, I don't think it's a good idea. UHS-II needs an
> > > > entirely different power sequence than the legacy interface(s), hence
> > > > I think it's simply cleaner to separate them.
> > > >
> > > > To move forward, I see two options.
> > > > 1) Use only the ->uhs2_host_operation() ops.
> > > > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > > > ->uhs2_host_operation() ops.
> > > >
> > >
> > > I referred to the usage of "host->ops->set_ios" in core.c, it is called in
> > > mmc_set_ios() and ".set_ios" is directed to sdhci_set_ios(), which is
> > > located in mmc/host/sdhci.c. So I created sd_uhs2_set_ios() and call
> > > host->ops->uhs2_set_ios() inside it. The ".uhs2_set_ios" is left to host
> > > part to implement it.
> >
> > I see. In that case, what you are looking for is an sdhci specific
> > callback, this wouldn't belong as part of the generic mmc host ops.
> >
> > That said, I still think we need to choose between the two options I
> > suggested above. Otherwise, I fear that it will turn into a nightmare
> > for the mmc host drivers to support both UHS-II and the legacy
> > interface.
> >
> > In other words, I strongly suggest that we must not call ->set_ios()
> > to manage the UHS-II interface.
> >
>
> I will defer to you. But it's a bit complicated so I need more time to think
> about how to organize these operations.  Therefore, I'll submit PATCH V4
> before sd_uhs2_set_ios() being implemented.

Okay. I will do my best to continue to provide guidance and review.

Perhaps you could also have a closer look at the legacy code for
eMMC/SD, to get some inspiration on how to structure the code in a
consistent way.

>
> > >
> > > > Both options work for me. However, perhaps if you could incorporate
> > > > the changes done on the host driver at next submission, it becomes
> > > > easier for me to understand what makes best sense.
> > > >
> > > > > +
> > > > > +       return err;
> > > > >  }
> > > > >
> >
> > [...]
> >
> > > > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > > >  {
> > > > > +       struct mmc_command cmd = {0};
> > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > +       u16 header = 0, arg = 0;
> > > > > +       u32 payload[2];
> > > > > +       u8 nMinDataGap;
> > > > > +       u8 plen;
> > > > > +       int err;
> > > > > +       u8 resp[5] = {0};
> > > > > +       u8 resp_len = 5;
> > > > > +
> > > > > +       header = UHS2_NATIVE_PACKET |
> > > > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > > +
> > > > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > > > +               /* Support HD */
> > > > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> > > >
> > > > How is the uhs2_caps.flags field intended to be used? To me it looks
> > > > like a way for the mmc core to exchange status/configuration
> > > > information about the initialization process of the card, with the mmc
> > > > host driver. Perhaps there is more too. Is that correct?
> > > >
> > > > If so, I think it looks quite similar for what we have in the struct
> > > > mmc_ios, for the legacy interface(s). I am not saying we should use
> > > > that, just trying to understand what would suit best here.
> > > >
> > >
> > > The usage of uhs2_caps.flags is spread out through core and host.
> > > All operations related to it cannot be integrated into uhs2_set_ios()
> > > simply. I recommend maintaining the status quo.
> >
> > What is puzzling to me, is that the data is stored below uhs2_caps.*
> > and that it's called "flags". It's not self-explanatory and it's not
> > consistent with the way we use the ->set_ios() callback, for the
> > legacy interface.
> >
>
> Sorry,  I did not understand why uhs2_caps.flag is related to set_ios().
> The bit flags are used to indicate current status of UHS2 card:
>   - bit 0: MMC_UHS2_SUPPORT
>              This flag indicates if current sd card  supports UHS2 mode.

By looking at the code, it seems like MMC_UHS2_SUPPORT is redundant,
at least from the mmc core point of view. Everywhere it's used, we use
MMC_UHS2_INITIALIZED too.

Moreover, in patch 2 (mmc: core: Prepare to support SD UHS-II cards) I
added MMC_CAP2_SD_UHS2 as a generic host cap, which is being used in
mmc_attach_sd_uhs2(). This should be sufficient as a generic cap for
UHS2, I think.

>   - bit 1: MMC_UHS2_INITIALIZED
>              This flag indicates if current uhs2 sd card  had been initialized.

In patch 2 (mmc: core: Prepare to support SD UHS-II cards), I added
MMC_TIMING_SD_UHS2. It looks like it tries to serve a similar purpose.

Moreover, I don't see where MMC_UHS2_INITIALIZED is getting set, but I
assume it must be before we try to initialize the card by sending UHS2
specific commands to it, right?

>   - bit 2: MMC_UHS2_2L_HD
>              This flag indicates the speed mode of current uhs2 sd card is
>              2L-HD mode. The host sets DCMD argument[0] bit 6
>              according to this flag. That means this flag was checked in
>              every MMC request.

If we compare how we managed things like this for the legacy
interface, this information is kept in the "ios->timing" variable.

>   - bit 3: MMC_UHS2_APP_CMD
>              This flag indicates if the current SD_TRAN command is an APP
>              command. The host sets DCMD argument[0] bit 14  according to
>              this flag.  That means this flag was checked in every MMC
>              request.

Alright, so it's a specific flag for UHS2 commands.

>   - bit 4: MMC_UHS2_SPEED_B
>              This flag indicates the speed range of current UHS2 sd card. This
>              flag is used only during uhs2 sd card initialization.
>
> > It looks to me that we should rather add a new variable to the struct
> > mmc_host and perhaps name it "uhs2_ios", to keep this data. Whether we
> > need to create a new struct for "uhs2_ios" or if it's better to extend
> > struct mmc_ios, I am not sure. I guess exploring this by writing the
> > code would tell us what is best suited.
> >
>
> I will add a new variable to the struct mmc_host and name it "uhs2_ios".
> In UHS2 CCMD/DCMD command packet, DM in TMODE(bit 6 in Argument)
> and bit APP(bit 15 in Argument) are set according to MMC_UHS2_2L_HD
> and MMC_UHS2_APP_CMD. Hence, I plan to define struct uhs2_ios as:
> struct sd_uhs2_ios {
>         bool                  is_2L_HD_mode;    /* DM bit in TMODE in UHS2 DCDM
>                                                                   *
> argument will be set to 1 when

Seems reasonable.

>                                                                   *
> this field is true. */
>         bool                  is_APP_CMD;         /* APP bit in UHS2 CCMD/DCDM
>                                                                   *
> argument will be set to 1 when
>                                                                   *
> this field is true. */

Sounds like this better belongs in the struct uhs2_command.

>         unsigned int     power_delay_ms;    /* waiting for stable power */
> };
>
> As for MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B, I plan to add a variable
> in struct mmc_card and name it "uhs2_state", to keep current UHS2 card states.
> struct mmc_card {
>         struct mmc_host *host; /* the host this device belongs to */
>         [...]
>         struct  sd_uhs2_config  uhs2_config;   /* SD UHS-II config */
>         u8                                  uhs2_state;     /* SD
> UHS-II states */
> #define MMC_UHS2_INITIALIZED    BIT(1)
> #define MMC_UHS2_SPEED_B        BIT(2)
>         [...]

Please don't.

If it's configurations of the card, the data belongs under struct
sd_uhs2_config. If it's configuration of the host, the data typically
belongs in the struct sd_uhs2_ios.

MMC_UHS2_SPEED_B is typically a speed mode for UHS2. For the legacy
interface in the struct mmc_ios, we have an "unsigned char timing"
variable to keep things like these. We need something similar in the
struct sd_uhs2_ios for this, I think. Maybe we should even turn the
bool for "2L_HD_mode" into being part of this "timing" variable.

When it comes to the MMC_UHS2_INITIALIZED, if I understand correctly,
it means that we have managed to power on the phy and are about to
start the communication with the card. We could use a "bool" in the
struct sd_uhs2_ios, to indicate this. Another option may be to use and
set "MMC_TIMING_SD_UHS2" as a default speed mode in the "timing"
variable above. No matter what, it seems like MMC_TIMING_SD_UHS2
doesn't belong in the legacy ios->variable, as I suggested in patch2.

> };
>
> Kind regards,
> Jason
>

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-04-12 11:57           ` Ulf Hansson
@ 2022-04-13  7:18             ` Lai Jason
  2022-04-13 11:03               ` Ulf Hansson
  0 siblings, 1 reply; 27+ messages in thread
From: Lai Jason @ 2022-04-13  7:18 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

On Tue, Apr 12, 2022 at 7:58 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Tue, 12 Apr 2022 at 05:32, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
> >
> > On Thu, Apr 7, 2022 at 11:01 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > >
> > > On Thu, 7 Apr 2022 at 12:45, Lai Jason <jasonlai.genesyslogic@gmail.com> wrote:
> > > >
> > > > Hi Uffe,
> > > >
> > > > On Thu, Mar 24, 2022 at 12:16 AM Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > > > >
> > > > > On Tue, 22 Feb 2022 at 04:40, Jason Lai <jasonlai.genesyslogic@gmail.com> wrote:
> > > > > >
> > > > > > From: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > > >
> > > > > > UHS-II card initialization flow is divided into 2 categories: PHY & Card.
> > > > > > Part 1 - PHY Initialization:
> > > > > >          Every host controller may need their own avtivation operation to
> > > > > >          establish LINK between controller and card. So we add a new member
> > > > > >          function(uhs2_detect_init) in struct mmc_host_ops for host
> > > > > >          controller use.
> > > > > > Part 2 - Card Initialization:
> > > > > >          This part can be divided into 6 substeps.
> > > > > >          1. Send UHS-II CCMD DEVICE_INIT to card.
> > > > > >          2. Send UHS-II CCMD ENUMERATE to card.
> > > > > >          3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG
> > > > > >             of card.
> > > > > >          4. Host compares capabilities of host controller and card, then
> > > > > >             write the negotiated values to Setting field in CFG_REG of card
> > > > > >             through UHS-II Native Write CCMD.
> > > > > >          5. Switch host controller's clock to Range B if it is supported by
> > > > > >             both host controller and card.
> > > > > >          6. Execute legacy SD initialization flow.
> > > > > > Part 3 - Provide a function to tranaform legacy SD command packet into
> > > > > >          UHS-II SD-TRAN DCMD packet.
> > > > > >
> > > > > > Most of the code added above came from Intel's original patch[3].
> > > > > >
> > > > > > [3]
> > > > > > https://patchwork.kernel.org/project/linux-mmc/patch/1419672479-30852-2-
> > > > > > git-send-email-yi.y.sun@intel.com/
> > > > > >
> > > > > > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > > > ---
> > > > > >  drivers/mmc/core/sd_uhs2.c | 835 ++++++++++++++++++++++++++++++++++++-
> > > > > >  1 file changed, 817 insertions(+), 18 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > > > > > index 800957f74632..f1e8e30301eb 100644
> > > > > > --- a/drivers/mmc/core/sd_uhs2.c
> > > > > > +++ b/drivers/mmc/core/sd_uhs2.c
> > > > > > @@ -3,6 +3,7 @@
> > > > > >   * Copyright (C) 2021 Linaro Ltd
> > > > > >   *
> > > > > >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> > > > > > + * Author: Jason Lai <jason.lai@genesyslogic.com.tw>
> > > > > >   *
> > > > > >   * Support for SD UHS-II cards
> > > > > >   */
> > > > > > @@ -10,19 +11,31 @@
> > > > > >
> > > > > >  #include <linux/mmc/host.h>
> > > > > >  #include <linux/mmc/card.h>
> > > > > > +#include <linux/mmc/mmc.h>
> > > > > > +#include <linux/mmc/sd_uhs2.h>
> > > > > >
> > > > > >  #include "core.h"
> > > > > >  #include "bus.h"
> > > > > > +#include "card.h"
> > > > > >  #include "sd.h"
> > > > > > +#include "sd_ops.h"
> > > > > >  #include "mmc_ops.h"
> > > > > > +#include "sd_uhs2.h"
> > > > > >
> > > > > >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> > > > > >
> > > > > >  static int sd_uhs2_set_ios(struct mmc_host *host)
> > > > > >  {
> > > > > >         struct mmc_ios *ios = &host->ios;
> > > > > > +       int err = 0;
> > > > > >
> > > > > > -       return host->ops->uhs2_set_ios(host, ios);
> > > > > > +       pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
> > > > > > +                mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd,
> > > > > > +                ios->timing);
> > > > > > +
> > > > > > +       host->ops->set_ios(host, ios);
> > > > >
> > > > > We discussed using the ->set_ios() callback in a previous version. To
> > > > > repeat myself, I don't think it's a good idea. UHS-II needs an
> > > > > entirely different power sequence than the legacy interface(s), hence
> > > > > I think it's simply cleaner to separate them.
> > > > >
> > > > > To move forward, I see two options.
> > > > > 1) Use only the ->uhs2_host_operation() ops.
> > > > > 2) Use a combination of the ->uhs2_set_ios() ops and the
> > > > > ->uhs2_host_operation() ops.
> > > > >
> > > >
> > > > I referred to the usage of "host->ops->set_ios" in core.c, it is called in
> > > > mmc_set_ios() and ".set_ios" is directed to sdhci_set_ios(), which is
> > > > located in mmc/host/sdhci.c. So I created sd_uhs2_set_ios() and call
> > > > host->ops->uhs2_set_ios() inside it. The ".uhs2_set_ios" is left to host
> > > > part to implement it.
> > >
> > > I see. In that case, what you are looking for is an sdhci specific
> > > callback, this wouldn't belong as part of the generic mmc host ops.
> > >
> > > That said, I still think we need to choose between the two options I
> > > suggested above. Otherwise, I fear that it will turn into a nightmare
> > > for the mmc host drivers to support both UHS-II and the legacy
> > > interface.
> > >
> > > In other words, I strongly suggest that we must not call ->set_ios()
> > > to manage the UHS-II interface.
> > >
> >
> > I will defer to you. But it's a bit complicated so I need more time to think
> > about how to organize these operations.  Therefore, I'll submit PATCH V4
> > before sd_uhs2_set_ios() being implemented.
>
> Okay. I will do my best to continue to provide guidance and review.
>
> Perhaps you could also have a closer look at the legacy code for
> eMMC/SD, to get some inspiration on how to structure the code in a
> consistent way.
>
> >
> > > >
> > > > > Both options work for me. However, perhaps if you could incorporate
> > > > > the changes done on the host driver at next submission, it becomes
> > > > > easier for me to understand what makes best sense.
> > > > >
> > > > > > +
> > > > > > +       return err;
> > > > > >  }
> > > > > >
> > >
> > > [...]
> > >
> > > > > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > > > >  {
> > > > > > +       struct mmc_command cmd = {0};
> > > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > > +       u16 header = 0, arg = 0;
> > > > > > +       u32 payload[2];
> > > > > > +       u8 nMinDataGap;
> > > > > > +       u8 plen;
> > > > > > +       int err;
> > > > > > +       u8 resp[5] = {0};
> > > > > > +       u8 resp_len = 5;
> > > > > > +
> > > > > > +       header = UHS2_NATIVE_PACKET |
> > > > > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > > > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > > > +
> > > > > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > > > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > > > > +               /* Support HD */
> > > > > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> > > > >
> > > > > How is the uhs2_caps.flags field intended to be used? To me it looks
> > > > > like a way for the mmc core to exchange status/configuration
> > > > > information about the initialization process of the card, with the mmc
> > > > > host driver. Perhaps there is more too. Is that correct?
> > > > >
> > > > > If so, I think it looks quite similar for what we have in the struct
> > > > > mmc_ios, for the legacy interface(s). I am not saying we should use
> > > > > that, just trying to understand what would suit best here.
> > > > >
> > > >
> > > > The usage of uhs2_caps.flags is spread out through core and host.
> > > > All operations related to it cannot be integrated into uhs2_set_ios()
> > > > simply. I recommend maintaining the status quo.
> > >
> > > What is puzzling to me, is that the data is stored below uhs2_caps.*
> > > and that it's called "flags". It's not self-explanatory and it's not
> > > consistent with the way we use the ->set_ios() callback, for the
> > > legacy interface.
> > >
> >
> > Sorry,  I did not understand why uhs2_caps.flag is related to set_ios().
> > The bit flags are used to indicate current status of UHS2 card:
> >   - bit 0: MMC_UHS2_SUPPORT
> >              This flag indicates if current sd card  supports UHS2 mode.
>
> By looking at the code, it seems like MMC_UHS2_SUPPORT is redundant,
> at least from the mmc core point of view. Everywhere it's used, we use
> MMC_UHS2_INITIALIZED too.
>

Yes. MMC_UHS2_SUPPORT is only useful in patch set: "Add support UHS-II
for GL9755"
(https://patchwork.kernel.org/project/linux-mmc/list/?series=378627&archive=both).

I will remove it in V4.

> Moreover, in patch 2 (mmc: core: Prepare to support SD UHS-II cards) I
> added MMC_CAP2_SD_UHS2 as a generic host cap, which is being used in
> mmc_attach_sd_uhs2(). This should be sufficient as a generic cap for
> UHS2, I think.
>
> >   - bit 1: MMC_UHS2_INITIALIZED
> >              This flag indicates if current uhs2 sd card  had been initialized.
>
> In patch 2 (mmc: core: Prepare to support SD UHS-II cards), I added
> MMC_TIMING_SD_UHS2. It looks like it tries to serve a similar purpose.
>

MMC_TIMING_SD_UHS2 is used to indicate that host controller is using UHS2
timing now. But MMC_UHS2_INITIALIZED=1 means that UHS2 interface
initialization is complete. The meaning of these two flags are different.

> Moreover, I don't see where MMC_UHS2_INITIALIZED is getting set, but I
> assume it must be before we try to initialize the card by sending UHS2
> specific commands to it, right?
>

It is set in sd_uhs2_init_card():
static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
{
        struct mmc_card *card;
        u32 node_id;
        int err;
        [...]
        if (card->uhs2_state & MMC_UHS2_SPEED_B) {
                err = sd_uhs2_change_speed(host, node_id);
                if (err)
                       return err;
        }

        card->uhs2_state |= MMC_UHS2_INITIALIZED;

        err = sd_uhs2_legacy_init(host, card);
        if (err)
                goto err;
        [...]

}

> >   - bit 2: MMC_UHS2_2L_HD
> >              This flag indicates the speed mode of current uhs2 sd card is
> >              2L-HD mode. The host sets DCMD argument[0] bit 6
> >              according to this flag. That means this flag was checked in
> >              every MMC request.
>
> If we compare how we managed things like this for the legacy
> interface, this information is kept in the "ios->timing" variable.
>
> >   - bit 3: MMC_UHS2_APP_CMD
> >              This flag indicates if the current SD_TRAN command is an APP
> >              command. The host sets DCMD argument[0] bit 14  according to
> >              this flag.  That means this flag was checked in every MMC
> >              request.
>
> Alright, so it's a specific flag for UHS2 commands.
>
> >   - bit 4: MMC_UHS2_SPEED_B
> >              This flag indicates the speed range of current UHS2 sd card. This
> >              flag is used only during uhs2 sd card initialization.
> >
> > > It looks to me that we should rather add a new variable to the struct
> > > mmc_host and perhaps name it "uhs2_ios", to keep this data. Whether we
> > > need to create a new struct for "uhs2_ios" or if it's better to extend
> > > struct mmc_ios, I am not sure. I guess exploring this by writing the
> > > code would tell us what is best suited.
> > >
> >
> > I will add a new variable to the struct mmc_host and name it "uhs2_ios".
> > In UHS2 CCMD/DCMD command packet, DM in TMODE(bit 6 in Argument)
> > and bit APP(bit 15 in Argument) are set according to MMC_UHS2_2L_HD
> > and MMC_UHS2_APP_CMD. Hence, I plan to define struct uhs2_ios as:
> > struct sd_uhs2_ios {
> >         bool                  is_2L_HD_mode;    /* DM bit in TMODE in UHS2 DCDM
> >                                                                   *
> > argument will be set to 1 when
>
> Seems reasonable.
>
> >                                                                   *
> > this field is true. */
> >         bool                  is_APP_CMD;         /* APP bit in UHS2 CCMD/DCDM
> >                                                                   *
> > argument will be set to 1 when
> >                                                                   *
> > this field is true. */
>
> Sounds like this better belongs in the struct uhs2_command.
>

Indeed, it is more reasonable to put is_APP_CMD in uhs2_cmd. But uhs2_cmd
is allocated in mmc_start_request() and is_APP_CMD is set in mmc_app_cmd(),
which is called prior to mmc_start_request(). So I use this flag as a mark and
check it when filling uhs2_cmd in uhs2_prepare_sd_cmd().

> >         unsigned int     power_delay_ms;    /* waiting for stable power */
> > };
> >
> > As for MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B, I plan to add a variable
> > in struct mmc_card and name it "uhs2_state", to keep current UHS2 card states.
> > struct mmc_card {
> >         struct mmc_host *host; /* the host this device belongs to */
> >         [...]
> >         struct  sd_uhs2_config  uhs2_config;   /* SD UHS-II config */
> >         u8                                  uhs2_state;     /* SD
> > UHS-II states */
> > #define MMC_UHS2_INITIALIZED    BIT(1)
> > #define MMC_UHS2_SPEED_B        BIT(2)
> >         [...]
>
> Please don't.
>
> If it's configurations of the card, the data belongs under struct
> sd_uhs2_config. If it's configuration of the host, the data typically
> belongs in the struct sd_uhs2_ios.
>
> MMC_UHS2_SPEED_B is typically a speed mode for UHS2. For the legacy
> interface in the struct mmc_ios, we have an "unsigned char timing"
> variable to keep things like these. We need something similar in the
> struct sd_uhs2_ios for this, I think. Maybe we should even turn the
> bool for "2L_HD_mode" into being part of this "timing" variable.
>

Actually, MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B are more
like "is UHS2 interface initialization complete?" and "is current UHS2 sd card
support Range B clock?". They are not configurations but a kind of flags which
are used to tell driver:
    - MMC_UHS2_SPEED_B:     Current UHS2 sd card supports Range B clock
                                                   frequency. Driver
can execute sd_uhs2_change_speed()
                                                   after sd_uhs2_config_write().
    - MMC_UHS2_INITIALIZED: UHS2 interface handshake done. Driver can access
                                                   current UHS2 sd
card with UHS2 interface.

That's why I named it 'uhs2_state'. What do you think?

Kind regards,
Jason

> When it comes to the MMC_UHS2_INITIALIZED, if I understand correctly,
> it means that we have managed to power on the phy and are about to
> start the communication with the card. We could use a "bool" in the
> struct sd_uhs2_ios, to indicate this. Another option may be to use and
> set "MMC_TIMING_SD_UHS2" as a default speed mode in the "timing"
> variable above. No matter what, it seems like MMC_TIMING_SD_UHS2
> doesn't belong in the legacy ios->variable, as I suggested in patch2.
>
> > };
> >
> > Kind regards,
> > Jason
> >
>
> [...]
>
> Kind regards
> Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-04-13  7:18             ` Lai Jason
@ 2022-04-13 11:03               ` Ulf Hansson
  2022-04-14 12:41                 ` Lai Jason
  0 siblings, 1 reply; 27+ messages in thread
From: Ulf Hansson @ 2022-04-13 11:03 UTC (permalink / raw)
  To: Lai Jason
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

[...]

> > > > > > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > > > > >  {
> > > > > > > +       struct mmc_command cmd = {0};
> > > > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > > > +       u16 header = 0, arg = 0;
> > > > > > > +       u32 payload[2];
> > > > > > > +       u8 nMinDataGap;
> > > > > > > +       u8 plen;
> > > > > > > +       int err;
> > > > > > > +       u8 resp[5] = {0};
> > > > > > > +       u8 resp_len = 5;
> > > > > > > +
> > > > > > > +       header = UHS2_NATIVE_PACKET |
> > > > > > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > > > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > > > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > > > > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > > > > +
> > > > > > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > > > > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > > > > > +               /* Support HD */
> > > > > > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> > > > > >
> > > > > > How is the uhs2_caps.flags field intended to be used? To me it looks
> > > > > > like a way for the mmc core to exchange status/configuration
> > > > > > information about the initialization process of the card, with the mmc
> > > > > > host driver. Perhaps there is more too. Is that correct?
> > > > > >
> > > > > > If so, I think it looks quite similar for what we have in the struct
> > > > > > mmc_ios, for the legacy interface(s). I am not saying we should use
> > > > > > that, just trying to understand what would suit best here.
> > > > > >
> > > > >
> > > > > The usage of uhs2_caps.flags is spread out through core and host.
> > > > > All operations related to it cannot be integrated into uhs2_set_ios()
> > > > > simply. I recommend maintaining the status quo.
> > > >
> > > > What is puzzling to me, is that the data is stored below uhs2_caps.*
> > > > and that it's called "flags". It's not self-explanatory and it's not
> > > > consistent with the way we use the ->set_ios() callback, for the
> > > > legacy interface.
> > > >
> > >
> > > Sorry,  I did not understand why uhs2_caps.flag is related to set_ios().
> > > The bit flags are used to indicate current status of UHS2 card:
> > >   - bit 0: MMC_UHS2_SUPPORT
> > >              This flag indicates if current sd card  supports UHS2 mode.
> >
> > By looking at the code, it seems like MMC_UHS2_SUPPORT is redundant,
> > at least from the mmc core point of view. Everywhere it's used, we use
> > MMC_UHS2_INITIALIZED too.
> >
>
> Yes. MMC_UHS2_SUPPORT is only useful in patch set: "Add support UHS-II
> for GL9755"
> (https://patchwork.kernel.org/project/linux-mmc/list/?series=378627&archive=both).
>
> I will remove it in V4.
>
> > Moreover, in patch 2 (mmc: core: Prepare to support SD UHS-II cards) I
> > added MMC_CAP2_SD_UHS2 as a generic host cap, which is being used in
> > mmc_attach_sd_uhs2(). This should be sufficient as a generic cap for
> > UHS2, I think.
> >
> > >   - bit 1: MMC_UHS2_INITIALIZED
> > >              This flag indicates if current uhs2 sd card  had been initialized.
> >
> > In patch 2 (mmc: core: Prepare to support SD UHS-II cards), I added
> > MMC_TIMING_SD_UHS2. It looks like it tries to serve a similar purpose.
> >
>
> MMC_TIMING_SD_UHS2 is used to indicate that host controller is using UHS2
> timing now. But MMC_UHS2_INITIALIZED=1 means that UHS2 interface
> initialization is complete. The meaning of these two flags are different.
>
> > Moreover, I don't see where MMC_UHS2_INITIALIZED is getting set, but I
> > assume it must be before we try to initialize the card by sending UHS2
> > specific commands to it, right?
> >
>
> It is set in sd_uhs2_init_card():
> static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
> {
>         struct mmc_card *card;
>         u32 node_id;
>         int err;
>         [...]
>         if (card->uhs2_state & MMC_UHS2_SPEED_B) {
>                 err = sd_uhs2_change_speed(host, node_id);
>                 if (err)
>                        return err;
>         }
>
>         card->uhs2_state |= MMC_UHS2_INITIALIZED;

This is probably a local change present only in your own tree, because
I don't see this anywhere in the v3 series.

In any case, it's not obvious to me why you need to set it exactly at
this point. Can you clarify why?

Perhaps, as I stated before, it's time to post the entire series,
including the host driver changes. In this way, I should be able to
get the complete picture.

>
>         err = sd_uhs2_legacy_init(host, card);
>         if (err)
>                 goto err;
>         [...]
>
> }
>
> > >   - bit 2: MMC_UHS2_2L_HD
> > >              This flag indicates the speed mode of current uhs2 sd card is
> > >              2L-HD mode. The host sets DCMD argument[0] bit 6
> > >              according to this flag. That means this flag was checked in
> > >              every MMC request.
> >
> > If we compare how we managed things like this for the legacy
> > interface, this information is kept in the "ios->timing" variable.
> >
> > >   - bit 3: MMC_UHS2_APP_CMD
> > >              This flag indicates if the current SD_TRAN command is an APP
> > >              command. The host sets DCMD argument[0] bit 14  according to
> > >              this flag.  That means this flag was checked in every MMC
> > >              request.
> >
> > Alright, so it's a specific flag for UHS2 commands.
> >
> > >   - bit 4: MMC_UHS2_SPEED_B
> > >              This flag indicates the speed range of current UHS2 sd card. This
> > >              flag is used only during uhs2 sd card initialization.
> > >
> > > > It looks to me that we should rather add a new variable to the struct
> > > > mmc_host and perhaps name it "uhs2_ios", to keep this data. Whether we
> > > > need to create a new struct for "uhs2_ios" or if it's better to extend
> > > > struct mmc_ios, I am not sure. I guess exploring this by writing the
> > > > code would tell us what is best suited.
> > > >
> > >
> > > I will add a new variable to the struct mmc_host and name it "uhs2_ios".
> > > In UHS2 CCMD/DCMD command packet, DM in TMODE(bit 6 in Argument)
> > > and bit APP(bit 15 in Argument) are set according to MMC_UHS2_2L_HD
> > > and MMC_UHS2_APP_CMD. Hence, I plan to define struct uhs2_ios as:
> > > struct sd_uhs2_ios {
> > >         bool                  is_2L_HD_mode;    /* DM bit in TMODE in UHS2 DCDM
> > >                                                                   *
> > > argument will be set to 1 when
> >
> > Seems reasonable.
> >
> > >                                                                   *
> > > this field is true. */
> > >         bool                  is_APP_CMD;         /* APP bit in UHS2 CCMD/DCDM
> > >                                                                   *
> > > argument will be set to 1 when
> > >                                                                   *
> > > this field is true. */
> >
> > Sounds like this better belongs in the struct uhs2_command.
> >
>
> Indeed, it is more reasonable to put is_APP_CMD in uhs2_cmd. But uhs2_cmd
> is allocated in mmc_start_request() and is_APP_CMD is set in mmc_app_cmd(),
> which is called prior to mmc_start_request(). So I use this flag as a mark and
> check it when filling uhs2_cmd in uhs2_prepare_sd_cmd().

I couldn't find where MMC_UHS2_APP_CMD was getting set, but I assume
you have some local changes that make it set in mmc_app_cmd() then.

I decided to have a closer look at this. Instead of using a flag
(MMC_UHS2_APP_CMD) for this, let's re-work the code so it becomes
possible to prepare the "uhs2_cmd" in mmc_app_cmd() too. That means,
from mmc_start_request() we need to check if the mmc_cmd->uhs2_cmd has
already been assigned and then just leave it as is.

>
> > >         unsigned int     power_delay_ms;    /* waiting for stable power */
> > > };
> > >
> > > As for MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B, I plan to add a variable
> > > in struct mmc_card and name it "uhs2_state", to keep current UHS2 card states.
> > > struct mmc_card {
> > >         struct mmc_host *host; /* the host this device belongs to */
> > >         [...]
> > >         struct  sd_uhs2_config  uhs2_config;   /* SD UHS-II config */
> > >         u8                                  uhs2_state;     /* SD
> > > UHS-II states */
> > > #define MMC_UHS2_INITIALIZED    BIT(1)
> > > #define MMC_UHS2_SPEED_B        BIT(2)
> > >         [...]
> >
> > Please don't.
> >
> > If it's configurations of the card, the data belongs under struct
> > sd_uhs2_config. If it's configuration of the host, the data typically
> > belongs in the struct sd_uhs2_ios.
> >
> > MMC_UHS2_SPEED_B is typically a speed mode for UHS2. For the legacy
> > interface in the struct mmc_ios, we have an "unsigned char timing"
> > variable to keep things like these. We need something similar in the
> > struct sd_uhs2_ios for this, I think. Maybe we should even turn the
> > bool for "2L_HD_mode" into being part of this "timing" variable.
> >
>
> Actually, MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B are more
> like "is UHS2 interface initialization complete?" and "is current UHS2 sd card
> support Range B clock?". They are not configurations but a kind of flags which
> are used to tell driver:
>     - MMC_UHS2_SPEED_B:     Current UHS2 sd card supports Range B clock
>                                                    frequency. Driver
> can execute sd_uhs2_change_speed()
>                                                    after sd_uhs2_config_write().

I assume you mean that if both the card and the host supports Range B
speed mode for UHS-II, we should try to switch to it.

Then I have two follow up questions related to this.
1) Is there some specific need to inform the host driver, in the
progress of switching to the new speed mode, to make this work? I
would believe so, but I don't have the complete picture.
2) After a successful switch to the new speed mode, we should keep
track of that we are running in this mode. In this way we can report
this in logs/debugfs. How do you intend to do this?

>     - MMC_UHS2_INITIALIZED: UHS2 interface handshake done. Driver can access
>                                                    current UHS2 sd
> card with UHS2 interface.

What does "driver can access UHS2 interface" really mean? Is it that
the card is ready to accept SD-TRAN commands, for example? Can you try
to be a bit more precise, please?

>
> That's why I named it 'uhs2_state'. What do you think?

Let's see where the discussion brings us. Although, "state" is rather
confusing - and it's inconsistent with the ways things are implemented
for the legacy interface.

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions
  2022-04-13 11:03               ` Ulf Hansson
@ 2022-04-14 12:41                 ` Lai Jason
  0 siblings, 0 replies; 27+ messages in thread
From: Lai Jason @ 2022-04-14 12:41 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: AKASHI Takahiro, Adrian Hunter, linux-mmc, dlunev, Ben Chuang,
	GregTu[杜啟軒],
	Jason Lai, otis.wu

On Wed, Apr 13, 2022 at 7:04 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> [...]
>
> > > > > > > >  static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card)
> > > > > > > >  {
> > > > > > > > +       struct mmc_command cmd = {0};
> > > > > > > > +       struct uhs2_command uhs2_cmd = {};
> > > > > > > > +       u16 header = 0, arg = 0;
> > > > > > > > +       u32 payload[2];
> > > > > > > > +       u8 nMinDataGap;
> > > > > > > > +       u8 plen;
> > > > > > > > +       int err;
> > > > > > > > +       u8 resp[5] = {0};
> > > > > > > > +       u8 resp_len = 5;
> > > > > > > > +
> > > > > > > > +       header = UHS2_NATIVE_PACKET |
> > > > > > > > +                UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
> > > > > > > > +       arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
> > > > > > > > +              UHS2_NATIVE_CMD_WRITE |
> > > > > > > > +              UHS2_NATIVE_CMD_PLEN_8B |
> > > > > > > > +              (UHS2_DEV_CONFIG_GEN_SET >> 8);
> > > > > > > > +
> > > > > > > > +       if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD &&
> > > > > > > > +           host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) {
> > > > > > > > +               /* Support HD */
> > > > > > > > +               host->uhs2_caps.flags |= MMC_UHS2_2L_HD;
> > > > > > >
> > > > > > > How is the uhs2_caps.flags field intended to be used? To me it looks
> > > > > > > like a way for the mmc core to exchange status/configuration
> > > > > > > information about the initialization process of the card, with the mmc
> > > > > > > host driver. Perhaps there is more too. Is that correct?
> > > > > > >
> > > > > > > If so, I think it looks quite similar for what we have in the struct
> > > > > > > mmc_ios, for the legacy interface(s). I am not saying we should use
> > > > > > > that, just trying to understand what would suit best here.
> > > > > > >
> > > > > >
> > > > > > The usage of uhs2_caps.flags is spread out through core and host.
> > > > > > All operations related to it cannot be integrated into uhs2_set_ios()
> > > > > > simply. I recommend maintaining the status quo.
> > > > >
> > > > > What is puzzling to me, is that the data is stored below uhs2_caps.*
> > > > > and that it's called "flags". It's not self-explanatory and it's not
> > > > > consistent with the way we use the ->set_ios() callback, for the
> > > > > legacy interface.
> > > > >
> > > >
> > > > Sorry,  I did not understand why uhs2_caps.flag is related to set_ios().
> > > > The bit flags are used to indicate current status of UHS2 card:
> > > >   - bit 0: MMC_UHS2_SUPPORT
> > > >              This flag indicates if current sd card  supports UHS2 mode.
> > >
> > > By looking at the code, it seems like MMC_UHS2_SUPPORT is redundant,
> > > at least from the mmc core point of view. Everywhere it's used, we use
> > > MMC_UHS2_INITIALIZED too.
> > >
> >
> > Yes. MMC_UHS2_SUPPORT is only useful in patch set: "Add support UHS-II
> > for GL9755"
> > (https://patchwork.kernel.org/project/linux-mmc/list/?series=378627&archive=both).
> >
> > I will remove it in V4.
> >
> > > Moreover, in patch 2 (mmc: core: Prepare to support SD UHS-II cards) I
> > > added MMC_CAP2_SD_UHS2 as a generic host cap, which is being used in
> > > mmc_attach_sd_uhs2(). This should be sufficient as a generic cap for
> > > UHS2, I think.
> > >
> > > >   - bit 1: MMC_UHS2_INITIALIZED
> > > >              This flag indicates if current uhs2 sd card  had been initialized.
> > >
> > > In patch 2 (mmc: core: Prepare to support SD UHS-II cards), I added
> > > MMC_TIMING_SD_UHS2. It looks like it tries to serve a similar purpose.
> > >
> >
> > MMC_TIMING_SD_UHS2 is used to indicate that host controller is using UHS2
> > timing now. But MMC_UHS2_INITIALIZED=1 means that UHS2 interface
> > initialization is complete. The meaning of these two flags are different.
> >
> > > Moreover, I don't see where MMC_UHS2_INITIALIZED is getting set, but I
> > > assume it must be before we try to initialize the card by sending UHS2
> > > specific commands to it, right?
> > >
> >
> > It is set in sd_uhs2_init_card():
> > static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
> > {
> >         struct mmc_card *card;
> >         u32 node_id;
> >         int err;
> >         [...]
> >         if (card->uhs2_state & MMC_UHS2_SPEED_B) {
> >                 err = sd_uhs2_change_speed(host, node_id);
> >                 if (err)
> >                        return err;
> >         }
> >
> >         card->uhs2_state |= MMC_UHS2_INITIALIZED;
>
> This is probably a local change present only in your own tree, because
> I don't see this anywhere in the v3 series.
>

Yes. They are in my V4 candidate.

> In any case, it's not obvious to me why you need to set it exactly at
> this point. Can you clarify why?
>

After changing data rate to Range B, the UHS2 handshake between
host and card is complete. Driver can start to use SD CCMD/DCMD
to control UHS2 SD card. Following listed the usage of UHS2 related
variables:
    MMC_TIMING_SD_UHS2 in host->ios.timing:
        - Enable UHS2 interface on host controller (drivers/mmc/host)
        - Read preset value from Preset Value Register of SD Host
Controller. (drivers/mmc/host)
    MMC_UHS2_SPEED_B in card->uhs2_state:
        - Used to indicate if current sd card supports UHS2 Range B data rate.
    MMC_UHS2_INITIALIZED in card->uhs2_state:
        - Used to indicate if UHS2 PHY handshaking between host
controller and sd card is complete.

> Perhaps, as I stated before, it's time to post the entire series,
> including the host driver changes. In this way, I should be able to
> get the complete picture.
>
> >
> >         err = sd_uhs2_legacy_init(host, card);
> >         if (err)
> >                 goto err;
> >         [...]
> >
> > }
> >
> > > >   - bit 2: MMC_UHS2_2L_HD
> > > >              This flag indicates the speed mode of current uhs2 sd card is
> > > >              2L-HD mode. The host sets DCMD argument[0] bit 6
> > > >              according to this flag. That means this flag was checked in
> > > >              every MMC request.
> > >
> > > If we compare how we managed things like this for the legacy
> > > interface, this information is kept in the "ios->timing" variable.
> > >
> > > >   - bit 3: MMC_UHS2_APP_CMD
> > > >              This flag indicates if the current SD_TRAN command is an APP
> > > >              command. The host sets DCMD argument[0] bit 14  according to
> > > >              this flag.  That means this flag was checked in every MMC
> > > >              request.
> > >
> > > Alright, so it's a specific flag for UHS2 commands.
> > >
> > > >   - bit 4: MMC_UHS2_SPEED_B
> > > >              This flag indicates the speed range of current UHS2 sd card. This
> > > >              flag is used only during uhs2 sd card initialization.
> > > >
> > > > > It looks to me that we should rather add a new variable to the struct
> > > > > mmc_host and perhaps name it "uhs2_ios", to keep this data. Whether we
> > > > > need to create a new struct for "uhs2_ios" or if it's better to extend
> > > > > struct mmc_ios, I am not sure. I guess exploring this by writing the
> > > > > code would tell us what is best suited.
> > > > >
> > > >
> > > > I will add a new variable to the struct mmc_host and name it "uhs2_ios".
> > > > In UHS2 CCMD/DCMD command packet, DM in TMODE(bit 6 in Argument)
> > > > and bit APP(bit 15 in Argument) are set according to MMC_UHS2_2L_HD
> > > > and MMC_UHS2_APP_CMD. Hence, I plan to define struct uhs2_ios as:
> > > > struct sd_uhs2_ios {
> > > >         bool                  is_2L_HD_mode;    /* DM bit in TMODE in UHS2 DCDM
> > > >                                                                   *
> > > > argument will be set to 1 when
> > >
> > > Seems reasonable.
> > >
> > > >                                                                   *
> > > > this field is true. */
> > > >         bool                  is_APP_CMD;         /* APP bit in UHS2 CCMD/DCDM
> > > >                                                                   *
> > > > argument will be set to 1 when
> > > >                                                                   *
> > > > this field is true. */
> > >
> > > Sounds like this better belongs in the struct uhs2_command.
> > >
> >
> > Indeed, it is more reasonable to put is_APP_CMD in uhs2_cmd. But uhs2_cmd
> > is allocated in mmc_start_request() and is_APP_CMD is set in mmc_app_cmd(),
> > which is called prior to mmc_start_request(). So I use this flag as a mark and
> > check it when filling uhs2_cmd in uhs2_prepare_sd_cmd().
>
> I couldn't find where MMC_UHS2_APP_CMD was getting set, but I assume
> you have some local changes that make it set in mmc_app_cmd() then.
>

It should be set here:
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
{
    int err;
    struct mmc_command cmd = {};
    [...]
    if (card->uhs2_state & MMC_UHS2_INITIALIZED) {
        host->uhs2_ios.is_APP_CMD = true;
        /* UHS2 does not support APP command(CMD55). It use APP bit in
UHS2 command argument to indicate APP CMD */
        return 0;
    }
    [...]
}
> I decided to have a closer look at this. Instead of using a flag
> (MMC_UHS2_APP_CMD) for this, let's re-work the code so it becomes
> possible to prepare the "uhs2_cmd" in mmc_app_cmd() too. That means,
> from mmc_start_request() we need to check if the mmc_cmd->uhs2_cmd has
> already been assigned and then just leave it as is.
>

uhs2_cmd is a local variable in mmc_start_request() and mmc_cqe_start_req().

> >
> > > >         unsigned int     power_delay_ms;    /* waiting for stable power */
> > > > };
> > > >
> > > > As for MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B, I plan to add a variable
> > > > in struct mmc_card and name it "uhs2_state", to keep current UHS2 card states.
> > > > struct mmc_card {
> > > >         struct mmc_host *host; /* the host this device belongs to */
> > > >         [...]
> > > >         struct  sd_uhs2_config  uhs2_config;   /* SD UHS-II config */
> > > >         u8                                  uhs2_state;     /* SD
> > > > UHS-II states */
> > > > #define MMC_UHS2_INITIALIZED    BIT(1)
> > > > #define MMC_UHS2_SPEED_B        BIT(2)
> > > >         [...]
> > >
> > > Please don't.
> > >
> > > If it's configurations of the card, the data belongs under struct
> > > sd_uhs2_config. If it's configuration of the host, the data typically
> > > belongs in the struct sd_uhs2_ios.
> > >
> > > MMC_UHS2_SPEED_B is typically a speed mode for UHS2. For the legacy
> > > interface in the struct mmc_ios, we have an "unsigned char timing"
> > > variable to keep things like these. We need something similar in the
> > > struct sd_uhs2_ios for this, I think. Maybe we should even turn the
> > > bool for "2L_HD_mode" into being part of this "timing" variable.
> > >
> >
> > Actually, MMC_UHS2_INITIALIZED and MMC_UHS2_SPEED_B are more
> > like "is UHS2 interface initialization complete?" and "is current UHS2 sd card
> > support Range B clock?". They are not configurations but a kind of flags which
> > are used to tell driver:
> >     - MMC_UHS2_SPEED_B:     Current UHS2 sd card supports Range B clock
> >                                                    frequency. Driver
> > can execute sd_uhs2_change_speed()
> >                                                    after sd_uhs2_config_write().
>
> I assume you mean that if both the card and the host supports Range B
> speed mode for UHS-II, we should try to switch to it.
>
> Then I have two follow up questions related to this.
> 1) Is there some specific need to inform the host driver, in the
> progress of switching to the new speed mode, to make this work? I
> would believe so, but I don't have the complete picture.

The driver I mentioned above generally includes core/sd/sd_uhs2/mmc_ops.
If we put all initialization flows in one function, MMC_UHS2_SPEED_B will be
declared as a local variable. I try to use a pseudo code to describe
their usage:

int uhs2_init(struct mmc_host *host)
{
    bool    isRangeB_supported;
    int     err;

    err = phy_init();
    if (err)
        return err;

    err= sd_uhs2_dev_init();
    if (err)
        return err;

    err= sd_uhs2_enum();
    if (err)
        return err;

    /* sd_uhs2_config_read() */
    err = Read_Generic_Capability_Register();
    if (err)
        return err;
    err = Read_PHY_Capability_Register();
    if (err)
        return err;
    err= Read_LINK_TRAN_Capability_Register();
    if (err)
        return err;

    /* sd_uhs2_config_Write() */
    Select_Lane_Number_Fit_to_HostController_and_Card;
    err = Write_Generic_Capability_Register();
    if (err)
        return err;

    if (MMC_Host support Range B data rate)
        isRangeB_supported = true;
    else
        isRangeB_supported = false;
    Select_max_lss_sync_Fit_to_HostController_and_Card;
    Select_max_lss_dir_Fit_to_HostController_and_Card;
    err = Write_PHY_Capability_Register();
    if (err)
        return err;

    Select_NFCU_MaxBlkLen_DataGap_MaxRetry_Fit_to_HostController_and_Card;
    err = Write_LINK_TRAN_Capability_Register();
    if (err)
        return err;

    /* card->uhs2_state bit 2: MMC_UHS2_SPEED_B */
    if (isRangeB_supported) {
        err = sd_uhs2_change_speed();
        if (err)
            return err;
    }

    /*
     * All legacy SD functions will use uhs2_cmd to send SD command if
     * this flag is set. Maybe it's easier to understand if I rename it to
     * MMC_SD_UHS2_MODE_ACTIVE?
     */
    card->uhs2_state |= MMC_UHS2_INITIALIZED;
    return 0;
}

> 2) After a successful switch to the new speed mode, we should keep
> track of that we are running in this mode. In this way we can report
> this in logs/debugfs. How do you intend to do this?
>

The mode will be kept until hw_reset/sd_uhs2_power_off/sd_uhs2_remove
being removed. So we can reach the goal by putting some pr_info() there
and check the mode in system log.

> >     - MMC_UHS2_INITIALIZED: UHS2 interface handshake done. Driver can access
> >                                                    current UHS2 sd
> > card with UHS2 interface.
>
> What does "driver can access UHS2 interface" really mean? Is it that
> the card is ready to accept SD-TRAN commands, for example? Can you try
> to be a bit more precise, please?
>

You are right.
In UHS-II Addendum v1.02, Figure 3-10: UHS-II Transaction by SD-TRAN can
help you learn more about SD-TRAN and "Figure 3-11: UHS-II  Initialization Flow"
shows the initialization sequence of uhs2 interface:

Power On or FULL_RESET --> PHY Initialization --> Device Initialization -->
         Enumeration --> Configuration --> Active

After sd card enters Active state, host can start to execute legacy sd
command via sdhci_uhs2_ops.send_command.
mmc_wait_for_cmd --> mmc_wait_for_req
--> __mmc_start_req --> mmc_start_request -> __mmc_start_request
--> host->ops->request==sdhci_request
--> sdhci_send_command_retry --> sdhci_send_command
--> sdhci_uhs2_ops.send_command==sdhci_uhs2_send_command

> >
> > That's why I named it 'uhs2_state'. What do you think?
>
> Let's see where the discussion brings us. Although, "state" is rather
> confusing - and it's inconsistent with the ways things are implemented
> for the legacy interface.
>

I will submit V4, which does not contain uhs2 host driver, in these 2 days for
your review. As for adding uhs2 host part, you prefer adding in V6 or
create another series of patches?

Kind  regards
Jason

> [...]
>
> Kind regards
> Uffe

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2022-04-14 12:42 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-22  3:39 [PATCH V3 0/7] Preparations to support SD UHS-II cards Jason Lai
2022-02-22  3:39 ` [PATCH V3 1/7] mmc: core: Cleanup printing of speed mode at card insertion Jason Lai
2022-02-22  3:39 ` [PATCH V3 2/7] mmc: core: Prepare to support SD UHS-II cards Jason Lai
2022-02-22  3:39 ` [PATCH V3 3/7] mmc: core: Announce successful insertion of an SD UHS-II card Jason Lai
2022-02-22  3:39 ` [PATCH V3 4/7] mmc: core: Extend support for mmc regulators with a vqmmc2 Jason Lai
2022-02-22  3:39 ` [PATCH V3 5/7] mmc: add UHS-II related definitions in headers Jason Lai
2022-02-22  3:39 ` [PATCH V3 6/7] mmc: Implement content of UHS-II card initialization functions Jason Lai
2022-03-23 16:15   ` Ulf Hansson
2022-03-24  1:29     ` AKASHI Takahiro
2022-03-24  6:09       ` Lai Jason
2022-03-24 10:22       ` Ulf Hansson
2022-03-24 10:50         ` AKASHI Takahiro
2022-03-25  3:53           ` Lai Jason
2022-03-25  8:10             ` Ulf Hansson
2022-04-07 10:45     ` Lai Jason
2022-04-07 15:00       ` Ulf Hansson
2022-04-12  3:32         ` Lai Jason
2022-04-12 11:57           ` Ulf Hansson
2022-04-13  7:18             ` Lai Jason
2022-04-13 11:03               ` Ulf Hansson
2022-04-14 12:41                 ` Lai Jason
2022-02-22  3:39 ` [PATCH V3 7/7] mmc: core: Support UHS-II card access Jason Lai
2022-03-23 16:23   ` Ulf Hansson
2022-04-07 11:00     ` Lai Jason
2022-04-07 15:21       ` Ulf Hansson
2022-03-18 13:16 ` [PATCH V3 0/7] Preparations to support SD UHS-II cards Ulf Hansson
2022-03-23 13:45   ` Ulf Hansson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.