[PATCH V22 07/22] mmc: core: Support UHS-II card control and access

Victor Shih posted 22 patches 2 months, 2 weeks ago
There is a newer version of this series
[PATCH V22 07/22] mmc: core: Support UHS-II card control and access
Posted by Victor Shih 2 months, 2 weeks ago
From: Victor Shih <victor.shih@genesyslogic.com.tw>

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

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---

Updates in V16:
 - Separate the Error Recovery mechanism into a separate patch#8.

Updates in V15:
 - Move struct uhs2_command uhs2_cmd to struct mmc_request and
   modify whatever other changers to make it work.
 - Refer the SD Host Controller Standard Specification Section 3.10
   to add Error Recovery mechanism to recover the command error.

Updates in V13:
 - Separate __mmc_go_idle() into one patch for re-factorring the code.
 - Move mmc_decode_scr declaration to sd.h.
 - Ues uhs2_sd_tran to stead MMC_UHS2_SD_TRAN. 
 - Drop unnecessary comment.

Updates in V12:
 - Use mmc_op_multi() to check DCMD which supports multi read/write
   in mmc_uhs2_prepare_cmd().

Updates in V10:
 - Move some definitions of PatchV9[02/23] to PatchV10[06/23].
 - Move some definitions of PatchV9[05/23] to PatchV10[06/23].
 - Drop do_multi in the mmc_blk_rw_rq_prep().
 - Use tmode_half_duplex to instead of uhs2_tmode0_flag.
 - Move entire control of the tmode into mmc_uhs2_prepare_cmd().

Updates in V8:
 - Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
 - Modify return value in sd_uhs2_attach().

Updates in V7:
 - Add mmc_uhs2_card_prepare_cmd helper function in sd_ops.h.
 - Drop uhs2_state in favor of ios->timing.
 - Remove unnecessary functions.

---

 drivers/mmc/core/core.c    |    6 +-
 drivers/mmc/core/sd.c      |   10 +-
 drivers/mmc/core/sd.h      |    5 +
 drivers/mmc/core/sd_ops.c  |    9 +
 drivers/mmc/core/sd_ops.h  |   13 +
 drivers/mmc/core/sd_uhs2.c | 1100 ++++++++++++++++++++++++++++++++++--
 include/linux/mmc/core.h   |   17 +
 include/linux/mmc/host.h   |   15 +
 8 files changed, 1136 insertions(+), 39 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 8b8aff131f8b..1841a2190571 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -351,6 +351,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 	if (err)
 		return err;
 
+	mmc_uhs2_card_prepare_cmd(host, mrq);
+
 	led_trigger_event(host->led, LED_FULL);
 	__mmc_start_request(host, mrq);
 
@@ -450,6 +452,8 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
 	if (err)
 		goto out_err;
 
+	mmc_uhs2_card_prepare_cmd(host, mrq);
+
 	err = host->cqe_ops->cqe_request(host, mrq);
 	if (err)
 		goto out_err;
@@ -1132,7 +1136,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
 		return 0;
 	}
 
-	if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
+	if (!mmc_card_uhs2(host) && host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
 		bit = ffs(ocr) - 1;
 		ocr &= 3 << bit;
 		mmc_power_cycle(host, ocr);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 12fe282bea77..d5576a2f4435 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -194,7 +194,7 @@ static int mmc_decode_csd(struct mmc_card *card)
 /*
  * Given a 64-bit response, decode to our card SCR structure.
  */
-static int mmc_decode_scr(struct mmc_card *card)
+int mmc_decode_scr(struct mmc_card *card)
 {
 	struct sd_scr *scr = &card->scr;
 	unsigned int scr_struct;
@@ -894,7 +894,7 @@ int mmc_sd_get_csd(struct mmc_card *card)
 	return 0;
 }
 
-static int mmc_sd_get_ro(struct mmc_host *host)
+int mmc_sd_get_ro(struct mmc_host *host)
 {
 	int ro;
 
@@ -1623,7 +1623,7 @@ static void mmc_sd_detect(struct mmc_host *host)
 	}
 }
 
-static int sd_can_poweroff_notify(struct mmc_card *card)
+int sd_can_poweroff_notify(struct mmc_card *card)
 {
 	return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
 }
@@ -1651,7 +1651,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
 	return 0;
 }
 
-static int sd_poweroff_notify(struct mmc_card *card)
+int sd_poweroff_notify(struct mmc_card *card)
 {
 	struct sd_busy_data cb_data;
 	u8 *reg_buf;
@@ -1699,7 +1699,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
 	if (mmc_card_suspended(card))
 		goto out;
 
-	if (sd_can_poweroff_notify(card))
+	if (mmc_sd_can_poweroff_notify(card))
 		err = sd_poweroff_notify(card);
 	else if (!mmc_host_is_spi(host))
 		err = mmc_deselect_cards(host);
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index fe6dd46927a4..49afaef269cd 100644
--- a/drivers/mmc/core/sd.h
+++ b/drivers/mmc/core/sd.h
@@ -11,10 +11,15 @@ struct mmc_card;
 
 int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
 int mmc_sd_get_csd(struct mmc_card *card);
+int mmc_decode_scr(struct mmc_card *card);
+int mmc_sd_get_ro(struct mmc_host *host);
 void mmc_decode_cid(struct mmc_card *card);
 int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
 	bool reinit);
 unsigned mmc_sd_get_max_clock(struct mmc_card *card);
 int mmc_sd_switch_hs(struct mmc_card *card);
 
+int sd_can_poweroff_notify(struct mmc_card *card);
+int sd_poweroff_notify(struct mmc_card *card);
+
 #endif
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index f93c392040ae..5cd1216edf0d 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -41,6 +41,15 @@ int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
 	if (WARN_ON(card && card->host != host))
 		return -EINVAL;
 
+	/*
+	 * UHS2 packet has APP bit so only set APP_CMD flag here.
+	 * Will set the APP bit when assembling UHS2 packet.
+	 */
+	if (host->uhs2_sd_tran) {
+		host->uhs2_app_cmd = true;
+		return 0;
+	}
+
 	cmd.opcode = MMC_APP_CMD;
 
 	if (card) {
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index 7667fc223b74..e3af68a52de8 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -11,6 +11,7 @@
 #include <linux/types.h>
 
 struct mmc_card;
+struct mmc_command;
 struct mmc_host;
 
 int mmc_app_set_bus_width(struct mmc_card *card, int width);
@@ -21,6 +22,18 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
 int mmc_app_send_scr(struct mmc_card *card);
 int mmc_app_sd_status(struct mmc_card *card, void *ssr);
 int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
+void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
+
+static inline void mmc_uhs2_card_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
+{
+	if (host->uhs2_sd_tran)
+		mmc_uhs2_prepare_cmd(host, mrq);
+}
+
+static inline int mmc_sd_can_poweroff_notify(struct mmc_card *card)
+{
+	return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
+}
 
 #endif
 
diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
index beb2541338ff..85939a2582dc 100644
--- a/drivers/mmc/core/sd_uhs2.c
+++ b/drivers/mmc/core/sd_uhs2.c
@@ -1,23 +1,51 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (C) 2021 Linaro Ltd
- *
  * Author: Ulf Hansson <ulf.hansson@linaro.org>
  *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ * Author: Yi Sun <yi.y.sun@intel.com>
+ *
+ * Copyright (C) 2020 Genesys Logic, Inc.
+ * Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
+ *
+ * Copyright (C) 2020 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ *
+ * Copyright (C) 2022 Genesys Logic, Inc.
+ * Authors: Jason Lai <jason.lai@genesyslogic.com.tw>
+ *
+ * Copyright (C) 2023 Genesys Logic, Inc.
+ * Authors: Victor Shih <victor.shih@genesyslogic.com.tw>
+ *
  * Support for SD UHS-II cards
  */
 #include <linux/err.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sd_uhs2.h>
 
+#include "card.h"
 #include "core.h"
 #include "bus.h"
 #include "sd.h"
+#include "sd_ops.h"
 #include "mmc_ops.h"
 
+#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US  (1 * 1000)
+#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100
+
 static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
 
+struct sd_uhs2_wait_active_state_data {
+	struct mmc_host *host;
+	struct mmc_command *cmd;
+};
+
 static int sd_uhs2_power_up(struct mmc_host *host)
 {
 	int err;
@@ -42,12 +70,46 @@ static int sd_uhs2_power_off(struct mmc_host *host)
 
 	host->ios.vdd = 0;
 	host->ios.clock = 0;
-	host->ios.timing = MMC_TIMING_LEGACY;
+	/* Must set UHS2 timing to identify UHS2 mode */
+	host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
 	host->ios.power_mode = MMC_POWER_OFF;
+	host->uhs2_sd_tran = false;
 
 	return host->ops->uhs2_control(host, UHS2_SET_IOS);
 }
 
+/*
+ * 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,
+				 u8 plen, u8 resp_len)
+{
+	uhs2_cmd->payload_len = plen * sizeof(u32);
+	uhs2_cmd->packet_len = uhs2_cmd->payload_len + 4;
+
+	cmd->uhs2_cmd = uhs2_cmd;
+	cmd->uhs2_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
@@ -55,7 +117,15 @@ static int sd_uhs2_power_off(struct mmc_host *host)
  */
 static int sd_uhs2_phy_init(struct mmc_host *host)
 {
-	return 0;
+	int err;
+
+	err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
+	if (err) {
+		pr_err("%s: failed to initial phy for UHS-II!\n",
+		       mmc_hostname(host));
+	}
+
+	return err;
 }
 
 /*
@@ -64,6 +134,83 @@ 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;
+	u8 gd = 0;
+	int err;
+
+	dap = host->uhs2_caps.dap;
+	gap = host->uhs2_caps.gap;
+
+	/*
+	 * Refer to UHS-II Addendum Version 1.02 Figure 6-21 to see DEVICE_INIT CCMD format.
+	 * Head:
+	 *      - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+	 *      - IOADR = CMD_BASE + 002h
+	 * Payload:
+	 *      - bit [3:0]  : GAP(Group Allocated Power)
+	 *      - bit [7:4]  : GD(Group Descriptor)
+	 *      - bit [11]   : Complete Flag
+	 *      - bit [15:12]: DAP(Device Allocated Power)
+	 */
+	uhs2_cmd.header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
+	uhs2_cmd.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.
+	 * Let's retry the DEVICE_INIT command no more than 30 times.
+	 */
+	for (cnt = 0; cnt < 30; cnt++) {
+		uhs2_cmd.payload[0] = ((dap & 0xF) << 12) |
+				       UHS2_DEV_INIT_COMPLETE_FLAG |
+				       ((gd & 0xF) << 4) |
+				       (gap & 0xF);
+
+		sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, UHS2_DEV_INIT_PAYLOAD_LEN,
+				     UHS2_DEV_INIT_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);
+			continue;
+		}
+
+		if (uhs2_cmd.uhs2_resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) {
+			pr_err("%s: DEVICE_INIT response is wrong!\n",
+			       mmc_hostname(host));
+			return -EIO;
+		}
+
+		if (uhs2_cmd.uhs2_resp[5] & 0x8) {
+			host->uhs2_caps.group_desc = gd;
+			return 0;
+		}
+		resp_gap = uhs2_cmd.uhs2_resp[4] & 0x0F;
+		if (gap == resp_gap)
+			gd++;
+	}
+
+	if (err) {
+		pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return err;
+	}
+
 	return 0;
 }
 
@@ -74,6 +221,48 @@ 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 = {};
+	u8 id_f = 0xF, id_l = 0x0;
+	int err;
+
+	/*
+	 * Refer to UHS-II Addendum Version 1.02 Figure 6-28 to see ENUMERATE CCMD format.
+	 * Header:
+	 *      - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+	 *      - IOADR = CMD_BASE + 003h
+	 * Payload:
+	 *      - bit [3:0]: ID_L(Last Node ID)
+	 *      - bit [7:4]: ID_F(First Node ID)
+	 */
+	uhs2_cmd.header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD;
+	uhs2_cmd.arg = ((UHS2_DEV_CMD_ENUMERATE & 0xFF) << 8) |
+			UHS2_NATIVE_CMD_WRITE |
+			UHS2_NATIVE_CMD_PLEN_4B |
+			(UHS2_DEV_CMD_ENUMERATE >> 8);
+
+	uhs2_cmd.payload[0] = (id_f << 4) | id_l;
+	uhs2_cmd.payload[0] = cpu_to_be32(uhs2_cmd.payload[0]);
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, UHS2_DEV_ENUM_PAYLOAD_LEN, UHS2_DEV_ENUM_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 err;
+	}
+
+	if (uhs2_cmd.uhs2_resp[3] != (UHS2_DEV_CMD_ENUMERATE & 0xFF)) {
+		pr_err("%s: ENUMERATE response is wrong!\n",
+		       mmc_hostname(host));
+		return -EIO;
+	}
+
+	id_f = (uhs2_cmd.uhs2_resp[4] >> 4) & 0xF;
+	id_l = uhs2_cmd.uhs2_resp[4] & 0xF;
+	*node_id = id_f;
+
 	return 0;
 }
 
@@ -84,6 +273,180 @@ static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id)
  */
 static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card)
 {
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	u32 cap;
+	int err;
+
+	/*
+	 * Use Control Read CCMD to read Generic Capability from Configuration Register.
+	 * - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+	 * - IOADR = Generic Capability Register(CFG_BASE + 000h)
+	 */
+	uhs2_cmd.header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
+	uhs2_cmd.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, 0, 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;
+	}
+
+	/*
+	 * Generic Capability Register:
+	 * bit [7:0]  : Reserved
+	 * bit [13:8] : Device-Specific Number of Lanes and Functionality
+	 *              bit 8: 2L-HD
+	 *              bit 9: 2D-1U FD
+	 *              bit 10: 1D-2U FD
+	 *              bit 11: 2D-2U FD
+	 *              Others: Reserved
+	 * bit [14]   : DADR Length
+	 *              0: 4 bytes
+	 *              1: Reserved
+	 * bit [23:16]: Application Type
+	 *              bit 16: 0=Non-SD memory, 1=SD memory
+	 *              bit 17: 0=Non-SDIO, 1=SDIO
+	 *              bit 18: 0=Card, 1=Embedded
+	 * bit [63:24]: Reserved
+	 */
+	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;
+
+	/*
+	 * Use Control Read CCMD to read PHY Capability from Configuration Register.
+	 * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+	 * - IOADR = PHY Capability Register(CFG_BASE + 002h)
+	 */
+	uhs2_cmd.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, 0, 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;
+	}
+
+	/*
+	 * PHY Capability Register:
+	 * bit [3:0]  : PHY Minor Revision
+	 * bit [5:4]  : PHY Major Revision
+	 * bit [15]   : Support Hibernate Mode
+	 *              0: Not support Hibernate Mode
+	 *              1: Support Hibernate Mode
+	 * bit [31:16]: Reserved
+	 * bit [35:32]: Device-Specific N_LSS_SYN
+	 * bit [39:36]: Device-Specific N_LSS_DIR
+	 * bit [63:40]: Reserved
+	 */
+	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;
+
+	/*
+	 * Use Control Read CCMD to read LINK/TRAN Capability from Configuration Register.
+	 * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+	 * - IOADR = LINK/TRAN Capability Register(CFG_BASE + 004h)
+	 */
+	uhs2_cmd.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, 0, 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;
+	}
+
+	/*
+	 * LINK/TRAN Capability Register:
+	 * bit [3:0]  : LINK_TRAN Minor Revision
+	 * bit [5:4]  : LINK/TRAN Major Revision
+	 * bit [7:6]  : Reserved
+	 * bit [15:8] : Device-Specific N_FCU
+	 * bit [18:16]: Device Type
+	 *              001b=Host
+	 *              010b=Device
+	 *              011b=Reserved for CMD issuable Device
+	 * bit [19]   : Reserved
+	 * bit [31:20]: Device-Specific MAX_BLKLEN
+	 * bit [39:32]: Device-Specific N_DATA_GAP
+	 * bit [63:40]: Reserved
+	 */
+	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;
 }
 
@@ -98,26 +461,350 @@ 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 = {};
+	u8 nMinDataGap;
+	int err;
+
+	/*
+	 * Use Control Write CCMD to set Generic Setting in Configuration Register.
+	 * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+	 * - IOADR = Generic Setting Register(CFG_BASE + 008h)
+	 * - Payload = New contents to be written to Generic Setting Register
+	 */
+	uhs2_cmd.header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id;
+	uhs2_cmd.arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+			UHS2_NATIVE_CMD_WRITE |
+			UHS2_NATIVE_CMD_PLEN_8B |
+			(UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+	/*
+	 * 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;
+
+	uhs2_cmd.payload[0] = card->uhs2_config.n_lanes_set << UHS2_DEV_CONFIG_N_LANES_POS;
+	uhs2_cmd.payload[1] = 0;
+	uhs2_cmd.payload[0] = cpu_to_be32(uhs2_cmd.payload[0]);
+	uhs2_cmd.payload[1] = cpu_to_be32(uhs2_cmd.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, UHS2_CFG_WRITE_PAYLOAD_LEN, 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;
+	}
+
+	/*
+	 * Use Control Write CCMD to set PHY Setting in Configuration Register.
+	 * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+	 * - IOADR = PHY Setting Register(CFG_BASE + 00Ah)
+	 * - Payload = New contents to be written to PHY Setting Register
+	 */
+	uhs2_cmd.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) {
+		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->ios.timing = MMC_TIMING_UHS2_SPEED_B_HD;
+			nMinDataGap = 1;
+		} else {
+			/* Only support 2L-FD so far */
+			host->ios.timing = MMC_TIMING_UHS2_SPEED_B;
+			nMinDataGap = 3;
+		}
+		card->uhs2_config.speed_range_set = UHS2_DEV_CONFIG_PHY_SET_SPEED_B;
+	} else {
+		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->ios.timing = MMC_TIMING_UHS2_SPEED_A_HD;
+			nMinDataGap = 1;
+		} else {
+			/* Only support 2L-FD so far */
+			host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
+			nMinDataGap = 3;
+		}
+		card->uhs2_config.speed_range_set = UHS2_DEV_CONFIG_PHY_SET_SPEED_A;
+	}
+
+	uhs2_cmd.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;
+
+	uhs2_cmd.payload[1] = (card->uhs2_config.n_lss_dir_set << UHS2_DEV_CONFIG_N_LSS_DIR_POS) |
+			      card->uhs2_config.n_lss_sync_set;
+	uhs2_cmd.payload[0] = cpu_to_be32(uhs2_cmd.payload[0]);
+	uhs2_cmd.payload[1] = cpu_to_be32(uhs2_cmd.payload[1]);
+
+	memset(uhs2_cmd.uhs2_resp, 0, sizeof(uhs2_cmd.uhs2_resp));
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, UHS2_CFG_WRITE_PAYLOAD_LEN,
+			     UHS2_CFG_WRITE_PHY_SET_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 err;
+	}
+
+	if ((uhs2_cmd.uhs2_resp[2] & 0x80)) {
+		pr_err("%s: %s: UHS2 CMD not accepted, resp= 0x%x!\n",
+		       mmc_hostname(host), __func__, uhs2_cmd.uhs2_resp[2]);
+		return -EIO;
+	}
+
+	/*
+	 * Use Control Write CCMD to set LINK/TRAN Setting in Configuration Register.
+	 * - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+	 * - IOADR = LINK/TRAN Setting Register(CFG_BASE + 00Ch)
+	 * - Payload = New contents to be written to LINK/TRAN Setting Register
+	 */
+	uhs2_cmd.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);
+
+	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;
+
+	uhs2_cmd.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);
+	uhs2_cmd.payload[1] = card->uhs2_config.n_data_gap_set;
+	uhs2_cmd.payload[0] = cpu_to_be32(uhs2_cmd.payload[0]);
+	uhs2_cmd.payload[1] = cpu_to_be32(uhs2_cmd.payload[1]);
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, UHS2_CFG_WRITE_PAYLOAD_LEN, 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;
+	}
+
+	/*
+	 * Use Control Write CCMD to set Config Completion(payload bit 63) in Generic Setting
+	 * Register.
+	 * Header:
+	 *      - Control Write(R/W=1) with 8-Byte payload(PLEN=10b).
+	 *      - IOADR = PGeneric Setting Register(CFG_BASE + 008h)
+	 * Payload:
+	 *      - bit [63]: Config Completion
+	 *
+	 * DLSM transits to Active state immediately when Config Completion is set to 1.
+	 */
+	uhs2_cmd.arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+			UHS2_NATIVE_CMD_WRITE |
+			UHS2_NATIVE_CMD_PLEN_8B |
+			(UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+	uhs2_cmd.payload[0] = 0;
+	uhs2_cmd.payload[1] = UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE;
+	uhs2_cmd.payload[0] = cpu_to_be32(uhs2_cmd.payload[0]);
+	uhs2_cmd.payload[1] = cpu_to_be32(uhs2_cmd.payload[1]);
+
+	memset(uhs2_cmd.uhs2_resp, 0, sizeof(uhs2_cmd.uhs2_resp));
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, UHS2_CFG_WRITE_PAYLOAD_LEN,
+			     UHS2_CFG_WRITE_GENERIC_SET_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 err;
+	}
+
+	/* Set host Config Setting registers */
+	err = host->ops->uhs2_control(host, UHS2_SET_CONFIG);
+	if (err) {
+		pr_err("%s: %s: UHS2 SET_CONFIG fail!\n", mmc_hostname(host), __func__);
+		return err;
+	}
+
 	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)
+static int sd_uhs2_go_dormant(struct mmc_host *host, u32 node_id)
+{
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	int err;
+
+	/* Disable Normal INT */
+	err = host->ops->uhs2_control(host, UHS2_DISABLE_INT);
+	if (err) {
+		pr_err("%s: %s: UHS2 DISABLE_INT fail!\n",
+		       mmc_hostname(host), __func__);
+		return err;
+	}
+
+	/*
+	 * Refer to UHS-II Addendum Version 1.02 Figure 6-17 to see GO_DORMANT_STATE CCMD format.
+	 * Header:
+	 *      - Control Write(R/W=1) with 4-Byte payload(PLEN=01b).
+	 *      - IOADR = CMD_BASE + 001h
+	 * Payload:
+	 *      - bit [7]: HBR(Entry to Hibernate Mode)
+	 *                 1: Host intends to enter Hibernate mode during Dormant state.
+	 *                 The default setting is 0 because hibernate is currently not supported.
+	 */
+	uhs2_cmd.header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
+	uhs2_cmd.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);
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, UHS2_GO_DORMANT_PAYLOAD_LEN, 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;
+	}
+
+	/* Check Dormant State in Present */
+	err = host->ops->uhs2_control(host, UHS2_CHECK_DORMANT);
+	if (err)
+		return err;
+
+	/* Disable UHS2 card clock */
+	err = host->ops->uhs2_control(host, UHS2_DISABLE_CLK);
+	if (err)
+		return err;
+
+	/* Restore sd clock */
+	mmc_delay(5);
+	err = host->ops->uhs2_control(host, UHS2_ENABLE_CLK);
+	if (err)
+		return err;
+
+	/* Enable Normal INT */
+	err = host->ops->uhs2_control(host, UHS2_ENABLE_INT);
+	if (err)
+		return err;
+
+	/* Detect UHS2 */
+	err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int sd_uhs2_wait_active_state_cb(void *cb_data, bool *busy)
+{
+	struct sd_uhs2_wait_active_state_data *data = cb_data;
+	struct mmc_host *host = data->host;
+	struct mmc_command *cmd = data->cmd;
+	int err;
+
+	err = mmc_wait_for_cmd(host, cmd, 0);
+	if (err)
+		return err;
+
+	if (cmd->resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE)
+		*busy = false;
+	else
+		*busy = true;
+
+	return 0;
+}
+
+static int sd_uhs2_go_dormant_state(struct mmc_host *host, u32 node_id)
 {
+	struct mmc_command cmd = {0};
+	struct uhs2_command uhs2_cmd = {};
+	int err;
+	struct sd_uhs2_wait_active_state_data cb_data = {
+		.host = host,
+		.cmd = &cmd
+	};
+
+	err = sd_uhs2_go_dormant(host, node_id);
+	if (err) {
+		pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n",
+		       mmc_hostname(host), __func__, err);
+		return err;
+	}
+
+	/*
+	 * Use Control Read CCMD to check Config Completion(bit 63) in Generic Setting Register.
+	 * - Control Read(R/W=0) with 8-Byte payload(PLEN=10b).
+	 * - IOADR = Generic Setting Register(CFG_BASE + 008h)
+	 *
+	 * When UHS-II card been switched to new speed mode, it will set Config Completion to 1.
+	 */
+	uhs2_cmd.header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id;
+	uhs2_cmd.arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) |
+			UHS2_NATIVE_CMD_READ |
+			UHS2_NATIVE_CMD_PLEN_8B |
+			(UHS2_DEV_CONFIG_GEN_SET >> 8);
+
+	sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, 0, 0);
+	err = __mmc_poll_for_busy(host, UHS2_WAIT_CFG_COMPLETE_PERIOD_US,
+				  UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS,
+				  &sd_uhs2_wait_active_state_cb, &cb_data);
+	if (err) {
+		pr_err("%s: %s: Not switch to Active in 100 ms\n", mmc_hostname(host), __func__);
+		return err;
+	}
+
 	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.
  */
-static int sd_uhs2_init_card(struct mmc_host *host)
+static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
 {
 	struct mmc_card *card;
 	u32 node_id;
@@ -131,9 +818,14 @@ static int sd_uhs2_init_card(struct mmc_host *host)
 	if (err)
 		return err;
 
-	card = mmc_alloc_card(host, &sd_type);
-	if (IS_ERR(card))
-		return PTR_ERR(card);
+	if (oldcard) {
+		card = oldcard;
+	} else {
+		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;
@@ -146,18 +838,189 @@ static int sd_uhs2_init_card(struct mmc_host *host)
 	if (err)
 		goto err;
 
-	host->card = card;
+	/* If change speed to Range B, need to GO_DORMANT_STATE */
+	if (host->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
+	    host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD) {
+		err = sd_uhs2_go_dormant_state(host, node_id);
+		if (err)
+			goto err;
+	}
+
+	host->uhs2_sd_tran = true;
+
 	return 0;
 
 err:
-	mmc_remove_card(card);
+	if (!oldcard)
+		mmc_remove_card(card);
+	else
+		sd_uhs2_remove(host);
 	return err;
 }
 
-static void sd_uhs2_remove(struct mmc_host *host)
+/*
+ * 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)
 {
-	mmc_remove_card(host->card);
-	host->card = NULL;
+	int err;
+	u32 cid[4];
+	u32 ocr;
+	u32 rocr;
+	u8  *status;
+	int ro;
+
+	/* Send CMD0 to reset SD card */
+	err = __mmc_go_idle(host);
+	if (err)
+		return err;
+
+	mmc_delay(1);
+
+	/* Send CMD8 to communicate SD interface operation condition */
+	err = mmc_send_if_cond(host, host->ocr_avail);
+	if (err) {
+		dev_warn(mmc_dev(host), "CMD8 error\n");
+		goto err;
+	}
+
+	/*
+	 * Probe SD card working voltage.
+	 */
+	err = mmc_send_app_op_cond(host, 0, &ocr);
+	if (err)
+		goto 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)
+		rocr = host->ocr_avail;
+
+	rocr |= (SD_OCR_CCS | SD_OCR_XPC);
+
+	/* Wait SD power on ready */
+	ocr = rocr;
+
+	err = mmc_send_app_op_cond(host, ocr, &rocr);
+	if (err)
+		goto err;
+
+	err = mmc_send_cid(host, cid);
+	if (err)
+		goto err;
+
+	memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+	mmc_decode_cid(card);
+
+	/*
+	 * For native busses:  get card RCA and quit open drain mode.
+	 */
+	err = mmc_send_relative_addr(host, &card->rca);
+	if (err)
+		goto err;
+
+	err = mmc_sd_get_csd(card);
+	if (err)
+		goto err;
+
+	/*
+	 * Select card, as all following commands rely on that.
+	 */
+	err = mmc_select_card(card);
+	if (err)
+		goto err;
+
+	/*
+	 * Fetch SCR from card.
+	 */
+	err = mmc_app_send_scr(card);
+	if (err)
+		goto err;
+
+	err = mmc_decode_scr(card);
+	if (err)
+		goto err;
+
+	/*
+	 * Switch to high power consumption mode.
+	 * Even switch failed, sd card can still work at lower power consumption mode, but
+	 * performance will be lower than high power consumption mode.
+	 */
+	status = kmalloc(64, GFP_KERNEL);
+	if (!status)
+		return -ENOMEM;
+
+	if (!(card->csd.cmdclass & CCC_SWITCH)) {
+		pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
+			mmc_hostname(card->host));
+	} else {
+		/* send CMD6 to set Maximum Power Consumption to get better performance */
+		err = mmc_sd_switch(card, 0, 3, SD4_SET_POWER_LIMIT_1_80W, status);
+		if (!err)
+			err = mmc_sd_switch(card, 1, 3, SD4_SET_POWER_LIMIT_1_80W, status);
+
+		err = 0;
+	}
+
+	/*
+	 * Check if read-only switch is active.
+	 */
+	ro = mmc_sd_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);
+	}
+
+	/*
+	 * NOTE:
+	 * Should we read Externsion Register to check power notification feature here?
+	 */
+
+	kfree(status);
+
+	return 0;
+
+err:
+	sd_uhs2_remove(host);
+	return err;
+}
+
+static int sd_uhs2_reinit(struct mmc_host *host)
+{
+	struct mmc_card *card = host->card;
+	int err;
+
+	sd_uhs2_power_up(host);
+	err = sd_uhs2_phy_init(host);
+	if (err)
+		return err;
+
+	err = sd_uhs2_init_card(host, card);
+	if (err)
+		return err;
+
+	err = sd_uhs2_legacy_init(host, card);
+	if (err)
+		return err;
+
+	mmc_card_set_present(card);
+	return err;
 }
 
 static int sd_uhs2_alive(struct mmc_host *host)
@@ -179,38 +1042,194 @@ static void sd_uhs2_detect(struct mmc_host *host)
 		mmc_claim_host(host);
 		mmc_detach_bus(host);
 		sd_uhs2_power_off(host);
+		/* Remove UHS2 timing to indicate the end of UHS2 mode */
+		host->ios.timing = MMC_TIMING_LEGACY;
 		mmc_release_host(host);
 	}
 }
 
+static int _sd_uhs2_suspend(struct mmc_host *host)
+{
+	struct mmc_card *card = host->card;
+	int err = 0;
+
+	mmc_claim_host(host);
+
+	if (mmc_card_suspended(card))
+		goto out;
+
+	if (mmc_sd_can_poweroff_notify(card))
+		err = sd_poweroff_notify(card);
+
+	if (!err) {
+		sd_uhs2_power_off(host);
+		mmc_card_set_suspended(card);
+	}
+
+out:
+	mmc_release_host(host);
+	return err;
+}
+
+/*
+ * Callback for suspend
+ */
 static int sd_uhs2_suspend(struct mmc_host *host)
 {
-	return 0;
+	int err;
+
+	err = _sd_uhs2_suspend(host);
+	if (!err) {
+		pm_runtime_disable(&host->card->dev);
+		pm_runtime_set_suspended(&host->card->dev);
+	}
+
+	return err;
+}
+
+/*
+ * This function tries to determine if the same card is still present
+ * and, if so, restore all state to it.
+ */
+static int _mmc_sd_uhs2_resume(struct mmc_host *host)
+{
+	int err = 0;
+
+	mmc_claim_host(host);
+
+	if (!mmc_card_suspended(host->card))
+		goto out;
+
+	/* Power up UHS2 SD card and re-initialize it. */
+	err = sd_uhs2_reinit(host);
+	mmc_card_clr_suspended(host->card);
+
+out:
+	mmc_release_host(host);
+	return err;
 }
 
+/*
+ * Callback for resume
+ */
 static int sd_uhs2_resume(struct mmc_host *host)
 {
+	pm_runtime_enable(&host->card->dev);
 	return 0;
 }
 
+/*
+ * Callback for runtime_suspend.
+ */
 static int sd_uhs2_runtime_suspend(struct mmc_host *host)
 {
-	return 0;
+	int err;
+
+	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+		return 0;
+
+	err = _sd_uhs2_suspend(host);
+	if (err)
+		pr_err("%s: error %d doing aggressive suspend\n", mmc_hostname(host), err);
+
+	return err;
 }
 
 static int sd_uhs2_runtime_resume(struct mmc_host *host)
 {
-	return 0;
+	int err;
+
+	err = _mmc_sd_uhs2_resume(host);
+	if (err && err != -ENOMEDIUM)
+		pr_err("%s: error %d doing runtime resume\n", mmc_hostname(host), err);
+
+	return err;
 }
 
-static int sd_uhs2_shutdown(struct mmc_host *host)
+static int sd_uhs2_hw_reset(struct mmc_host *host)
 {
-	return 0;
+	int err;
+
+	sd_uhs2_power_off(host);
+	/* Wait at least 1 ms according to SD spec */
+	mmc_delay(1);
+	sd_uhs2_power_up(host);
+
+	err = sd_uhs2_reinit(host);
+
+	return err;
 }
 
-static int sd_uhs2_hw_reset(struct mmc_host *host)
+/*
+ * mmc_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
+ */
+void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
 {
-	return 0;
+	struct mmc_command *cmd;
+	struct uhs2_command *uhs2_cmd;
+	u8 plen;
+
+	cmd = mrq->cmd;
+	cmd->uhs2_cmd = &mrq->uhs2_cmd;
+	uhs2_cmd = cmd->uhs2_cmd;
+	uhs2_cmd->header = host->card->uhs2_config.node_id;
+	if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
+		uhs2_cmd->header |= UHS2_PACKET_TYPE_DCMD;
+	else
+		uhs2_cmd->header |= UHS2_PACKET_TYPE_CCMD;
+
+	uhs2_cmd->arg = cmd->opcode << UHS2_SD_CMD_INDEX_POS;
+	if (host->uhs2_app_cmd) {
+		uhs2_cmd->arg |= UHS2_SD_CMD_APP;
+		host->uhs2_app_cmd = false;
+	}
+
+	/*
+	 * UHS-II Addendum 7.2.1.2
+	 * Host may set DM to 1 for DCMD which supports multi-block read/write regardless of
+	 * data transfer length (e.g., CMD18, CMD25). Otherwise, it shall not set DM to 1.
+	 * (e.g., CMD6, CMD17, CMD24). These rules are also applied to other multi-block read/write
+	 * commands defined in other Part of SD specifications (for example, Host may set DM to 1
+	 * for ACMD18 or ACMD25).
+	 */
+	if (mmc_op_multi(cmd->opcode))
+		cmd->uhs2_cmd->tmode_half_duplex = mmc_card_uhs2_hd_mode(host);
+	else
+		cmd->uhs2_cmd->tmode_half_duplex = 0;
+
+	uhs2_cmd = cmd->uhs2_cmd;
+	plen = 2; /* at the maximum */
+
+	if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC &&
+	    cmd->uhs2_cmd->tmode_half_duplex) {
+		if (mmc_card_uhs2_hd_mode(host))
+			uhs2_cmd->arg |= UHS2_DCMD_2L_HD_MODE;
+
+		uhs2_cmd->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) {
+			uhs2_cmd->arg |= UHS2_DCMD_TLUM_BYTE_MODE;
+			uhs2_cmd->payload[1] = cpu_to_be32(cmd->data->blksz);
+		} else {
+			uhs2_cmd->payload[1] = cpu_to_be32(cmd->data->blocks);
+		}
+	} else {
+		plen = 1;
+	}
+
+	uhs2_cmd->payload[0] = cpu_to_be32(cmd->arg);
+	sd_uhs2_cmd_assemble(cmd, uhs2_cmd, plen, 0);
 }
 
 static const struct mmc_bus_ops sd_uhs2_ops = {
@@ -221,7 +1240,7 @@ static const struct mmc_bus_ops sd_uhs2_ops = {
 	.resume = sd_uhs2_resume,
 	.runtime_suspend = sd_uhs2_runtime_suspend,
 	.runtime_resume = sd_uhs2_runtime_resume,
-	.shutdown = sd_uhs2_shutdown,
+	.shutdown = sd_uhs2_suspend,
 	.hw_reset = sd_uhs2_hw_reset,
 };
 
@@ -237,7 +1256,7 @@ static int sd_uhs2_attach(struct mmc_host *host)
 	if (err)
 		goto err;
 
-	err = sd_uhs2_init_card(host);
+	err = sd_uhs2_init_card(host, NULL);
 	if (err)
 		goto err;
 
@@ -254,21 +1273,33 @@ static int sd_uhs2_attach(struct mmc_host *host)
 		goto remove_card;
 
 	mmc_claim_host(host);
+
 	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);
+	/* Remove UHS2 timing to indicate the end of UHS2 mode */
+	host->ios.timing = MMC_TIMING_LEGACY;
 	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;
+	int i, err;
 
 	if (!(host->caps2 & MMC_CAP2_SD_UHS2))
 		return -EOPNOTSUPP;
@@ -283,6 +1314,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_debug("%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;
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index f0ac2e469b32..291f89e12c65 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -11,6 +11,20 @@
 struct mmc_data;
 struct mmc_request;
 
+#define UHS2_MAX_PAYLOAD_LEN 2
+#define UHS2_MAX_RESP_LEN 20
+
+struct uhs2_command {
+	u16	header;
+	u16	arg;
+	__be32	payload[UHS2_MAX_PAYLOAD_LEN];
+	u8	payload_len;
+	u8	packet_len;
+	u8	tmode_half_duplex;
+	u8	uhs2_resp[UHS2_MAX_RESP_LEN];	/* UHS2 native cmd resp */
+	u8	uhs2_resp_len;			/* UHS2 native cmd resp len */
+};
+
 struct mmc_command {
 	u32			opcode;
 	u32			arg;
@@ -96,6 +110,8 @@ 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 */
 };
 
 struct mmc_data {
@@ -154,6 +170,7 @@ struct mmc_request {
 	const struct bio_crypt_ctx *crypto_ctx;
 	int			crypto_key_slot;
 #endif
+	struct uhs2_command	uhs2_cmd;
 };
 
 struct mmc_card;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0e1112ee160e..4b9e23f2afee 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -126,6 +126,13 @@ struct sd_uhs2_caps {
 };
 
 enum sd_uhs2_operation {
+	UHS2_PHY_INIT = 0,
+	UHS2_SET_CONFIG,
+	UHS2_ENABLE_INT,
+	UHS2_DISABLE_INT,
+	UHS2_ENABLE_CLK,
+	UHS2_DISABLE_CLK,
+	UHS2_CHECK_DORMANT,
 	UHS2_SET_IOS,
 };
 
@@ -452,6 +459,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 */
 
+	bool			uhs2_sd_tran;
+	bool			uhs2_app_cmd;	/* Host UHS-II APP Command */
 	struct sd_uhs2_caps	uhs2_caps;	/* Host UHS-II capabilities */
 
 	int			fixed_drv_type;	/* fixed driver type for non-removable media */
@@ -713,6 +722,12 @@ static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host,
 	host->err_stats[stat] += 1;
 }
 
+static inline int mmc_card_uhs2_hd_mode(struct mmc_host *host)
+{
+	return host->ios.timing == MMC_TIMING_UHS2_SPEED_A_HD ||
+	       host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD;
+}
+
 int mmc_sd_switch(struct mmc_card *card, bool mode, int group,
 		u8 value, u8 *resp);
 int mmc_send_status(struct mmc_card *card, u32 *status);
-- 
2.25.1
Re: [PATCH V22 07/22] mmc: core: Support UHS-II card control and access
Posted by Ulf Hansson 1 month, 3 weeks ago
On Fri, 13 Sept 2024 at 12:29, Victor Shih <victorshihgli@gmail.com> wrote:
>
> From: Victor Shih <victor.shih@genesyslogic.com.tw>
>
> Embed UHS-II access/control functionality into the MMC request
> processing flow.
>
> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
> ---
>
> Updates in V16:
>  - Separate the Error Recovery mechanism into a separate patch#8.
>
> Updates in V15:
>  - Move struct uhs2_command uhs2_cmd to struct mmc_request and
>    modify whatever other changers to make it work.
>  - Refer the SD Host Controller Standard Specification Section 3.10
>    to add Error Recovery mechanism to recover the command error.
>
> Updates in V13:
>  - Separate __mmc_go_idle() into one patch for re-factorring the code.
>  - Move mmc_decode_scr declaration to sd.h.
>  - Ues uhs2_sd_tran to stead MMC_UHS2_SD_TRAN.
>  - Drop unnecessary comment.
>
> Updates in V12:
>  - Use mmc_op_multi() to check DCMD which supports multi read/write
>    in mmc_uhs2_prepare_cmd().
>
> Updates in V10:
>  - Move some definitions of PatchV9[02/23] to PatchV10[06/23].
>  - Move some definitions of PatchV9[05/23] to PatchV10[06/23].
>  - Drop do_multi in the mmc_blk_rw_rq_prep().
>  - Use tmode_half_duplex to instead of uhs2_tmode0_flag.
>  - Move entire control of the tmode into mmc_uhs2_prepare_cmd().
>
> Updates in V8:
>  - Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
>  - Modify return value in sd_uhs2_attach().
>
> Updates in V7:
>  - Add mmc_uhs2_card_prepare_cmd helper function in sd_ops.h.
>  - Drop uhs2_state in favor of ios->timing.
>  - Remove unnecessary functions.
>
> ---
>
>  drivers/mmc/core/core.c    |    6 +-
>  drivers/mmc/core/sd.c      |   10 +-
>  drivers/mmc/core/sd.h      |    5 +
>  drivers/mmc/core/sd_ops.c  |    9 +
>  drivers/mmc/core/sd_ops.h  |   13 +
>  drivers/mmc/core/sd_uhs2.c | 1100 ++++++++++++++++++++++++++++++++++--
>  include/linux/mmc/core.h   |   17 +
>  include/linux/mmc/host.h   |   15 +
>  8 files changed, 1136 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 8b8aff131f8b..1841a2190571 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -351,6 +351,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
>         if (err)
>                 return err;
>
> +       mmc_uhs2_card_prepare_cmd(host, mrq);
> +
>         led_trigger_event(host->led, LED_FULL);
>         __mmc_start_request(host, mrq);
>
> @@ -450,6 +452,8 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
>         if (err)
>                 goto out_err;
>
> +       mmc_uhs2_card_prepare_cmd(host, mrq);
> +
>         err = host->cqe_ops->cqe_request(host, mrq);
>         if (err)
>                 goto out_err;
> @@ -1132,7 +1136,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
>                 return 0;
>         }
>
> -       if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> +       if (!mmc_card_uhs2(host) && host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
>                 bit = ffs(ocr) - 1;
>                 ocr &= 3 << bit;
>                 mmc_power_cycle(host, ocr);
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 12fe282bea77..d5576a2f4435 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -194,7 +194,7 @@ static int mmc_decode_csd(struct mmc_card *card)
>  /*
>   * Given a 64-bit response, decode to our card SCR structure.
>   */
> -static int mmc_decode_scr(struct mmc_card *card)
> +int mmc_decode_scr(struct mmc_card *card)
>  {
>         struct sd_scr *scr = &card->scr;
>         unsigned int scr_struct;
> @@ -894,7 +894,7 @@ int mmc_sd_get_csd(struct mmc_card *card)
>         return 0;
>  }
>
> -static int mmc_sd_get_ro(struct mmc_host *host)
> +int mmc_sd_get_ro(struct mmc_host *host)
>  {
>         int ro;
>
> @@ -1623,7 +1623,7 @@ static void mmc_sd_detect(struct mmc_host *host)
>         }
>  }
>
> -static int sd_can_poweroff_notify(struct mmc_card *card)
> +int sd_can_poweroff_notify(struct mmc_card *card)
>  {
>         return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
>  }
> @@ -1651,7 +1651,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
>         return 0;
>  }
>
> -static int sd_poweroff_notify(struct mmc_card *card)
> +int sd_poweroff_notify(struct mmc_card *card)
>  {
>         struct sd_busy_data cb_data;
>         u8 *reg_buf;
> @@ -1699,7 +1699,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
>         if (mmc_card_suspended(card))
>                 goto out;
>
> -       if (sd_can_poweroff_notify(card))
> +       if (mmc_sd_can_poweroff_notify(card))

There is no need to change this. Please stick with sd_can_poweroff_notify().

>                 err = sd_poweroff_notify(card);
>         else if (!mmc_host_is_spi(host))
>                 err = mmc_deselect_cards(host);
> diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
> index fe6dd46927a4..49afaef269cd 100644
> --- a/drivers/mmc/core/sd.h
> +++ b/drivers/mmc/core/sd.h
> @@ -11,10 +11,15 @@ struct mmc_card;
>
>  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
>  int mmc_sd_get_csd(struct mmc_card *card);
> +int mmc_decode_scr(struct mmc_card *card);
> +int mmc_sd_get_ro(struct mmc_host *host);
>  void mmc_decode_cid(struct mmc_card *card);
>  int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
>         bool reinit);
>  unsigned mmc_sd_get_max_clock(struct mmc_card *card);
>  int mmc_sd_switch_hs(struct mmc_card *card);
>
> +int sd_can_poweroff_notify(struct mmc_card *card);
> +int sd_poweroff_notify(struct mmc_card *card);
> +
>  #endif
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index f93c392040ae..5cd1216edf0d 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -41,6 +41,15 @@ int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
>         if (WARN_ON(card && card->host != host))
>                 return -EINVAL;
>
> +       /*
> +        * UHS2 packet has APP bit so only set APP_CMD flag here.
> +        * Will set the APP bit when assembling UHS2 packet.
> +        */
> +       if (host->uhs2_sd_tran) {
> +               host->uhs2_app_cmd = true;
> +               return 0;
> +       }
> +
>         cmd.opcode = MMC_APP_CMD;
>
>         if (card) {
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index 7667fc223b74..e3af68a52de8 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -11,6 +11,7 @@
>  #include <linux/types.h>
>
>  struct mmc_card;
> +struct mmc_command;
>  struct mmc_host;
>
>  int mmc_app_set_bus_width(struct mmc_card *card, int width);
> @@ -21,6 +22,18 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
>  int mmc_app_send_scr(struct mmc_card *card);
>  int mmc_app_sd_status(struct mmc_card *card, void *ssr);
>  int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
> +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
> +
> +static inline void mmc_uhs2_card_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
> +{
> +       if (host->uhs2_sd_tran)
> +               mmc_uhs2_prepare_cmd(host, mrq);
> +}

Please remove this inline function and just check "host->uhs2_sd_tran"
in mmc_start_request() and mmc_cqe_start_req(). If "uhs2_sd_tran" is
set, then just call mmc_uhs2_prepare_cmd() directly.

> +
> +static inline int mmc_sd_can_poweroff_notify(struct mmc_card *card)
> +{
> +       return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> +}

This function is a copy of sd_can_poweroff_notify(). Please use
sd_can_poweroff_notify() and remove mmc_sd_can_poweroff_notify().

>
>  #endif
>
> diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> index beb2541338ff..85939a2582dc 100644
> --- a/drivers/mmc/core/sd_uhs2.c
> +++ b/drivers/mmc/core/sd_uhs2.c
> @@ -1,23 +1,51 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  /*
>   * Copyright (C) 2021 Linaro Ltd
> - *
>   * Author: Ulf Hansson <ulf.hansson@linaro.org>
>   *
> + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> + * Author: Yi Sun <yi.y.sun@intel.com>
> + *
> + * Copyright (C) 2020 Genesys Logic, Inc.
> + * Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> + *
> + * Copyright (C) 2020 Linaro Limited
> + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> + *
> + * Copyright (C) 2022 Genesys Logic, Inc.
> + * Authors: Jason Lai <jason.lai@genesyslogic.com.tw>
> + *
> + * Copyright (C) 2023 Genesys Logic, Inc.
> + * Authors: Victor Shih <victor.shih@genesyslogic.com.tw>
> + *
>   * Support for SD UHS-II cards
>   */
>  #include <linux/err.h>
> +#include <linux/pm_runtime.h>
>
>  #include <linux/mmc/host.h>
>  #include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/mmc/sd_uhs2.h>
>
> +#include "card.h"
>  #include "core.h"
>  #include "bus.h"
>  #include "sd.h"
> +#include "sd_ops.h"
>  #include "mmc_ops.h"
>
> +#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US  (1 * 1000)
> +#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100
> +
>  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
>
> +struct sd_uhs2_wait_active_state_data {
> +       struct mmc_host *host;
> +       struct mmc_command *cmd;
> +};
> +
>  static int sd_uhs2_power_up(struct mmc_host *host)
>  {
>         int err;
> @@ -42,12 +70,46 @@ static int sd_uhs2_power_off(struct mmc_host *host)
>
>         host->ios.vdd = 0;
>         host->ios.clock = 0;
> -       host->ios.timing = MMC_TIMING_LEGACY;
> +       /* Must set UHS2 timing to identify UHS2 mode */
> +       host->ios.timing = MMC_TIMING_UHS2_SPEED_A;

The only code that seems to check for MMC_TIMING_UHS2_SPEED_A is
__sdhci_uhs2_set_ios() (that is added later in the series). Moreover
that code only cares about MMC_TIMING_UHS2_SPEED_A if "ios->power_mode
!= MMC_POWER_OFF".

In other words, I think it should be okay to keep MMC_TIMING_LEGACY here, right?

>         host->ios.power_mode = MMC_POWER_OFF;
> +       host->uhs2_sd_tran = false;
>
>         return host->ops->uhs2_control(host, UHS2_SET_IOS);
>  }
>

[...]

>  /*
>   * 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)
> +static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
>  {
>         struct mmc_card *card;
>         u32 node_id;
> @@ -131,9 +818,14 @@ static int sd_uhs2_init_card(struct mmc_host *host)
>         if (err)
>                 return err;
>
> -       card = mmc_alloc_card(host, &sd_type);
> -       if (IS_ERR(card))
> -               return PTR_ERR(card);
> +       if (oldcard) {
> +               card = oldcard;
> +       } else {
> +               card = mmc_alloc_card(host, &sd_type);
> +               if (IS_ERR(card))
> +                       return PTR_ERR(card);
> +       }
> +       host->card = card;

Why do you need to assign the host->card at this point, isn't it okay
to do that later where it was originally?

Anyway, there is a more important comment a few lines below that also
relates to this.

>
>         card->uhs2_config.node_id = node_id;
>         card->type = MMC_TYPE_SD;
> @@ -146,18 +838,189 @@ static int sd_uhs2_init_card(struct mmc_host *host)
>         if (err)
>                 goto err;
>
> -       host->card = card;
> +       /* If change speed to Range B, need to GO_DORMANT_STATE */
> +       if (host->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
> +           host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD) {
> +               err = sd_uhs2_go_dormant_state(host, node_id);
> +               if (err)
> +                       goto err;
> +       }
> +
> +       host->uhs2_sd_tran = true;
> +
>         return 0;
>
>  err:
> -       mmc_remove_card(card);
> +       if (!oldcard)
> +               mmc_remove_card(card);
> +       else
> +               sd_uhs2_remove(host);

This doesn't work. If there is an "oldcard", that's because we are
trying to re-initialize the card during a resume. If we fail to
re-initialize it we must not remove the oldcard in this path, but
rather just return/propagate an error code.

Instead, the oldcard should be removed from the mmc_rescan() work when
it realizes that the call to "host->bus_ops->detect(host)" fails (and
it must fail then).

>         return err;
>  }
>
> -static void sd_uhs2_remove(struct mmc_host *host)
> +/*
> + * 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)
>  {
> -       mmc_remove_card(host->card);
> -       host->card = NULL;
> +       int err;
> +       u32 cid[4];
> +       u32 ocr;
> +       u32 rocr;
> +       u8  *status;
> +       int ro;
> +
> +       /* Send CMD0 to reset SD card */
> +       err = __mmc_go_idle(host);
> +       if (err)
> +               return err;
> +
> +       mmc_delay(1);
> +
> +       /* Send CMD8 to communicate SD interface operation condition */
> +       err = mmc_send_if_cond(host, host->ocr_avail);
> +       if (err) {
> +               dev_warn(mmc_dev(host), "CMD8 error\n");
> +               goto err;
> +       }
> +
> +       /*
> +        * Probe SD card working voltage.
> +        */
> +       err = mmc_send_app_op_cond(host, 0, &ocr);
> +       if (err)
> +               goto 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)
> +               rocr = host->ocr_avail;
> +
> +       rocr |= (SD_OCR_CCS | SD_OCR_XPC);
> +
> +       /* Wait SD power on ready */
> +       ocr = rocr;
> +
> +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> +       if (err)
> +               goto err;
> +
> +       err = mmc_send_cid(host, cid);
> +       if (err)
> +               goto err;
> +
> +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> +       mmc_decode_cid(card);
> +
> +       /*
> +        * For native busses:  get card RCA and quit open drain mode.
> +        */
> +       err = mmc_send_relative_addr(host, &card->rca);
> +       if (err)
> +               goto err;
> +
> +       err = mmc_sd_get_csd(card);
> +       if (err)
> +               goto err;
> +
> +       /*
> +        * Select card, as all following commands rely on that.
> +        */
> +       err = mmc_select_card(card);
> +       if (err)
> +               goto err;
> +
> +       /*
> +        * Fetch SCR from card.
> +        */
> +       err = mmc_app_send_scr(card);
> +       if (err)
> +               goto err;
> +
> +       err = mmc_decode_scr(card);
> +       if (err)
> +               goto err;
> +
> +       /*
> +        * Switch to high power consumption mode.
> +        * Even switch failed, sd card can still work at lower power consumption mode, but
> +        * performance will be lower than high power consumption mode.
> +        */
> +       status = kmalloc(64, GFP_KERNEL);
> +       if (!status)
> +               return -ENOMEM;
> +
> +       if (!(card->csd.cmdclass & CCC_SWITCH)) {
> +               pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
> +                       mmc_hostname(card->host));
> +       } else {
> +               /* send CMD6 to set Maximum Power Consumption to get better performance */
> +               err = mmc_sd_switch(card, 0, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> +               if (!err)
> +                       err = mmc_sd_switch(card, 1, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> +
> +               err = 0;
> +       }
> +
> +       /*
> +        * Check if read-only switch is active.
> +        */
> +       ro = mmc_sd_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);
> +       }

Some of the above commands in sd_uhs2_legacy_init() are only needed
during the first initialization of the card. When re-initializing a
card during a resume, we can skip several of these steps as they are
simply unnecessary.

However, to validate that it's the same card that remains inserted
during re-initialization, we need to read the CID register and compare
it against the CID register that we have stored earlier in
card->raw_cid. Please have a look at mmc_sd_init_card() to see how
this works for legacy SD cards.

> +
> +       /*
> +        * NOTE:
> +        * Should we read Externsion Register to check power notification feature here?
> +        */

Yes, this is needed too.

> +
> +       kfree(status);
> +
> +       return 0;
> +
> +err:
> +       sd_uhs2_remove(host);

Again, this doesn't work if we are re-initializing the card during a
resume. The card must only be removed if this is the first
initialization of the card - otherwise the removal needs to be managed
by the mmc_rescan() work.

> +       return err;
> +}
> +
> +static int sd_uhs2_reinit(struct mmc_host *host)
> +{
> +       struct mmc_card *card = host->card;
> +       int err;
> +
> +       sd_uhs2_power_up(host);
> +       err = sd_uhs2_phy_init(host);
> +       if (err)
> +               return err;
> +
> +       err = sd_uhs2_init_card(host, card);
> +       if (err)
> +               return err;
> +
> +       err = sd_uhs2_legacy_init(host, card);
> +       if (err)
> +               return err;
> +
> +       mmc_card_set_present(card);

Drop this, it's already managed when calling mmc_add_card() and that
should be sufficient.

> +       return err;
>  }
>
>  static int sd_uhs2_alive(struct mmc_host *host)
> @@ -179,38 +1042,194 @@ static void sd_uhs2_detect(struct mmc_host *host)
>                 mmc_claim_host(host);
>                 mmc_detach_bus(host);
>                 sd_uhs2_power_off(host);
> +               /* Remove UHS2 timing to indicate the end of UHS2 mode */
> +               host->ios.timing = MMC_TIMING_LEGACY;

If my earlier comment for sd_uhs2_power_off() was correct, this part
should be dropped too.

>                 mmc_release_host(host);
>         }
>  }
>
> +static int _sd_uhs2_suspend(struct mmc_host *host)
> +{
> +       struct mmc_card *card = host->card;
> +       int err = 0;
> +
> +       mmc_claim_host(host);
> +
> +       if (mmc_card_suspended(card))
> +               goto out;
> +
> +       if (mmc_sd_can_poweroff_notify(card))

Use sd_can_poweroff_notify() instead.

> +               err = sd_poweroff_notify(card);
> +
> +       if (!err) {
> +               sd_uhs2_power_off(host);
> +               mmc_card_set_suspended(card);
> +       }
> +
> +out:
> +       mmc_release_host(host);
> +       return err;
> +}
> +

[...]

>
> @@ -237,7 +1256,7 @@ static int sd_uhs2_attach(struct mmc_host *host)
>         if (err)
>                 goto err;
>
> -       err = sd_uhs2_init_card(host);
> +       err = sd_uhs2_init_card(host, NULL);
>         if (err)
>                 goto err;
>
> @@ -254,21 +1273,33 @@ static int sd_uhs2_attach(struct mmc_host *host)
>                 goto remove_card;
>
>         mmc_claim_host(host);
> +
>         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);
> +       /* Remove UHS2 timing to indicate the end of UHS2 mode */
> +       host->ios.timing = MMC_TIMING_LEGACY;

Again, see my earlier comment for sd_uhs2_power_off(). Can this part be dropped?

>         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;
> +       int i, err;
>
>         if (!(host->caps2 & MMC_CAP2_SD_UHS2))
>                 return -EOPNOTSUPP;
> @@ -283,6 +1314,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_debug("%s: %s: trying to init UHS-II card at %u Hz\n",
> +                        mmc_hostname(host), __func__, host->f_init);
> +

This looks like it belongs in patch 2 instead. I have amended patch 2
with this before I applied it.

>                 err = sd_uhs2_attach(host);
>                 if (!err)
>                         break;

[...]

Other than the above comments, I just wanted to highlight that I have
just queued up a series from Avri that adds support for SD Ultra
Capacity (SDUC) cards.

There are some adjustments needed for your series for UHS-II to cope
with SDUC, but I think those should be quite minor.

Kind regards
Uffe
Re: [PATCH V22 07/22] mmc: core: Support UHS-II card control and access
Posted by Victor Shih 1 month, 1 week ago
On Mon, Oct 7, 2024 at 11:36 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Fri, 13 Sept 2024 at 12:29, Victor Shih <victorshihgli@gmail.com> wrote:
> >
> > From: Victor Shih <victor.shih@genesyslogic.com.tw>
> >
> > Embed UHS-II access/control functionality into the MMC request
> > processing flow.
> >
> > Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
> > ---
> >
> > Updates in V16:
> >  - Separate the Error Recovery mechanism into a separate patch#8.
> >
> > Updates in V15:
> >  - Move struct uhs2_command uhs2_cmd to struct mmc_request and
> >    modify whatever other changers to make it work.
> >  - Refer the SD Host Controller Standard Specification Section 3.10
> >    to add Error Recovery mechanism to recover the command error.
> >
> > Updates in V13:
> >  - Separate __mmc_go_idle() into one patch for re-factorring the code.
> >  - Move mmc_decode_scr declaration to sd.h.
> >  - Ues uhs2_sd_tran to stead MMC_UHS2_SD_TRAN.
> >  - Drop unnecessary comment.
> >
> > Updates in V12:
> >  - Use mmc_op_multi() to check DCMD which supports multi read/write
> >    in mmc_uhs2_prepare_cmd().
> >
> > Updates in V10:
> >  - Move some definitions of PatchV9[02/23] to PatchV10[06/23].
> >  - Move some definitions of PatchV9[05/23] to PatchV10[06/23].
> >  - Drop do_multi in the mmc_blk_rw_rq_prep().
> >  - Use tmode_half_duplex to instead of uhs2_tmode0_flag.
> >  - Move entire control of the tmode into mmc_uhs2_prepare_cmd().
> >
> > Updates in V8:
> >  - Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
> >  - Modify return value in sd_uhs2_attach().
> >
> > Updates in V7:
> >  - Add mmc_uhs2_card_prepare_cmd helper function in sd_ops.h.
> >  - Drop uhs2_state in favor of ios->timing.
> >  - Remove unnecessary functions.
> >
> > ---
> >
> >  drivers/mmc/core/core.c    |    6 +-
> >  drivers/mmc/core/sd.c      |   10 +-
> >  drivers/mmc/core/sd.h      |    5 +
> >  drivers/mmc/core/sd_ops.c  |    9 +
> >  drivers/mmc/core/sd_ops.h  |   13 +
> >  drivers/mmc/core/sd_uhs2.c | 1100 ++++++++++++++++++++++++++++++++++--
> >  include/linux/mmc/core.h   |   17 +
> >  include/linux/mmc/host.h   |   15 +
> >  8 files changed, 1136 insertions(+), 39 deletions(-)
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index 8b8aff131f8b..1841a2190571 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -351,6 +351,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 return err;
> >
> > +       mmc_uhs2_card_prepare_cmd(host, mrq);
> > +
> >         led_trigger_event(host->led, LED_FULL);
> >         __mmc_start_request(host, mrq);
> >
> > @@ -450,6 +452,8 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 goto out_err;
> >
> > +       mmc_uhs2_card_prepare_cmd(host, mrq);
> > +
> >         err = host->cqe_ops->cqe_request(host, mrq);
> >         if (err)
> >                 goto out_err;
> > @@ -1132,7 +1136,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
> >                 return 0;
> >         }
> >
> > -       if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> > +       if (!mmc_card_uhs2(host) && host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> >                 bit = ffs(ocr) - 1;
> >                 ocr &= 3 << bit;
> >                 mmc_power_cycle(host, ocr);
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index 12fe282bea77..d5576a2f4435 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -194,7 +194,7 @@ static int mmc_decode_csd(struct mmc_card *card)
> >  /*
> >   * Given a 64-bit response, decode to our card SCR structure.
> >   */
> > -static int mmc_decode_scr(struct mmc_card *card)
> > +int mmc_decode_scr(struct mmc_card *card)
> >  {
> >         struct sd_scr *scr = &card->scr;
> >         unsigned int scr_struct;
> > @@ -894,7 +894,7 @@ int mmc_sd_get_csd(struct mmc_card *card)
> >         return 0;
> >  }
> >
> > -static int mmc_sd_get_ro(struct mmc_host *host)
> > +int mmc_sd_get_ro(struct mmc_host *host)
> >  {
> >         int ro;
> >
> > @@ -1623,7 +1623,7 @@ static void mmc_sd_detect(struct mmc_host *host)
> >         }
> >  }
> >
> > -static int sd_can_poweroff_notify(struct mmc_card *card)
> > +int sd_can_poweroff_notify(struct mmc_card *card)
> >  {
> >         return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> >  }
> > @@ -1651,7 +1651,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
> >         return 0;
> >  }
> >
> > -static int sd_poweroff_notify(struct mmc_card *card)
> > +int sd_poweroff_notify(struct mmc_card *card)
> >  {
> >         struct sd_busy_data cb_data;
> >         u8 *reg_buf;
> > @@ -1699,7 +1699,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
> >         if (mmc_card_suspended(card))
> >                 goto out;
> >
> > -       if (sd_can_poweroff_notify(card))
> > +       if (mmc_sd_can_poweroff_notify(card))
>
> There is no need to change this. Please stick with sd_can_poweroff_notify().
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> >                 err = sd_poweroff_notify(card);
> >         else if (!mmc_host_is_spi(host))
> >                 err = mmc_deselect_cards(host);
> > diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
> > index fe6dd46927a4..49afaef269cd 100644
> > --- a/drivers/mmc/core/sd.h
> > +++ b/drivers/mmc/core/sd.h
> > @@ -11,10 +11,15 @@ struct mmc_card;
> >
> >  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
> >  int mmc_sd_get_csd(struct mmc_card *card);
> > +int mmc_decode_scr(struct mmc_card *card);
> > +int mmc_sd_get_ro(struct mmc_host *host);
> >  void mmc_decode_cid(struct mmc_card *card);
> >  int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
> >         bool reinit);
> >  unsigned mmc_sd_get_max_clock(struct mmc_card *card);
> >  int mmc_sd_switch_hs(struct mmc_card *card);
> >
> > +int sd_can_poweroff_notify(struct mmc_card *card);
> > +int sd_poweroff_notify(struct mmc_card *card);
> > +
> >  #endif
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index f93c392040ae..5cd1216edf0d 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -41,6 +41,15 @@ int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
> >         if (WARN_ON(card && card->host != host))
> >                 return -EINVAL;
> >
> > +       /*
> > +        * UHS2 packet has APP bit so only set APP_CMD flag here.
> > +        * Will set the APP bit when assembling UHS2 packet.
> > +        */
> > +       if (host->uhs2_sd_tran) {
> > +               host->uhs2_app_cmd = true;
> > +               return 0;
> > +       }
> > +
> >         cmd.opcode = MMC_APP_CMD;
> >
> >         if (card) {
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index 7667fc223b74..e3af68a52de8 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -11,6 +11,7 @@
> >  #include <linux/types.h>
> >
> >  struct mmc_card;
> > +struct mmc_command;
> >  struct mmc_host;
> >
> >  int mmc_app_set_bus_width(struct mmc_card *card, int width);
> > @@ -21,6 +22,18 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
> >  int mmc_app_send_scr(struct mmc_card *card);
> >  int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> >  int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
> > +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
> > +
> > +static inline void mmc_uhs2_card_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
> > +{
> > +       if (host->uhs2_sd_tran)
> > +               mmc_uhs2_prepare_cmd(host, mrq);
> > +}
>
> Please remove this inline function and just check "host->uhs2_sd_tran"
> in mmc_start_request() and mmc_cqe_start_req(). If "uhs2_sd_tran" is
> set, then just call mmc_uhs2_prepare_cmd() directly.
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> > +
> > +static inline int mmc_sd_can_poweroff_notify(struct mmc_card *card)
> > +{
> > +       return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> > +}
>
> This function is a copy of sd_can_poweroff_notify(). Please use
> sd_can_poweroff_notify() and remove mmc_sd_can_poweroff_notify().
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> >
> >  #endif
> >
> > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > index beb2541338ff..85939a2582dc 100644
> > --- a/drivers/mmc/core/sd_uhs2.c
> > +++ b/drivers/mmc/core/sd_uhs2.c
> > @@ -1,23 +1,51 @@
> >  // SPDX-License-Identifier: GPL-2.0-only
> >  /*
> >   * Copyright (C) 2021 Linaro Ltd
> > - *
> >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> >   *
> > + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> > + * Author: Yi Sun <yi.y.sun@intel.com>
> > + *
> > + * Copyright (C) 2020 Genesys Logic, Inc.
> > + * Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> > + *
> > + * Copyright (C) 2020 Linaro Limited
> > + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > + *
> > + * Copyright (C) 2022 Genesys Logic, Inc.
> > + * Authors: Jason Lai <jason.lai@genesyslogic.com.tw>
> > + *
> > + * Copyright (C) 2023 Genesys Logic, Inc.
> > + * Authors: Victor Shih <victor.shih@genesyslogic.com.tw>
> > + *
> >   * Support for SD UHS-II cards
> >   */
> >  #include <linux/err.h>
> > +#include <linux/pm_runtime.h>
> >
> >  #include <linux/mmc/host.h>
> >  #include <linux/mmc/card.h>
> > +#include <linux/mmc/mmc.h>
> > +#include <linux/mmc/sd.h>
> > +#include <linux/mmc/sd_uhs2.h>
> >
> > +#include "card.h"
> >  #include "core.h"
> >  #include "bus.h"
> >  #include "sd.h"
> > +#include "sd_ops.h"
> >  #include "mmc_ops.h"
> >
> > +#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US  (1 * 1000)
> > +#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100
> > +
> >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> >
> > +struct sd_uhs2_wait_active_state_data {
> > +       struct mmc_host *host;
> > +       struct mmc_command *cmd;
> > +};
> > +
> >  static int sd_uhs2_power_up(struct mmc_host *host)
> >  {
> >         int err;
> > @@ -42,12 +70,46 @@ static int sd_uhs2_power_off(struct mmc_host *host)
> >
> >         host->ios.vdd = 0;
> >         host->ios.clock = 0;
> > -       host->ios.timing = MMC_TIMING_LEGACY;
> > +       /* Must set UHS2 timing to identify UHS2 mode */
> > +       host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
>
> The only code that seems to check for MMC_TIMING_UHS2_SPEED_A is
> __sdhci_uhs2_set_ios() (that is added later in the series). Moreover
> that code only cares about MMC_TIMING_UHS2_SPEED_A if "ios->power_mode
> != MMC_POWER_OFF".
>
> In other words, I think it should be okay to keep MMC_TIMING_LEGACY here, right?
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> >         host->ios.power_mode = MMC_POWER_OFF;
> > +       host->uhs2_sd_tran = false;
> >
> >         return host->ops->uhs2_control(host, UHS2_SET_IOS);
> >  }
> >
>
> [...]
>
> >  /*
> >   * 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)
> > +static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
> >  {
> >         struct mmc_card *card;
> >         u32 node_id;
> > @@ -131,9 +818,14 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 return err;
> >
> > -       card = mmc_alloc_card(host, &sd_type);
> > -       if (IS_ERR(card))
> > -               return PTR_ERR(card);
> > +       if (oldcard) {
> > +               card = oldcard;
> > +       } else {
> > +               card = mmc_alloc_card(host, &sd_type);
> > +               if (IS_ERR(card))
> > +                       return PTR_ERR(card);
> > +       }
> > +       host->card = card;
>
> Why do you need to assign the host->card at this point, isn't it okay
> to do that later where it was originally?
>
> Anyway, there is a more important comment a few lines below that also
> relates to this.
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> >
> >         card->uhs2_config.node_id = node_id;
> >         card->type = MMC_TYPE_SD;
> > @@ -146,18 +838,189 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > -       host->card = card;
> > +       /* If change speed to Range B, need to GO_DORMANT_STATE */
> > +       if (host->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
> > +           host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD) {
> > +               err = sd_uhs2_go_dormant_state(host, node_id);
> > +               if (err)
> > +                       goto err;
> > +       }
> > +
> > +       host->uhs2_sd_tran = true;
> > +
> >         return 0;
> >
> >  err:
> > -       mmc_remove_card(card);
> > +       if (!oldcard)
> > +               mmc_remove_card(card);
> > +       else
> > +               sd_uhs2_remove(host);
>
> This doesn't work. If there is an "oldcard", that's because we are
> trying to re-initialize the card during a resume. If we fail to
> re-initialize it we must not remove the oldcard in this path, but
> rather just return/propagate an error code.
>
> Instead, the oldcard should be removed from the mmc_rescan() work when
> it realizes that the call to "host->bus_ops->detect(host)" fails (and
> it must fail then).
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> >         return err;
> >  }
> >
> > -static void sd_uhs2_remove(struct mmc_host *host)
> > +/*
> > + * 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)
> >  {
> > -       mmc_remove_card(host->card);
> > -       host->card = NULL;
> > +       int err;
> > +       u32 cid[4];
> > +       u32 ocr;
> > +       u32 rocr;
> > +       u8  *status;
> > +       int ro;
> > +
> > +       /* Send CMD0 to reset SD card */
> > +       err = __mmc_go_idle(host);
> > +       if (err)
> > +               return err;
> > +
> > +       mmc_delay(1);
> > +
> > +       /* Send CMD8 to communicate SD interface operation condition */
> > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > +       if (err) {
> > +               dev_warn(mmc_dev(host), "CMD8 error\n");
> > +               goto err;
> > +       }
> > +
> > +       /*
> > +        * Probe SD card working voltage.
> > +        */
> > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > +       if (err)
> > +               goto 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)
> > +               rocr = host->ocr_avail;
> > +
> > +       rocr |= (SD_OCR_CCS | SD_OCR_XPC);
> > +
> > +       /* Wait SD power on ready */
> > +       ocr = rocr;
> > +
> > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > +       if (err)
> > +               goto err;
> > +
> > +       err = mmc_send_cid(host, cid);
> > +       if (err)
> > +               goto err;
> > +
> > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > +       mmc_decode_cid(card);
> > +
> > +       /*
> > +        * For native busses:  get card RCA and quit open drain mode.
> > +        */
> > +       err = mmc_send_relative_addr(host, &card->rca);
> > +       if (err)
> > +               goto err;
> > +
> > +       err = mmc_sd_get_csd(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       /*
> > +        * Select card, as all following commands rely on that.
> > +        */
> > +       err = mmc_select_card(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       /*
> > +        * Fetch SCR from card.
> > +        */
> > +       err = mmc_app_send_scr(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       err = mmc_decode_scr(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       /*
> > +        * Switch to high power consumption mode.
> > +        * Even switch failed, sd card can still work at lower power consumption mode, but
> > +        * performance will be lower than high power consumption mode.
> > +        */
> > +       status = kmalloc(64, GFP_KERNEL);
> > +       if (!status)
> > +               return -ENOMEM;
> > +
> > +       if (!(card->csd.cmdclass & CCC_SWITCH)) {
> > +               pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
> > +                       mmc_hostname(card->host));
> > +       } else {
> > +               /* send CMD6 to set Maximum Power Consumption to get better performance */
> > +               err = mmc_sd_switch(card, 0, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> > +               if (!err)
> > +                       err = mmc_sd_switch(card, 1, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> > +
> > +               err = 0;
> > +       }
> > +
> > +       /*
> > +        * Check if read-only switch is active.
> > +        */
> > +       ro = mmc_sd_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);
> > +       }
>
> Some of the above commands in sd_uhs2_legacy_init() are only needed
> during the first initialization of the card. When re-initializing a
> card during a resume, we can skip several of these steps as they are
> simply unnecessary.
>
> However, to validate that it's the same card that remains inserted
> during re-initialization, we need to read the CID register and compare
> it against the CID register that we have stored earlier in
> card->raw_cid. Please have a look at mmc_sd_init_card() to see how
> this works for legacy SD cards.
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> > +
> > +       /*
> > +        * NOTE:
> > +        * Should we read Externsion Register to check power notification feature here?
> > +        */
>
> Yes, this is needed too.
>
> > +
> > +       kfree(status);
> > +
> > +       return 0;
> > +
> > +err:
> > +       sd_uhs2_remove(host);
>
> Again, this doesn't work if we are re-initializing the card during a
> resume. The card must only be removed if this is the first
> initialization of the card - otherwise the removal needs to be managed
> by the mmc_rescan() work.
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> > +       return err;
> > +}
> > +
> > +static int sd_uhs2_reinit(struct mmc_host *host)
> > +{
> > +       struct mmc_card *card = host->card;
> > +       int err;
> > +
> > +       sd_uhs2_power_up(host);
> > +       err = sd_uhs2_phy_init(host);
> > +       if (err)
> > +               return err;
> > +
> > +       err = sd_uhs2_init_card(host, card);
> > +       if (err)
> > +               return err;
> > +
> > +       err = sd_uhs2_legacy_init(host, card);
> > +       if (err)
> > +               return err;
> > +
> > +       mmc_card_set_present(card);
>
> Drop this, it's already managed when calling mmc_add_card() and that
> should be sufficient.
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> > +       return err;
> >  }
> >
> >  static int sd_uhs2_alive(struct mmc_host *host)
> > @@ -179,38 +1042,194 @@ static void sd_uhs2_detect(struct mmc_host *host)
> >                 mmc_claim_host(host);
> >                 mmc_detach_bus(host);
> >                 sd_uhs2_power_off(host);
> > +               /* Remove UHS2 timing to indicate the end of UHS2 mode */
> > +               host->ios.timing = MMC_TIMING_LEGACY;
>
> If my earlier comment for sd_uhs2_power_off() was correct, this part
> should be dropped too.
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> >                 mmc_release_host(host);
> >         }
> >  }
> >
> > +static int _sd_uhs2_suspend(struct mmc_host *host)
> > +{
> > +       struct mmc_card *card = host->card;
> > +       int err = 0;
> > +
> > +       mmc_claim_host(host);
> > +
> > +       if (mmc_card_suspended(card))
> > +               goto out;
> > +
> > +       if (mmc_sd_can_poweroff_notify(card))
>
> Use sd_can_poweroff_notify() instead.
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> > +               err = sd_poweroff_notify(card);
> > +
> > +       if (!err) {
> > +               sd_uhs2_power_off(host);
> > +               mmc_card_set_suspended(card);
> > +       }
> > +
> > +out:
> > +       mmc_release_host(host);
> > +       return err;
> > +}
> > +
>
> [...]
>
> >
> > @@ -237,7 +1256,7 @@ static int sd_uhs2_attach(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > -       err = sd_uhs2_init_card(host);
> > +       err = sd_uhs2_init_card(host, NULL);
> >         if (err)
> >                 goto err;
> >
> > @@ -254,21 +1273,33 @@ static int sd_uhs2_attach(struct mmc_host *host)
> >                 goto remove_card;
> >
> >         mmc_claim_host(host);
> > +
> >         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);
> > +       /* Remove UHS2 timing to indicate the end of UHS2 mode */
> > +       host->ios.timing = MMC_TIMING_LEGACY;
>
> Again, see my earlier comment for sd_uhs2_power_off(). Can this part be dropped?
>

Hi, Ulf

I will update it in the next version.

Thanks, Victor Shih

> >         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;
> > +       int i, err;
> >
> >         if (!(host->caps2 & MMC_CAP2_SD_UHS2))
> >                 return -EOPNOTSUPP;
> > @@ -283,6 +1314,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_debug("%s: %s: trying to init UHS-II card at %u Hz\n",
> > +                        mmc_hostname(host), __func__, host->f_init);
> > +
>
> This looks like it belongs in patch 2 instead. I have amended patch 2
> with this before I applied it.
>
> >                 err = sd_uhs2_attach(host);
> >                 if (!err)
> >                         break;
>
> [...]
>
> Other than the above comments, I just wanted to highlight that I have
> just queued up a series from Avri that adds support for SD Ultra
> Capacity (SDUC) cards.
>
> There are some adjustments needed for your series for UHS-II to cope
> with SDUC, but I think those should be quite minor.
>
> Kind regards
> Uffe

Hi, Ulf

I will rebase the patches on the latest mmc/next branch.

Thanks, Victor Shih
Re: [PATCH V22 07/22] mmc: core: Support UHS-II card control and access
Posted by Victor Shih 1 month, 2 weeks ago
On Mon, Oct 7, 2024 at 11:36 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Fri, 13 Sept 2024 at 12:29, Victor Shih <victorshihgli@gmail.com> wrote:
> >
> > From: Victor Shih <victor.shih@genesyslogic.com.tw>
> >
> > Embed UHS-II access/control functionality into the MMC request
> > processing flow.
> >
> > Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> > Signed-off-by: Jason Lai <jason.lai@genesyslogic.com.tw>
> > Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
> > ---
> >
> > Updates in V16:
> >  - Separate the Error Recovery mechanism into a separate patch#8.
> >
> > Updates in V15:
> >  - Move struct uhs2_command uhs2_cmd to struct mmc_request and
> >    modify whatever other changers to make it work.
> >  - Refer the SD Host Controller Standard Specification Section 3.10
> >    to add Error Recovery mechanism to recover the command error.
> >
> > Updates in V13:
> >  - Separate __mmc_go_idle() into one patch for re-factorring the code.
> >  - Move mmc_decode_scr declaration to sd.h.
> >  - Ues uhs2_sd_tran to stead MMC_UHS2_SD_TRAN.
> >  - Drop unnecessary comment.
> >
> > Updates in V12:
> >  - Use mmc_op_multi() to check DCMD which supports multi read/write
> >    in mmc_uhs2_prepare_cmd().
> >
> > Updates in V10:
> >  - Move some definitions of PatchV9[02/23] to PatchV10[06/23].
> >  - Move some definitions of PatchV9[05/23] to PatchV10[06/23].
> >  - Drop do_multi in the mmc_blk_rw_rq_prep().
> >  - Use tmode_half_duplex to instead of uhs2_tmode0_flag.
> >  - Move entire control of the tmode into mmc_uhs2_prepare_cmd().
> >
> > Updates in V8:
> >  - Add MMC_UHS2_SUPPORT to be cleared in sd_uhs2_detect().
> >  - Modify return value in sd_uhs2_attach().
> >
> > Updates in V7:
> >  - Add mmc_uhs2_card_prepare_cmd helper function in sd_ops.h.
> >  - Drop uhs2_state in favor of ios->timing.
> >  - Remove unnecessary functions.
> >
> > ---
> >
> >  drivers/mmc/core/core.c    |    6 +-
> >  drivers/mmc/core/sd.c      |   10 +-
> >  drivers/mmc/core/sd.h      |    5 +
> >  drivers/mmc/core/sd_ops.c  |    9 +
> >  drivers/mmc/core/sd_ops.h  |   13 +
> >  drivers/mmc/core/sd_uhs2.c | 1100 ++++++++++++++++++++++++++++++++++--
> >  include/linux/mmc/core.h   |   17 +
> >  include/linux/mmc/host.h   |   15 +
> >  8 files changed, 1136 insertions(+), 39 deletions(-)
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index 8b8aff131f8b..1841a2190571 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -351,6 +351,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 return err;
> >
> > +       mmc_uhs2_card_prepare_cmd(host, mrq);
> > +
> >         led_trigger_event(host->led, LED_FULL);
> >         __mmc_start_request(host, mrq);
> >
> > @@ -450,6 +452,8 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> >         if (err)
> >                 goto out_err;
> >
> > +       mmc_uhs2_card_prepare_cmd(host, mrq);
> > +
> >         err = host->cqe_ops->cqe_request(host, mrq);
> >         if (err)
> >                 goto out_err;
> > @@ -1132,7 +1136,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
> >                 return 0;
> >         }
> >
> > -       if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> > +       if (!mmc_card_uhs2(host) && host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
> >                 bit = ffs(ocr) - 1;
> >                 ocr &= 3 << bit;
> >                 mmc_power_cycle(host, ocr);
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index 12fe282bea77..d5576a2f4435 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -194,7 +194,7 @@ static int mmc_decode_csd(struct mmc_card *card)
> >  /*
> >   * Given a 64-bit response, decode to our card SCR structure.
> >   */
> > -static int mmc_decode_scr(struct mmc_card *card)
> > +int mmc_decode_scr(struct mmc_card *card)
> >  {
> >         struct sd_scr *scr = &card->scr;
> >         unsigned int scr_struct;
> > @@ -894,7 +894,7 @@ int mmc_sd_get_csd(struct mmc_card *card)
> >         return 0;
> >  }
> >
> > -static int mmc_sd_get_ro(struct mmc_host *host)
> > +int mmc_sd_get_ro(struct mmc_host *host)
> >  {
> >         int ro;
> >
> > @@ -1623,7 +1623,7 @@ static void mmc_sd_detect(struct mmc_host *host)
> >         }
> >  }
> >
> > -static int sd_can_poweroff_notify(struct mmc_card *card)
> > +int sd_can_poweroff_notify(struct mmc_card *card)
> >  {
> >         return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> >  }
> > @@ -1651,7 +1651,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy)
> >         return 0;
> >  }
> >
> > -static int sd_poweroff_notify(struct mmc_card *card)
> > +int sd_poweroff_notify(struct mmc_card *card)
> >  {
> >         struct sd_busy_data cb_data;
> >         u8 *reg_buf;
> > @@ -1699,7 +1699,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
> >         if (mmc_card_suspended(card))
> >                 goto out;
> >
> > -       if (sd_can_poweroff_notify(card))
> > +       if (mmc_sd_can_poweroff_notify(card))
>
> There is no need to change this. Please stick with sd_can_poweroff_notify().
>
> >                 err = sd_poweroff_notify(card);
> >         else if (!mmc_host_is_spi(host))
> >                 err = mmc_deselect_cards(host);
> > diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
> > index fe6dd46927a4..49afaef269cd 100644
> > --- a/drivers/mmc/core/sd.h
> > +++ b/drivers/mmc/core/sd.h
> > @@ -11,10 +11,15 @@ struct mmc_card;
> >
> >  int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
> >  int mmc_sd_get_csd(struct mmc_card *card);
> > +int mmc_decode_scr(struct mmc_card *card);
> > +int mmc_sd_get_ro(struct mmc_host *host);
> >  void mmc_decode_cid(struct mmc_card *card);
> >  int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
> >         bool reinit);
> >  unsigned mmc_sd_get_max_clock(struct mmc_card *card);
> >  int mmc_sd_switch_hs(struct mmc_card *card);
> >
> > +int sd_can_poweroff_notify(struct mmc_card *card);
> > +int sd_poweroff_notify(struct mmc_card *card);
> > +
> >  #endif
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index f93c392040ae..5cd1216edf0d 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -41,6 +41,15 @@ int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
> >         if (WARN_ON(card && card->host != host))
> >                 return -EINVAL;
> >
> > +       /*
> > +        * UHS2 packet has APP bit so only set APP_CMD flag here.
> > +        * Will set the APP bit when assembling UHS2 packet.
> > +        */
> > +       if (host->uhs2_sd_tran) {
> > +               host->uhs2_app_cmd = true;
> > +               return 0;
> > +       }
> > +
> >         cmd.opcode = MMC_APP_CMD;
> >
> >         if (card) {
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index 7667fc223b74..e3af68a52de8 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -11,6 +11,7 @@
> >  #include <linux/types.h>
> >
> >  struct mmc_card;
> > +struct mmc_command;
> >  struct mmc_host;
> >
> >  int mmc_app_set_bus_width(struct mmc_card *card, int width);
> > @@ -21,6 +22,18 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
> >  int mmc_app_send_scr(struct mmc_card *card);
> >  int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> >  int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
> > +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
> > +
> > +static inline void mmc_uhs2_card_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq)
> > +{
> > +       if (host->uhs2_sd_tran)
> > +               mmc_uhs2_prepare_cmd(host, mrq);
> > +}
>
> Please remove this inline function and just check "host->uhs2_sd_tran"
> in mmc_start_request() and mmc_cqe_start_req(). If "uhs2_sd_tran" is
> set, then just call mmc_uhs2_prepare_cmd() directly.
>
> > +
> > +static inline int mmc_sd_can_poweroff_notify(struct mmc_card *card)
> > +{
> > +       return card->ext_power.feature_support & SD_EXT_POWER_OFF_NOTIFY;
> > +}
>
> This function is a copy of sd_can_poweroff_notify(). Please use
> sd_can_poweroff_notify() and remove mmc_sd_can_poweroff_notify().
>
> >
> >  #endif
> >
> > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> > index beb2541338ff..85939a2582dc 100644
> > --- a/drivers/mmc/core/sd_uhs2.c
> > +++ b/drivers/mmc/core/sd_uhs2.c
> > @@ -1,23 +1,51 @@
> >  // SPDX-License-Identifier: GPL-2.0-only
> >  /*
> >   * Copyright (C) 2021 Linaro Ltd
> > - *
> >   * Author: Ulf Hansson <ulf.hansson@linaro.org>
> >   *
> > + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> > + * Author: Yi Sun <yi.y.sun@intel.com>
> > + *
> > + * Copyright (C) 2020 Genesys Logic, Inc.
> > + * Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> > + *
> > + * Copyright (C) 2020 Linaro Limited
> > + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > + *
> > + * Copyright (C) 2022 Genesys Logic, Inc.
> > + * Authors: Jason Lai <jason.lai@genesyslogic.com.tw>
> > + *
> > + * Copyright (C) 2023 Genesys Logic, Inc.
> > + * Authors: Victor Shih <victor.shih@genesyslogic.com.tw>
> > + *
> >   * Support for SD UHS-II cards
> >   */
> >  #include <linux/err.h>
> > +#include <linux/pm_runtime.h>
> >
> >  #include <linux/mmc/host.h>
> >  #include <linux/mmc/card.h>
> > +#include <linux/mmc/mmc.h>
> > +#include <linux/mmc/sd.h>
> > +#include <linux/mmc/sd_uhs2.h>
> >
> > +#include "card.h"
> >  #include "core.h"
> >  #include "bus.h"
> >  #include "sd.h"
> > +#include "sd_ops.h"
> >  #include "mmc_ops.h"
> >
> > +#define UHS2_WAIT_CFG_COMPLETE_PERIOD_US  (1 * 1000)
> > +#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100
> > +
> >  static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 };
> >
> > +struct sd_uhs2_wait_active_state_data {
> > +       struct mmc_host *host;
> > +       struct mmc_command *cmd;
> > +};
> > +
> >  static int sd_uhs2_power_up(struct mmc_host *host)
> >  {
> >         int err;
> > @@ -42,12 +70,46 @@ static int sd_uhs2_power_off(struct mmc_host *host)
> >
> >         host->ios.vdd = 0;
> >         host->ios.clock = 0;
> > -       host->ios.timing = MMC_TIMING_LEGACY;
> > +       /* Must set UHS2 timing to identify UHS2 mode */
> > +       host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
>
> The only code that seems to check for MMC_TIMING_UHS2_SPEED_A is
> __sdhci_uhs2_set_ios() (that is added later in the series). Moreover
> that code only cares about MMC_TIMING_UHS2_SPEED_A if "ios->power_mode
> != MMC_POWER_OFF".
>
> In other words, I think it should be okay to keep MMC_TIMING_LEGACY here, right?
>
> >         host->ios.power_mode = MMC_POWER_OFF;
> > +       host->uhs2_sd_tran = false;
> >
> >         return host->ops->uhs2_control(host, UHS2_SET_IOS);
> >  }
> >
>
> [...]
>
> >  /*
> >   * 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)
> > +static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
> >  {
> >         struct mmc_card *card;
> >         u32 node_id;
> > @@ -131,9 +818,14 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 return err;
> >
> > -       card = mmc_alloc_card(host, &sd_type);
> > -       if (IS_ERR(card))
> > -               return PTR_ERR(card);
> > +       if (oldcard) {
> > +               card = oldcard;
> > +       } else {
> > +               card = mmc_alloc_card(host, &sd_type);
> > +               if (IS_ERR(card))
> > +                       return PTR_ERR(card);
> > +       }
> > +       host->card = card;
>
> Why do you need to assign the host->card at this point, isn't it okay
> to do that later where it was originally?
>
> Anyway, there is a more important comment a few lines below that also
> relates to this.
>
> >
> >         card->uhs2_config.node_id = node_id;
> >         card->type = MMC_TYPE_SD;
> > @@ -146,18 +838,189 @@ static int sd_uhs2_init_card(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > -       host->card = card;
> > +       /* If change speed to Range B, need to GO_DORMANT_STATE */
> > +       if (host->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
> > +           host->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD) {
> > +               err = sd_uhs2_go_dormant_state(host, node_id);
> > +               if (err)
> > +                       goto err;
> > +       }
> > +
> > +       host->uhs2_sd_tran = true;
> > +
> >         return 0;
> >
> >  err:
> > -       mmc_remove_card(card);
> > +       if (!oldcard)
> > +               mmc_remove_card(card);
> > +       else
> > +               sd_uhs2_remove(host);
>
> This doesn't work. If there is an "oldcard", that's because we are
> trying to re-initialize the card during a resume. If we fail to
> re-initialize it we must not remove the oldcard in this path, but
> rather just return/propagate an error code.
>
> Instead, the oldcard should be removed from the mmc_rescan() work when
> it realizes that the call to "host->bus_ops->detect(host)" fails (and
> it must fail then).
>
> >         return err;
> >  }
> >
> > -static void sd_uhs2_remove(struct mmc_host *host)
> > +/*
> > + * 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)
> >  {
> > -       mmc_remove_card(host->card);
> > -       host->card = NULL;
> > +       int err;
> > +       u32 cid[4];
> > +       u32 ocr;
> > +       u32 rocr;
> > +       u8  *status;
> > +       int ro;
> > +
> > +       /* Send CMD0 to reset SD card */
> > +       err = __mmc_go_idle(host);
> > +       if (err)
> > +               return err;
> > +
> > +       mmc_delay(1);
> > +
> > +       /* Send CMD8 to communicate SD interface operation condition */
> > +       err = mmc_send_if_cond(host, host->ocr_avail);
> > +       if (err) {
> > +               dev_warn(mmc_dev(host), "CMD8 error\n");
> > +               goto err;
> > +       }
> > +
> > +       /*
> > +        * Probe SD card working voltage.
> > +        */
> > +       err = mmc_send_app_op_cond(host, 0, &ocr);
> > +       if (err)
> > +               goto 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)
> > +               rocr = host->ocr_avail;
> > +
> > +       rocr |= (SD_OCR_CCS | SD_OCR_XPC);
> > +
> > +       /* Wait SD power on ready */
> > +       ocr = rocr;
> > +
> > +       err = mmc_send_app_op_cond(host, ocr, &rocr);
> > +       if (err)
> > +               goto err;
> > +
> > +       err = mmc_send_cid(host, cid);
> > +       if (err)
> > +               goto err;
> > +
> > +       memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
> > +       mmc_decode_cid(card);
> > +
> > +       /*
> > +        * For native busses:  get card RCA and quit open drain mode.
> > +        */
> > +       err = mmc_send_relative_addr(host, &card->rca);
> > +       if (err)
> > +               goto err;
> > +
> > +       err = mmc_sd_get_csd(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       /*
> > +        * Select card, as all following commands rely on that.
> > +        */
> > +       err = mmc_select_card(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       /*
> > +        * Fetch SCR from card.
> > +        */
> > +       err = mmc_app_send_scr(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       err = mmc_decode_scr(card);
> > +       if (err)
> > +               goto err;
> > +
> > +       /*
> > +        * Switch to high power consumption mode.
> > +        * Even switch failed, sd card can still work at lower power consumption mode, but
> > +        * performance will be lower than high power consumption mode.
> > +        */
> > +       status = kmalloc(64, GFP_KERNEL);
> > +       if (!status)
> > +               return -ENOMEM;
> > +
> > +       if (!(card->csd.cmdclass & CCC_SWITCH)) {
> > +               pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
> > +                       mmc_hostname(card->host));
> > +       } else {
> > +               /* send CMD6 to set Maximum Power Consumption to get better performance */
> > +               err = mmc_sd_switch(card, 0, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> > +               if (!err)
> > +                       err = mmc_sd_switch(card, 1, 3, SD4_SET_POWER_LIMIT_1_80W, status);
> > +
> > +               err = 0;
> > +       }
> > +
> > +       /*
> > +        * Check if read-only switch is active.
> > +        */
> > +       ro = mmc_sd_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);
> > +       }
>
> Some of the above commands in sd_uhs2_legacy_init() are only needed
> during the first initialization of the card. When re-initializing a
> card during a resume, we can skip several of these steps as they are
> simply unnecessary.
>
> However, to validate that it's the same card that remains inserted
> during re-initialization, we need to read the CID register and compare
> it against the CID register that we have stored earlier in
> card->raw_cid. Please have a look at mmc_sd_init_card() to see how
> this works for legacy SD cards.
>
> > +
> > +       /*
> > +        * NOTE:
> > +        * Should we read Externsion Register to check power notification feature here?
> > +        */
>
> Yes, this is needed too.
>

Hi, Ulf

We have the call sd_can_poweroff_notify() in the _mmc_sd_suspend() and
_sd_uhs2_suspend.
If we need to check the power notification feature here,
do you have any suggestions for the type of power notification that
needs to be confirmed?

Thanks, Victor Shih

> > +
> > +       kfree(status);
> > +
> > +       return 0;
> > +
> > +err:
> > +       sd_uhs2_remove(host);
>
> Again, this doesn't work if we are re-initializing the card during a
> resume. The card must only be removed if this is the first
> initialization of the card - otherwise the removal needs to be managed
> by the mmc_rescan() work.
>
> > +       return err;
> > +}
> > +
> > +static int sd_uhs2_reinit(struct mmc_host *host)
> > +{
> > +       struct mmc_card *card = host->card;
> > +       int err;
> > +
> > +       sd_uhs2_power_up(host);
> > +       err = sd_uhs2_phy_init(host);
> > +       if (err)
> > +               return err;
> > +
> > +       err = sd_uhs2_init_card(host, card);
> > +       if (err)
> > +               return err;
> > +
> > +       err = sd_uhs2_legacy_init(host, card);
> > +       if (err)
> > +               return err;
> > +
> > +       mmc_card_set_present(card);
>
> Drop this, it's already managed when calling mmc_add_card() and that
> should be sufficient.
>
> > +       return err;
> >  }
> >
> >  static int sd_uhs2_alive(struct mmc_host *host)
> > @@ -179,38 +1042,194 @@ static void sd_uhs2_detect(struct mmc_host *host)
> >                 mmc_claim_host(host);
> >                 mmc_detach_bus(host);
> >                 sd_uhs2_power_off(host);
> > +               /* Remove UHS2 timing to indicate the end of UHS2 mode */
> > +               host->ios.timing = MMC_TIMING_LEGACY;
>
> If my earlier comment for sd_uhs2_power_off() was correct, this part
> should be dropped too.
>
> >                 mmc_release_host(host);
> >         }
> >  }
> >
> > +static int _sd_uhs2_suspend(struct mmc_host *host)
> > +{
> > +       struct mmc_card *card = host->card;
> > +       int err = 0;
> > +
> > +       mmc_claim_host(host);
> > +
> > +       if (mmc_card_suspended(card))
> > +               goto out;
> > +
> > +       if (mmc_sd_can_poweroff_notify(card))
>
> Use sd_can_poweroff_notify() instead.
>
> > +               err = sd_poweroff_notify(card);
> > +
> > +       if (!err) {
> > +               sd_uhs2_power_off(host);
> > +               mmc_card_set_suspended(card);
> > +       }
> > +
> > +out:
> > +       mmc_release_host(host);
> > +       return err;
> > +}
> > +
>
> [...]
>
> >
> > @@ -237,7 +1256,7 @@ static int sd_uhs2_attach(struct mmc_host *host)
> >         if (err)
> >                 goto err;
> >
> > -       err = sd_uhs2_init_card(host);
> > +       err = sd_uhs2_init_card(host, NULL);
> >         if (err)
> >                 goto err;
> >
> > @@ -254,21 +1273,33 @@ static int sd_uhs2_attach(struct mmc_host *host)
> >                 goto remove_card;
> >
> >         mmc_claim_host(host);
> > +
> >         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);
> > +       /* Remove UHS2 timing to indicate the end of UHS2 mode */
> > +       host->ios.timing = MMC_TIMING_LEGACY;
>
> Again, see my earlier comment for sd_uhs2_power_off(). Can this part be dropped?
>
> >         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;
> > +       int i, err;
> >
> >         if (!(host->caps2 & MMC_CAP2_SD_UHS2))
> >                 return -EOPNOTSUPP;
> > @@ -283,6 +1314,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_debug("%s: %s: trying to init UHS-II card at %u Hz\n",
> > +                        mmc_hostname(host), __func__, host->f_init);
> > +
>
> This looks like it belongs in patch 2 instead. I have amended patch 2
> with this before I applied it.
>
> >                 err = sd_uhs2_attach(host);
> >                 if (!err)
> >                         break;
>
> [...]
>
> Other than the above comments, I just wanted to highlight that I have
> just queued up a series from Avri that adds support for SD Ultra
> Capacity (SDUC) cards.
>
> There are some adjustments needed for your series for UHS-II to cope
> with SDUC, but I think those should be quite minor.
>
> Kind regards
> Uffe
Re: [PATCH V22 07/22] mmc: core: Support UHS-II card control and access
Posted by Ulf Hansson 1 month, 2 weeks ago
[...]

> > > +
> > > +       /*
> > > +        * NOTE:
> > > +        * Should we read Externsion Register to check power notification feature here?
> > > +        */
> >
> > Yes, this is needed too.
> >
>
> Hi, Ulf
>
> We have the call sd_can_poweroff_notify() in the _mmc_sd_suspend() and
> _sd_uhs2_suspend.
> If we need to check the power notification feature here,
> do you have any suggestions for the type of power notification that
> needs to be confirmed?

Right, I see what you mean! It's been a while since I read these parts
of the SD spec, apologize for my ignorance.

In UHS-II mode, the power-off-notification is *not* supported. This
means that we should not use it from _sd_uhs2_suspend().

In UHS-II mode we should instead use the so-called "hibernate" mode
(hbr bit in the GO_DORMANT command), which allows us to turn off
clocks and VDD1 (corresponds to the vmmc regulator). Note that the
support for hibernate mode is optional for an UHS-II SD card. Thus,
the support must be checked via the corresponding field in the PHY
capabilities, before we try to use it.

That said, to not make it more complicated for you to get the basic
UHS-II support landed upstream, I am fine if you decide to defer the
support for hibernate mode to later. Instead, it seems like we should
just do a regular power-off in _sd_uhs2_suspend(). In other words,
just call sd_uhs2_power_off() from there.

Moreover, reading the SD Extension Registers would still be valuable
during the UHS-II card initialization (even if hibernate isn't
supported), as it allows us to support the "cache maintenance"
feature, for example. However, the support for these features are
optional too for SD cards, so I am fine if you decide to defer this to
a later improvement too.

Does this make sense to you?

[...]

Kind regards
Uffe
Re: [PATCH V22 07/22] mmc: core: Support UHS-II card control and access
Posted by Victor Shih 1 month, 1 week ago
On Thu, Oct 10, 2024 at 7:22 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> [...]
>
> > > > +
> > > > +       /*
> > > > +        * NOTE:
> > > > +        * Should we read Externsion Register to check power notification feature here?
> > > > +        */
> > >
> > > Yes, this is needed too.
> > >
> >
> > Hi, Ulf
> >
> > We have the call sd_can_poweroff_notify() in the _mmc_sd_suspend() and
> > _sd_uhs2_suspend.
> > If we need to check the power notification feature here,
> > do you have any suggestions for the type of power notification that
> > needs to be confirmed?
>
> Right, I see what you mean! It's been a while since I read these parts
> of the SD spec, apologize for my ignorance.
>
> In UHS-II mode, the power-off-notification is *not* supported. This
> means that we should not use it from _sd_uhs2_suspend().
>
> In UHS-II mode we should instead use the so-called "hibernate" mode
> (hbr bit in the GO_DORMANT command), which allows us to turn off
> clocks and VDD1 (corresponds to the vmmc regulator). Note that the
> support for hibernate mode is optional for an UHS-II SD card. Thus,
> the support must be checked via the corresponding field in the PHY
> capabilities, before we try to use it.
>
> That said, to not make it more complicated for you to get the basic
> UHS-II support landed upstream, I am fine if you decide to defer the
> support for hibernate mode to later. Instead, it seems like we should
> just do a regular power-off in _sd_uhs2_suspend(). In other words,
> just call sd_uhs2_power_off() from there.
>
> Moreover, reading the SD Extension Registers would still be valuable
> during the UHS-II card initialization (even if hibernate isn't
> supported), as it allows us to support the "cache maintenance"
> feature, for example. However, the support for these features are
> optional too for SD cards, so I am fine if you decide to defer this to
> a later improvement too.
>
> Does this make sense to you?
>
> [...]
>
> Kind regards
> Uffe

Hi, Ulf

I hope that this feature can be implemented later.

Thanks, Victor Shih