* [PATCH V23 01/16] mmc: core: Support UHS-II card control and access
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-24 10:47 ` Adrian Hunter
2024-10-18 10:53 ` [PATCH V23 02/16] mmc: sdhci: add UHS-II related definitions in headers Victor Shih
` (16 subsequent siblings)
17 siblings, 1 reply; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Jason Lai, Victor Shih
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 V23:
- Remove mmc_uhs2_card_prepare_cmd() function.
- Remove mmc_sd_can_poweroff_notify() function.
- Modify ios.timing setting in the sd_uhs2_power_off() function.
- Restore the position of assign the host->card to original position
in the sd_uhs2_init_card() function.
- Remove unnecessary error handle in the sd_uhs2_init_card() function.
- Add oldcard judgment to skip some programs in the
sd_uhs2_legacy_init() function.
- Remove unnecessary error handle in the sd_uhs2_legacy_init() function.
- Remove mmc_card_set_present() function in the sd_uhs2_reinit()function.
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 | 8 +-
drivers/mmc/core/sd.c | 8 +-
drivers/mmc/core/sd.h | 5 +
drivers/mmc/core/sd_ops.c | 9 +
drivers/mmc/core/sd_ops.h | 2 +
drivers/mmc/core/sd_uhs2.c | 1097 ++++++++++++++++++++++++++++++++++--
include/linux/mmc/core.h | 17 +
include/linux/mmc/host.h | 15 +
8 files changed, 1122 insertions(+), 39 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 54ca9dc2114c..a499f3c59de5 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -354,6 +354,9 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
if (err)
return err;
+ if (host->uhs2_sd_tran)
+ mmc_uhs2_prepare_cmd(host, mrq);
+
led_trigger_event(host->led, LED_FULL);
__mmc_start_request(host, mrq);
@@ -453,6 +456,9 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
if (err)
goto out_err;
+ if (host->uhs2_sd_tran)
+ mmc_uhs2_prepare_cmd(host, mrq);
+
err = host->cqe_ops->cqe_request(host, mrq);
if (err)
goto out_err;
@@ -1135,7 +1141,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 918b86bf8bbb..f3811b523f68 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -200,7 +200,7 @@ static int mmc_decode_csd(struct mmc_card *card, bool is_sduc)
/*
* 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;
@@ -903,7 +903,7 @@ int mmc_sd_get_csd(struct mmc_card *card, bool is_sduc)
return 0;
}
-static int mmc_sd_get_ro(struct mmc_host *host)
+int mmc_sd_get_ro(struct mmc_host *host)
{
int ro;
@@ -1635,7 +1635,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;
}
@@ -1663,7 +1663,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;
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index 7e8beface2ca..0060accc3a31 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, bool is_sduc);
+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 50d1380e93b8..cd86463dd306 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -42,6 +42,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 fd3f10b9cf86..a86c0cfa0aae 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);
@@ -22,6 +23,7 @@ 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);
int mmc_send_ext_addr(struct mmc_host *host, u32 addr);
+void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
#endif
diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
index 19d62d45e1ec..4e6889430957 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;
+ /* Must set UHS2 timing to identify UHS2 mode */
host->ios.timing = MMC_TIMING_LEGACY;
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;
+}
+
+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;
}
-/*
- * 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_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 = 0;
@@ -131,33 +818,211 @@ 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);
+ }
card->uhs2_config.node_id = node_id;
card->type = MMC_TYPE_SD;
err = sd_uhs2_config_read(host, card);
if (err)
- goto err;
+ return err;
err = sd_uhs2_config_write(host, card);
if (err)
- goto err;
+ return 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)
+ return err;
+ }
+
+ host->uhs2_sd_tran = true;
+
+ 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,
+ struct mmc_card *oldcard)
+{
+ 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;
+
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
+ pr_debug("%s: Perhaps the card was replaced\n",
+ mmc_hostname(host));
+ return -ENOENT;
+ }
+
+ card = oldcard;
+ } else {
+ 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, false);
+ 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:
- mmc_remove_card(card);
return err;
}
-static void sd_uhs2_remove(struct mmc_host *host)
+static int sd_uhs2_reinit(struct mmc_host *host)
{
- mmc_remove_card(host->card);
- host->card = NULL;
+ 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, card);
+ if (err)
+ return err;
+
+ return err;
}
static int sd_uhs2_alive(struct mmc_host *host)
@@ -183,34 +1048,188 @@ static void sd_uhs2_detect(struct mmc_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 (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,11 +1256,11 @@ 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;
- err = sd_uhs2_legacy_init(host, host->card);
+ err = sd_uhs2_legacy_init(host, host->card, NULL);
if (err)
goto err;
@@ -254,21 +1273,31 @@ 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);
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;
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index a890a71288ef..56972bd78462 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;
@@ -97,6 +111,8 @@ struct mmc_command {
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
+ struct uhs2_command *uhs2_cmd; /* UHS2 command */
+
/* for SDUC */
bool has_ext_addr;
u8 ext_addr;
@@ -158,6 +174,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 0980d06ed419..b4c00dbcaf0b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -127,6 +127,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,
};
@@ -453,6 +460,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 */
@@ -714,6 +723,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
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH V23 01/16] mmc: core: Support UHS-II card control and access
2024-10-18 10:53 ` [PATCH V23 01/16] mmc: core: Support UHS-II card control and access Victor Shih
@ 2024-10-24 10:47 ` Adrian Hunter
2024-10-24 12:41 ` Ulf Hansson
0 siblings, 1 reply; 27+ messages in thread
From: Adrian Hunter @ 2024-10-24 10:47 UTC (permalink / raw)
To: Victor Shih, ulf.hansson
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Jason Lai, Victor Shih
On 18/10/24 13:53, Victor Shih 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 V23:
> - Remove mmc_uhs2_card_prepare_cmd() function.
> - Remove mmc_sd_can_poweroff_notify() function.
> - Modify ios.timing setting in the sd_uhs2_power_off() function.
> - Restore the position of assign the host->card to original position
> in the sd_uhs2_init_card() function.
> - Remove unnecessary error handle in the sd_uhs2_init_card() function.
> - Add oldcard judgment to skip some programs in the
> sd_uhs2_legacy_init() function.
> - Remove unnecessary error handle in the sd_uhs2_legacy_init() function.
> - Remove mmc_card_set_present() function in the sd_uhs2_reinit()function.
>
> 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 | 8 +-
> drivers/mmc/core/sd.c | 8 +-
> drivers/mmc/core/sd.h | 5 +
> drivers/mmc/core/sd_ops.c | 9 +
> drivers/mmc/core/sd_ops.h | 2 +
> drivers/mmc/core/sd_uhs2.c | 1097 ++++++++++++++++++++++++++++++++++--
> include/linux/mmc/core.h | 17 +
> include/linux/mmc/host.h | 15 +
> 8 files changed, 1122 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 54ca9dc2114c..a499f3c59de5 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -354,6 +354,9 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
> if (err)
> return err;
>
> + if (host->uhs2_sd_tran)
> + mmc_uhs2_prepare_cmd(host, mrq);
> +
> led_trigger_event(host->led, LED_FULL);
> __mmc_start_request(host, mrq);
>
> @@ -453,6 +456,9 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
> if (err)
> goto out_err;
>
> + if (host->uhs2_sd_tran)
> + mmc_uhs2_prepare_cmd(host, mrq);
> +
> err = host->cqe_ops->cqe_request(host, mrq);
> if (err)
> goto out_err;
> @@ -1135,7 +1141,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 918b86bf8bbb..f3811b523f68 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -200,7 +200,7 @@ static int mmc_decode_csd(struct mmc_card *card, bool is_sduc)
> /*
> * 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;
> @@ -903,7 +903,7 @@ int mmc_sd_get_csd(struct mmc_card *card, bool is_sduc)
> return 0;
> }
>
> -static int mmc_sd_get_ro(struct mmc_host *host)
> +int mmc_sd_get_ro(struct mmc_host *host)
> {
> int ro;
>
> @@ -1635,7 +1635,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;
> }
> @@ -1663,7 +1663,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;
> diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
> index 7e8beface2ca..0060accc3a31 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, bool is_sduc);
> +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 50d1380e93b8..cd86463dd306 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -42,6 +42,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 fd3f10b9cf86..a86c0cfa0aae 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);
> @@ -22,6 +23,7 @@ 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);
> int mmc_send_ext_addr(struct mmc_host *host, u32 addr);
> +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq);
>
> #endif
>
> diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
> index 19d62d45e1ec..4e6889430957 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;
> + /* Must set UHS2 timing to identify UHS2 mode */
That comment is stale, but...
> host->ios.timing = MMC_TIMING_LEGACY;
MMC_TIMING_UHS2_SPEED_A is also checked by mmc_card_uhs2()
which is used a lot.
It might be that ios.timing should be MMC_TIMING_UHS2_SPEED_A
until after calling host->ops->uhs2_control() ?
> 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;
> +}
> +
> +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;
> }
>
> -/*
> - * 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_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 = 0;
> @@ -131,33 +818,211 @@ 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);
> + }
>
> card->uhs2_config.node_id = node_id;
> card->type = MMC_TYPE_SD;
>
> err = sd_uhs2_config_read(host, card);
> if (err)
> - goto err;
> + return err;
This leaks card if !oldcard. Still need:
err:
if (!oldcard)
mmc_remove_card(card);
>
> err = sd_uhs2_config_write(host, card);
> if (err)
> - goto err;
> + return err;
Ditto
>
> 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)
> + return err;
And here host->card is assigned but we have failed.
The other card types do not set host->card until
just before return 0. Then this can be goto err
instead of return err.
> + }
> +
> + host->uhs2_sd_tran = true;
> +
> + 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,
> + struct mmc_card *oldcard)
> +{
> + 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;
> +
> + if (oldcard) {
> + if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
> + pr_debug("%s: Perhaps the card was replaced\n",
> + mmc_hostname(host));
> + return -ENOENT;
> + }
> +
> + card = oldcard;
> + } else {
> + 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, false);
> + 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:
> - mmc_remove_card(card);
> return err;
> }
>
> -static void sd_uhs2_remove(struct mmc_host *host)
> +static int sd_uhs2_reinit(struct mmc_host *host)
> {
> - mmc_remove_card(host->card);
> - host->card = NULL;
> + 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, card);
> + if (err)
> + return err;
> +
> + return err;
> }
>
> static int sd_uhs2_alive(struct mmc_host *host)
> @@ -183,34 +1048,188 @@ static void sd_uhs2_detect(struct mmc_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 (sd_can_poweroff_notify(card))
> + err = sd_poweroff_notify(card);
> +
> + if (!err) {
> + sd_uhs2_power_off(host);
MMC_TIMING_UHS2_SPEED_A is also checked by mmc_card_uhs2()
which is used a lot.
Are you sure we don't need it here for the host controller's
suspend / resume? i.e.
host->ios.timing = MMC_TIMING_UHS2_SPEED_A;
> + 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,11 +1256,11 @@ 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;
>
> - err = sd_uhs2_legacy_init(host, host->card);
> + err = sd_uhs2_legacy_init(host, host->card, NULL);
> if (err)
> goto err;
>
> @@ -254,21 +1273,31 @@ 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);
> 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;
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index a890a71288ef..56972bd78462 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;
> @@ -97,6 +111,8 @@ struct mmc_command {
> struct mmc_data *data; /* data segment associated with cmd */
> struct mmc_request *mrq; /* associated request */
>
> + struct uhs2_command *uhs2_cmd; /* UHS2 command */
> +
> /* for SDUC */
> bool has_ext_addr;
> u8 ext_addr;
> @@ -158,6 +174,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 0980d06ed419..b4c00dbcaf0b 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -127,6 +127,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,
> };
>
> @@ -453,6 +460,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 */
> @@ -714,6 +723,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);
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH V23 01/16] mmc: core: Support UHS-II card control and access
2024-10-24 10:47 ` Adrian Hunter
@ 2024-10-24 12:41 ` Ulf Hansson
0 siblings, 0 replies; 27+ messages in thread
From: Ulf Hansson @ 2024-10-24 12:41 UTC (permalink / raw)
To: Victor Shih, Adrian Hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Jason Lai, Victor Shih
[...]
> > +
> > 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;
> > + /* Must set UHS2 timing to identify UHS2 mode */
>
> That comment is stale, but...
>
> > host->ios.timing = MMC_TIMING_LEGACY;
>
> MMC_TIMING_UHS2_SPEED_A is also checked by mmc_card_uhs2()
> which is used a lot.
>
> It might be that ios.timing should be MMC_TIMING_UHS2_SPEED_A
> until after calling host->ops->uhs2_control() ?
I agree, that was also how the code looked before my previous comment for v22.
Victor, sorry for the confusion, I simply didn't realize that
mmc_card_uhs2() was using MMC_TIMING_UHS2_SPEED_A.
It looks like we should set MMC_TIMING_LEGACY, *after* calling
host->ops->uhs2_control(). Just to make sure we have the correct
initial state after powering-off.
I will fix this when applying, see more comments about that below.
>
> > host->ios.power_mode = MMC_POWER_OFF;
> > + host->uhs2_sd_tran = false;
> >
> > return host->ops->uhs2_control(host, UHS2_SET_IOS);
> > }
[...]
> > +static int sd_uhs2_init_card(struct mmc_host *host, struct mmc_card *oldcard)
> > {
> > struct mmc_card *card;
> > u32 node_id = 0;
> > @@ -131,33 +818,211 @@ 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);
> > + }
> >
> > card->uhs2_config.node_id = node_id;
> > card->type = MMC_TYPE_SD;
> >
> > err = sd_uhs2_config_read(host, card);
> > if (err)
> > - goto err;
> > + return err;
>
> This leaks card if !oldcard. Still need:
>
> err:
> if (!oldcard)
> mmc_remove_card(card);
>
>
> >
> > err = sd_uhs2_config_write(host, card);
> > if (err)
> > - goto err;
> > + return err;
>
> Ditto
>
> >
> > 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)
> > + return err;
>
> And here host->card is assigned but we have failed.
> The other card types do not set host->card until
> just before return 0. Then this can be goto err
> instead of return err.
In regards to the error path and how to handle the cleanup of the
mmc_card, indeed things are still not correct.
However, I realize these parts are somewhat a bit tricky to get right.
Therefore, I am simply going to leave this as is when applying and I
am going to send patches that fixes things on top.
In this way we can move things forward and avoid additional new
versions of the complete series to be sent.
> > + }
> > +
> > + host->uhs2_sd_tran = true;
> > +
> > + return 0;
> > +}
> > +
>
[...]
Kind regards
Uffe
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH V23 02/16] mmc: sdhci: add UHS-II related definitions in headers
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
2024-10-18 10:53 ` [PATCH V23 01/16] mmc: core: Support UHS-II card control and access Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 03/16] mmc: sdhci: add UHS-II module and add a kernel configuration Victor Shih
` (15 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
Add UHS-II related definitions in sdhci.h and sdhci-uhs2.h.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
Updates in V9:
- Modify the commit message.
Updates in V8:
- Use tabs instead of spaces.
Updates in V7:
- Reorder values and positions of definitions.
Updates in V6:
- Rename definitions.
- Use BIT() GENMASK() instead of bitwise operations.
---
drivers/mmc/host/sdhci-uhs2.h | 177 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.h | 54 ++++++++++-
2 files changed, 230 insertions(+), 1 deletion(-)
create mode 100644 drivers/mmc/host/sdhci-uhs2.h
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
new file mode 100644
index 000000000000..e993f41ffb7f
--- /dev/null
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * linux/drivers/mmc/host/sdhci-uhs2.h - Secure Digital Host Controller Interface driver
+ *
+ * Header file for Host Controller UHS2 related registers.
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ */
+#ifndef __SDHCI_UHS2_H
+#define __SDHCI_UHS2_H
+
+#include <linux/bits.h>
+
+/* SDHCI Category C registers : UHS2 usage */
+
+#define SDHCI_UHS2_CM_TRAN_RESP 0x10
+#define SDHCI_UHS2_SD_TRAN_RESP 0x18
+#define SDHCI_UHS2_SD_TRAN_RESP_1 0x1C
+
+/* SDHCI Category B registers : UHS2 only */
+
+#define SDHCI_UHS2_BLOCK_SIZE 0x80
+#define SDHCI_UHS2_MAKE_BLKSZ(dma, blksz) ((((dma) & 0x7) << 12) | ((blksz) & 0xFFF))
+
+#define SDHCI_UHS2_BLOCK_COUNT 0x84
+
+#define SDHCI_UHS2_CMD_PACKET 0x88
+#define SDHCI_UHS2_CMD_PACK_MAX_LEN 20
+
+#define SDHCI_UHS2_TRANS_MODE 0x9C
+#define SDHCI_UHS2_TRNS_DMA BIT(0)
+#define SDHCI_UHS2_TRNS_BLK_CNT_EN BIT(1)
+#define SDHCI_UHS2_TRNS_DATA_TRNS_WRT BIT(4)
+#define SDHCI_UHS2_TRNS_BLK_BYTE_MODE BIT(5)
+#define SDHCI_UHS2_TRNS_RES_R5 BIT(6)
+#define SDHCI_UHS2_TRNS_RES_ERR_CHECK_EN BIT(7)
+#define SDHCI_UHS2_TRNS_RES_INT_DIS BIT(8)
+#define SDHCI_UHS2_TRNS_WAIT_EBSY BIT(14)
+#define SDHCI_UHS2_TRNS_2L_HD BIT(15)
+
+#define SDHCI_UHS2_CMD 0x9E
+#define SDHCI_UHS2_CMD_SUB_CMD BIT(2)
+#define SDHCI_UHS2_CMD_DATA BIT(5)
+#define SDHCI_UHS2_CMD_TRNS_ABORT BIT(6)
+#define SDHCI_UHS2_CMD_CMD12 BIT(7)
+#define SDHCI_UHS2_CMD_DORMANT GENMASK(7, 6)
+#define SDHCI_UHS2_CMD_PACK_LEN_MASK GENMASK(12, 8)
+
+#define SDHCI_UHS2_RESPONSE 0xA0
+#define SDHCI_UHS2_RESPONSE_MAX_LEN 20
+
+#define SDHCI_UHS2_MSG_SELECT 0xB4
+#define SDHCI_UHS2_MSG_SELECT_CURR 0x0
+#define SDHCI_UHS2_MSG_SELECT_ONE 0x1
+#define SDHCI_UHS2_MSG_SELECT_TWO 0x2
+#define SDHCI_UHS2_MSG_SELECT_THREE 0x3
+
+#define SDHCI_UHS2_MSG 0xB8
+
+#define SDHCI_UHS2_DEV_INT_STATUS 0xBC
+
+#define SDHCI_UHS2_DEV_SELECT 0xBE
+#define SDHCI_UHS2_DEV_SEL_MASK GENMASK(3, 0)
+#define SDHCI_UHS2_DEV_SEL_INT_MSG_EN BIT(7)
+
+#define SDHCI_UHS2_DEV_INT_CODE 0xBF
+
+#define SDHCI_UHS2_SW_RESET 0xC0
+#define SDHCI_UHS2_SW_RESET_FULL BIT(0)
+#define SDHCI_UHS2_SW_RESET_SD BIT(1)
+
+#define SDHCI_UHS2_TIMER_CTRL 0xC2
+#define SDHCI_UHS2_TIMER_CTRL_DEADLOCK_MASK GENMASK(7, 4)
+
+#define SDHCI_UHS2_INT_STATUS 0xC4
+#define SDHCI_UHS2_INT_STATUS_ENABLE 0xC8
+#define SDHCI_UHS2_INT_SIGNAL_ENABLE 0xCC
+#define SDHCI_UHS2_INT_HEADER_ERR BIT(0)
+#define SDHCI_UHS2_INT_RES_ERR BIT(1)
+#define SDHCI_UHS2_INT_RETRY_EXP BIT(2)
+#define SDHCI_UHS2_INT_CRC BIT(3)
+#define SDHCI_UHS2_INT_FRAME_ERR BIT(4)
+#define SDHCI_UHS2_INT_TID_ERR BIT(5)
+#define SDHCI_UHS2_INT_UNRECOVER BIT(7)
+#define SDHCI_UHS2_INT_EBUSY_ERR BIT(8)
+#define SDHCI_UHS2_INT_ADMA_ERROR BIT(15)
+#define SDHCI_UHS2_INT_CMD_TIMEOUT BIT(16)
+#define SDHCI_UHS2_INT_DEADLOCK_TIMEOUT BIT(17)
+#define SDHCI_UHS2_INT_VENDOR_ERR BIT(27)
+#define SDHCI_UHS2_INT_ERROR_MASK ( \
+ SDHCI_UHS2_INT_HEADER_ERR | \
+ SDHCI_UHS2_INT_RES_ERR | \
+ SDHCI_UHS2_INT_RETRY_EXP | \
+ SDHCI_UHS2_INT_CRC | \
+ SDHCI_UHS2_INT_FRAME_ERR | \
+ SDHCI_UHS2_INT_TID_ERR | \
+ SDHCI_UHS2_INT_UNRECOVER | \
+ SDHCI_UHS2_INT_EBUSY_ERR | \
+ SDHCI_UHS2_INT_ADMA_ERROR | \
+ SDHCI_UHS2_INT_CMD_TIMEOUT | \
+ SDHCI_UHS2_INT_DEADLOCK_TIMEOUT)
+#define SDHCI_UHS2_INT_CMD_ERR_MASK ( \
+ SDHCI_UHS2_INT_HEADER_ERR | \
+ SDHCI_UHS2_INT_RES_ERR | \
+ SDHCI_UHS2_INT_FRAME_ERR | \
+ SDHCI_UHS2_INT_TID_ERR | \
+ SDHCI_UHS2_INT_CMD_TIMEOUT)
+/* CRC Error occurs during a packet receiving */
+#define SDHCI_UHS2_INT_DATA_ERR_MASK ( \
+ SDHCI_UHS2_INT_RETRY_EXP | \
+ SDHCI_UHS2_INT_CRC | \
+ SDHCI_UHS2_INT_UNRECOVER | \
+ SDHCI_UHS2_INT_EBUSY_ERR | \
+ SDHCI_UHS2_INT_ADMA_ERROR | \
+ SDHCI_UHS2_INT_DEADLOCK_TIMEOUT)
+
+#define SDHCI_UHS2_SETTINGS_PTR 0xE0
+#define SDHCI_UHS2_GEN_SETTINGS_POWER_LOW BIT(0)
+#define SDHCI_UHS2_GEN_SETTINGS_N_LANES_MASK GENMASK(11, 8)
+#define SDHCI_UHS2_FD_OR_2L_HD 0x0 /* 2 lanes */
+#define SDHCI_UHS2_2D1U_FD 0x2 /* 3 lanes, 2 down, 1 up, full duplex */
+#define SDHCI_UHS2_1D2U_FD 0x3 /* 3 lanes, 1 down, 2 up, full duplex */
+#define SDHCI_UHS2_2D2U_FD 0x4 /* 4 lanes, 2 down, 2 up, full duplex */
+
+#define SDHCI_UHS2_PHY_SET_SPEED_B BIT(6)
+#define SDHCI_UHS2_PHY_HIBERNATE_EN BIT(12)
+#define SDHCI_UHS2_PHY_N_LSS_SYN_MASK GENMASK(19, 16)
+#define SDHCI_UHS2_PHY_N_LSS_DIR_MASK GENMASK(23, 20)
+
+#define SDHCI_UHS2_TRAN_N_FCU_MASK GENMASK(15, 8)
+#define SDHCI_UHS2_TRAN_RETRY_CNT_MASK GENMASK(17, 16)
+#define SDHCI_UHS2_TRAN_1_N_DAT_GAP_MASK GENMASK(7, 0)
+
+#define SDHCI_UHS2_CAPS_PTR 0xE2
+#define SDHCI_UHS2_CAPS_OFFSET 0
+#define SDHCI_UHS2_CAPS_DAP_MASK GENMASK(3, 0)
+#define SDHCI_UHS2_CAPS_GAP_MASK GENMASK(7, 4)
+#define SDHCI_UHS2_CAPS_GAP(gap) ((gap) * 360)
+#define SDHCI_UHS2_CAPS_LANE_MASK GENMASK(13, 8)
+#define SDHCI_UHS2_CAPS_2L_HD_FD 1
+#define SDHCI_UHS2_CAPS_2D1U_FD 2
+#define SDHCI_UHS2_CAPS_1D2U_FD 4
+#define SDHCI_UHS2_CAPS_2D2U_FD 8
+#define SDHCI_UHS2_CAPS_ADDR_64 BIT(14)
+#define SDHCI_UHS2_CAPS_BOOT BIT(15)
+#define SDHCI_UHS2_CAPS_DEV_TYPE_MASK GENMASK(17, 16)
+#define SDHCI_UHS2_CAPS_DEV_TYPE_RMV 0
+#define SDHCI_UHS2_CAPS_DEV_TYPE_EMB 1
+#define SDHCI_UHS2_CAPS_DEV_TYPE_EMB_RMV 2
+#define SDHCI_UHS2_CAPS_NUM_DEV_MASK GENMASK(21, 18)
+#define SDHCI_UHS2_CAPS_BUS_TOPO_MASK GENMASK(23, 22)
+#define SDHCI_UHS2_CAPS_BUS_TOPO_SHIFT 22
+#define SDHCI_UHS2_CAPS_BUS_TOPO_P2P 0
+#define SDHCI_UHS2_CAPS_BUS_TOPO_RING 1
+#define SDHCI_UHS2_CAPS_BUS_TOPO_HUB 2
+#define SDHCI_UHS2_CAPS_BUS_TOPO_HUB_RING 3
+
+#define SDHCI_UHS2_CAPS_PHY_OFFSET 4
+#define SDHCI_UHS2_CAPS_PHY_REV_MASK GENMASK(5, 0)
+#define SDHCI_UHS2_CAPS_PHY_RANGE_MASK GENMASK(7, 6)
+#define SDHCI_UHS2_CAPS_PHY_RANGE_A 0
+#define SDHCI_UHS2_CAPS_PHY_RANGE_B 1
+#define SDHCI_UHS2_CAPS_PHY_N_LSS_SYN_MASK GENMASK(19, 16)
+#define SDHCI_UHS2_CAPS_PHY_N_LSS_DIR_MASK GENMASK(23, 20)
+#define SDHCI_UHS2_CAPS_TRAN_OFFSET 8
+#define SDHCI_UHS2_CAPS_TRAN_LINK_REV_MASK GENMASK(5, 0)
+#define SDHCI_UHS2_CAPS_TRAN_N_FCU_MASK GENMASK(15, 8)
+#define SDHCI_UHS2_CAPS_TRAN_HOST_TYPE_MASK GENMASK(18, 16)
+#define SDHCI_UHS2_CAPS_TRAN_BLK_LEN_MASK GENMASK(31, 20)
+
+#define SDHCI_UHS2_CAPS_TRAN_1_OFFSET 12
+#define SDHCI_UHS2_CAPS_TRAN_1_N_DATA_GAP_MASK GENMASK(7, 0)
+
+#define SDHCI_UHS2_EMBED_CTRL_PTR 0xE6
+#define SDHCI_UHS2_VENDOR_PTR 0xE8
+
+#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index f531b617f28d..e1013925e91e 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -43,8 +43,23 @@
#define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20
+/*
+ * Defined in Host Version 4.0.
+ */
+#define SDHCI_TRNS_RES_TYPE 0x40
+#define SDHCI_TRNS_RES_ERR_CHECK 0x80
+#define SDHCI_TRNS_RES_INT_DIS 0x0100
+
#define SDHCI_COMMAND 0x0E
#define SDHCI_CMD_RESP_MASK 0x03
+
+/*
+ * Host Version 4.10 adds this bit to distinguish a main command or
+ * sub command.
+ * For example with SDIO, CMD52 (sub command) issued during CMD53 (main command).
+ */
+#define SDHCI_CMD_SUB_CMD 0x04
+
#define SDHCI_CMD_CRC 0x08
#define SDHCI_CMD_INDEX 0x10
#define SDHCI_CMD_DATA 0x20
@@ -65,6 +80,9 @@
#define SDHCI_PRESENT_STATE 0x24
#define SDHCI_CMD_INHIBIT 0x00000001
#define SDHCI_DATA_INHIBIT 0x00000002
+
+#define SDHCI_DAT_4_TO_7_LVL_MASK 0x000000F0
+
#define SDHCI_DOING_WRITE 0x00000100
#define SDHCI_DOING_READ 0x00000200
#define SDHCI_SPACE_AVAILABLE 0x00000400
@@ -80,6 +98,15 @@
#define SDHCI_DATA_0_LVL_MASK 0x00100000
#define SDHCI_CMD_LVL 0x01000000
+/* Host Version 4.10 */
+
+#define SDHCI_HOST_REGULATOR_STABLE 0x02000000
+#define SDHCI_CMD_NOT_ISSUED_ERR 0x08000000
+#define SDHCI_SUB_CMD_STATUS 0x10000000
+#define SDHCI_UHS2_IN_DORMANT_STATE 0x20000000
+#define SDHCI_UHS2_LANE_SYNC 0x40000000
+#define SDHCI_UHS2_IF_DETECT 0x80000000
+
#define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01
#define SDHCI_CTRL_4BITBUS 0x02
@@ -117,7 +144,7 @@
#define SDHCI_CLOCK_CONTROL 0x2C
#define SDHCI_DIVIDER_SHIFT 8
#define SDHCI_DIVIDER_HI_SHIFT 6
-#define SDHCI_DIV_MASK 0xFF
+#define SDHCI_DIV_MASK 0xFF
#define SDHCI_DIV_MASK_LEN 8
#define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020
@@ -146,6 +173,10 @@
#define SDHCI_INT_CARD_REMOVE 0x00000080
#define SDHCI_INT_CARD_INT 0x00000100
#define SDHCI_INT_RETUNE 0x00001000
+
+/* Host Version 4.10 */
+#define SDHCI_INT_FX_EVENT 0x00002000
+
#define SDHCI_INT_CQE 0x00004000
#define SDHCI_INT_ERROR 0x00008000
#define SDHCI_INT_TIMEOUT 0x00010000
@@ -160,6 +191,9 @@
#define SDHCI_INT_ADMA_ERROR 0x02000000
#define SDHCI_INT_TUNING_ERROR 0x04000000
+/* Host Version 4.0 */
+#define SDHCI_INT_RESP_ERR 0x08000000
+
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
@@ -186,6 +220,9 @@
#define SDHCI_AUTO_CMD_END_BIT 0x00000008
#define SDHCI_AUTO_CMD_INDEX 0x00000010
+/* Host Version 4.10 */
+#define SDHCI_AUTO_CMD_RESP_ERR 0x0020
+
#define SDHCI_HOST_CONTROL2 0x3E
#define SDHCI_CTRL_UHS_MASK 0x0007
#define SDHCI_CTRL_UHS_SDR12 0x0000
@@ -194,6 +231,7 @@
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
+#define SDHCI_CTRL_UHS2 0x0007
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
@@ -202,9 +240,12 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
+#define SDHCI_CTRL_UHS2_ENABLE 0x0100
+#define SDHCI_CTRL_ADMA2_LEN_MODE 0x0400
#define SDHCI_CMD23_ENABLE 0x0800
#define SDHCI_CTRL_V4_MODE 0x1000
#define SDHCI_CTRL_64BIT_ADDR 0x2000
+#define SDHCI_CTRL_ASYNC_INT_ENABLE 0x4000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
@@ -227,11 +268,13 @@
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT_V4 0x08000000
#define SDHCI_CAN_64BIT 0x10000000
+#define SDHCI_CAN_ASYNC_INT 0x20000000
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
+#define SDHCI_SUPPORT_UHS2 0x00000008
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
@@ -240,6 +283,7 @@
#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
#define SDHCI_CAN_DO_ADMA3 0x08000000
+#define SDHCI_CAN_VDD2_180 0x10000000 /* UHS-2 1.8V VDD2 */
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_MAX_CURRENT 0x48
@@ -247,11 +291,14 @@
#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
+#define SDHCI_MAX_CURRENT_1 0x4C
+#define SDHCI_MAX_CURRENT_VDD2_180_MASK GENMASK(7, 0) /* UHS2 */
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */
#define SDHCI_SET_ACMD12_ERROR 0x50
+/* Host Version 4.10 */
#define SDHCI_SET_INT_ERROR 0x52
#define SDHCI_ADMA_ERROR 0x54
@@ -270,10 +317,15 @@
#define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E
#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
+
+/* UHS2 */
+#define SDHCI_PRESET_FOR_UHS2 0x74
#define SDHCI_PRESET_DRV_MASK GENMASK(15, 14)
#define SDHCI_PRESET_CLKGEN_SEL BIT(10)
#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
+#define SDHCI_ADMA3_ADDRESS 0x78
+
#define SDHCI_SLOT_INT_STATUS 0xFC
#define SDHCI_HOST_VERSION 0xFE
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 03/16] mmc: sdhci: add UHS-II module and add a kernel configuration
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
2024-10-18 10:53 ` [PATCH V23 01/16] mmc: core: Support UHS-II card control and access Victor Shih
2024-10-18 10:53 ` [PATCH V23 02/16] mmc: sdhci: add UHS-II related definitions in headers Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-29 13:56 ` Geert Uytterhoeven
2024-10-18 10:53 ` [PATCH V23 04/16] mmc: sdhci-uhs2: dump UHS-II registers Victor Shih
` (14 subsequent siblings)
17 siblings, 1 reply; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This patch adds sdhci-uhs2.c as a module for UHS-II support.
This is a skeleton for further development in this patch series.
This kernel configuration, CONFIG_MMC_SDHCI_UHS2, will be used
in the following commits to indicate UHS-II specific code in sdhci
controllers.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V22:
- Remove defined but not used code.
Updates in V9:
- Modify the commit message.
Updates in V8:
- Modify MODULE_LICENSE from "GPL v2" to "GPL".
Updates in V6:
- Merage V5 of patch[7] and patch[9] in to V6 of patch[8].
---
drivers/mmc/host/Kconfig | 9 ++++++++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-uhs2.c | 41 +++++++++++++++++++++++++++++++++++
3 files changed, 51 insertions(+)
create mode 100644 drivers/mmc/host/sdhci-uhs2.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 0ba5a9f769fb..45df8ddb8918 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -98,6 +98,15 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
This is the case for the Nintendo Wii SDHCI.
+config MMC_SDHCI_UHS2
+ tristate "UHS2 support on SDHCI controller"
+ depends on MMC_SDHCI
+ help
+ This option is selected by SDHCI controller drivers that want to
+ support UHS2-capable devices.
+
+ If you have a controller with this feature, say Y or M here.
+
config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3ccffebbe59b..5147467ec825 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
+obj-$(CONFIG_MMC_SDHCI_UHS2) += sdhci-uhs2.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
new file mode 100644
index 000000000000..9ec1d6abb47c
--- /dev/null
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/drivers/mmc/host/sdhci_uhs2.c - Secure Digital Host Controller
+ * Interface driver
+ *
+ * Copyright (C) 2014 Intel Corp, All Rights Reserved.
+ * 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>
+ */
+
+#include <linux/module.h>
+
+#include "sdhci.h"
+#include "sdhci-uhs2.h"
+
+#define DRIVER_NAME "sdhci_uhs2"
+#define DBG(f, x...) \
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init sdhci_uhs2_mod_init(void)
+{
+ return 0;
+}
+module_init(sdhci_uhs2_mod_init);
+
+static void __exit sdhci_uhs2_mod_exit(void)
+{
+}
+module_exit(sdhci_uhs2_mod_exit);
+
+MODULE_AUTHOR("Intel, Genesys Logic, Linaro");
+MODULE_DESCRIPTION("MMC UHS-II Support");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH V23 03/16] mmc: sdhci: add UHS-II module and add a kernel configuration
2024-10-18 10:53 ` [PATCH V23 03/16] mmc: sdhci: add UHS-II module and add a kernel configuration Victor Shih
@ 2024-10-29 13:56 ` Geert Uytterhoeven
2024-11-01 10:39 ` Victor Shih
0 siblings, 1 reply; 27+ messages in thread
From: Geert Uytterhoeven @ 2024-10-29 13:56 UTC (permalink / raw)
To: Victor Shih
Cc: ulf.hansson, adrian.hunter, linux-mmc, linux-kernel, benchuanggli,
Lucas.Lai, HL.Liu, Greg.tu, dlunev, Ben Chuang, AKASHI Takahiro,
Victor Shih
Hi Victor,
On Fri, Oct 18, 2024 at 1:14 PM Victor Shih <victorshihgli@gmail.com> wrote:
> From: Victor Shih <victor.shih@genesyslogic.com.tw>
>
> This patch adds sdhci-uhs2.c as a module for UHS-II support.
> This is a skeleton for further development in this patch series.
>
> This kernel configuration, CONFIG_MMC_SDHCI_UHS2, will be used
> in the following commits to indicate UHS-II specific code in sdhci
> controllers.
>
> Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
Thanks for your patch, which is now commit 2af7dd8b64f2fd6a ("mmc:
sdhci: add UHS-II module and add a kernel configuration") in
linux-next/master mmc/next next-20241025 next-20241028 next-20241029
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-uhs2.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * linux/drivers/mmc/host/sdhci_uhs2.c - Secure Digital Host Controller
> + * Interface driver
> + *
> + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> + * 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>
That is a very extensive copyright header, for just a small piece of
boilerplate code?
> + */
> +
> +#include <linux/module.h>
> +
> +#include "sdhci.h"
> +#include "sdhci-uhs2.h"
> +
> +#define DRIVER_NAME "sdhci_uhs2"
> +#define DBG(f, x...) \
> + pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
> +
> +/*****************************************************************************\
> + * *
> + * Driver init/exit *
> + * *
> +\*****************************************************************************/
> +
> +static int __init sdhci_uhs2_mod_init(void)
> +{
> + return 0;
> +}
> +module_init(sdhci_uhs2_mod_init);
> +
> +static void __exit sdhci_uhs2_mod_exit(void)
> +{
> +}
> +module_exit(sdhci_uhs2_mod_exit);
> +
> +MODULE_AUTHOR("Intel, Genesys Logic, Linaro");
> +MODULE_DESCRIPTION("MMC UHS-II Support");
> +MODULE_LICENSE("GPL");
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH V23 03/16] mmc: sdhci: add UHS-II module and add a kernel configuration
2024-10-29 13:56 ` Geert Uytterhoeven
@ 2024-11-01 10:39 ` Victor Shih
0 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-11-01 10:39 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: ulf.hansson, adrian.hunter, linux-mmc, linux-kernel, benchuanggli,
Lucas.Lai, HL.Liu, Greg.tu, dlunev, Ben Chuang, AKASHI Takahiro,
Victor Shih
On Tue, Oct 29, 2024 at 9:56 PM Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>
> Hi Victor,
>
> On Fri, Oct 18, 2024 at 1:14 PM Victor Shih <victorshihgli@gmail.com> wrote:
> > From: Victor Shih <victor.shih@genesyslogic.com.tw>
> >
> > This patch adds sdhci-uhs2.c as a module for UHS-II support.
> > This is a skeleton for further development in this patch series.
> >
> > This kernel configuration, CONFIG_MMC_SDHCI_UHS2, will be used
> > in the following commits to indicate UHS-II specific code in sdhci
> > controllers.
> >
> > Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
>
> Thanks for your patch, which is now commit 2af7dd8b64f2fd6a ("mmc:
> sdhci: add UHS-II module and add a kernel configuration") in
> linux-next/master mmc/next next-20241025 next-20241028 next-20241029
>
> > --- /dev/null
> > +++ b/drivers/mmc/host/sdhci-uhs2.c
> > @@ -0,0 +1,41 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * linux/drivers/mmc/host/sdhci_uhs2.c - Secure Digital Host Controller
> > + * Interface driver
> > + *
> > + * Copyright (C) 2014 Intel Corp, All Rights Reserved.
> > + * 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>
>
> That is a very extensive copyright header, for just a small piece of
> boilerplate code?
>
Hi, Geert
As I know, the copyright is for the complete sdhci-uhs2.c file
and subsequent patches added the code into the sdhci-uhs2.c file.
Thanks, Victor Shih
> > + */
> > +
> > +#include <linux/module.h>
> > +
> > +#include "sdhci.h"
> > +#include "sdhci-uhs2.h"
> > +
> > +#define DRIVER_NAME "sdhci_uhs2"
> > +#define DBG(f, x...) \
> > + pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
> > +
> > +/*****************************************************************************\
> > + * *
> > + * Driver init/exit *
> > + * *
> > +\*****************************************************************************/
> > +
> > +static int __init sdhci_uhs2_mod_init(void)
> > +{
> > + return 0;
> > +}
> > +module_init(sdhci_uhs2_mod_init);
> > +
> > +static void __exit sdhci_uhs2_mod_exit(void)
> > +{
> > +}
> > +module_exit(sdhci_uhs2_mod_exit);
> > +
> > +MODULE_AUTHOR("Intel, Genesys Logic, Linaro");
> > +MODULE_DESCRIPTION("MMC UHS-II Support");
> > +MODULE_LICENSE("GPL");
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH V23 04/16] mmc: sdhci-uhs2: dump UHS-II registers
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (2 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 03/16] mmc: sdhci: add UHS-II module and add a kernel configuration Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 05/16] mmc: sdhci-uhs2: add reset function Victor Shih
` (13 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
Dump UHS-II specific registers, if available, in sdhci_dumpregs()
for informative/debugging use.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V14:
- Use mmc_card_uhs2() to stead sdhci_uhs2_mode() in the
sdhci_uhs2_dump_regs().
Updates in V7:
- Use sdhci_uhs2_mode() to simplify code.
Updates in V6:
- Remove unnecessary code.
---
drivers/mmc/host/sdhci-uhs2.c | 30 ++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 4 ++++
drivers/mmc/host/sdhci.c | 3 +++
drivers/mmc/host/sdhci.h | 1 +
4 files changed, 38 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 9ec1d6abb47c..14514710e763 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -18,6 +18,36 @@
#define DRIVER_NAME "sdhci_uhs2"
#define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+#define SDHCI_UHS2_DUMP(f, x...) \
+ pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+
+void sdhci_uhs2_dump_regs(struct sdhci_host *host)
+{
+ if (!(mmc_card_uhs2(host->mmc)))
+ return;
+
+ SDHCI_UHS2_DUMP("==================== UHS2 ==================\n");
+ SDHCI_UHS2_DUMP("Blk Size: 0x%08x | Blk Cnt: 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_BLOCK_SIZE),
+ sdhci_readl(host, SDHCI_UHS2_BLOCK_COUNT));
+ SDHCI_UHS2_DUMP("Cmd: 0x%08x | Trn mode: 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_CMD),
+ sdhci_readw(host, SDHCI_UHS2_TRANS_MODE));
+ SDHCI_UHS2_DUMP("Int Stat: 0x%08x | Dev Sel : 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_DEV_INT_STATUS),
+ sdhci_readb(host, SDHCI_UHS2_DEV_SELECT));
+ SDHCI_UHS2_DUMP("Dev Int Code: 0x%08x\n",
+ sdhci_readb(host, SDHCI_UHS2_DEV_INT_CODE));
+ SDHCI_UHS2_DUMP("Reset: 0x%08x | Timer: 0x%08x\n",
+ sdhci_readw(host, SDHCI_UHS2_SW_RESET),
+ sdhci_readw(host, SDHCI_UHS2_TIMER_CTRL));
+ SDHCI_UHS2_DUMP("ErrInt: 0x%08x | ErrIntEn: 0x%08x\n",
+ sdhci_readl(host, SDHCI_UHS2_INT_STATUS),
+ sdhci_readl(host, SDHCI_UHS2_INT_STATUS_ENABLE));
+ SDHCI_UHS2_DUMP("ErrSigEn: 0x%08x\n",
+ sdhci_readl(host, SDHCI_UHS2_INT_SIGNAL_ENABLE));
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
/*****************************************************************************\
* *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index e993f41ffb7f..2bfe18d29bca 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -174,4 +174,8 @@
#define SDHCI_UHS2_EMBED_CTRL_PTR 0xE6
#define SDHCI_UHS2_VENDOR_PTR 0xE8
+struct sdhci_host;
+
+void sdhci_uhs2_dump_regs(struct sdhci_host *host);
+
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 4b91c9e96635..5a5fe3528bb4 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -110,6 +110,9 @@ void sdhci_dumpregs(struct sdhci_host *host)
}
}
+ if (host->ops->dump_uhs2_regs)
+ host->ops->dump_uhs2_regs(host);
+
if (host->ops->dump_vendor_regs)
host->ops->dump_vendor_regs(host);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index e1013925e91e..66ab90bd4017 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -719,6 +719,7 @@ struct sdhci_ops {
void (*request_done)(struct sdhci_host *host,
struct mmc_request *mrq);
void (*dump_vendor_regs)(struct sdhci_host *host);
+ void (*dump_uhs2_regs)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 05/16] mmc: sdhci-uhs2: add reset function
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (3 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 04/16] mmc: sdhci-uhs2: dump UHS-II registers Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 06/16] mmc: sdhci-uhs2: add set_power() to support vdd2 Victor Shih
` (12 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
Sdhci_uhs2_reset() does a UHS-II specific reset operation.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V17:
- Move the sdhci_uhs2_reset_cmd_data() to the other patch it was used
for the first time.
Updates in V16:
- Add sdhci_uhs2_reset_cmd_data() function.
Updates in V15:
- Refer the SD Host Controller Standard Specification Section 3.10
to add reset command data mechanism.
Updates in V14:
- Since mmc_card_uhs2() is the same as sdhci_uhs2_mode(), so drop
sdhci_uhs2_mode() and use mmc_card_uhs2() instead of sdhci_uhs2_mode().
Updates in V13:
- Use ios timing to stead MMC_UHS2_SUPPORT for indicate the UHS2 mode.
Updates in V8:
- Adjust the position of matching brackets.
Updates in V6:
- Remove unnecessary functions and simplify code.
---
drivers/mmc/host/sdhci-uhs2.c | 37 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 1 +
2 files changed, 38 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 14514710e763..71c60952a40a 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -10,7 +10,9 @@
* Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
*/
+#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/iopoll.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -21,6 +23,8 @@
#define SDHCI_UHS2_DUMP(f, x...) \
pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+#define UHS2_RESET_TIMEOUT_100MS 100000
+
void sdhci_uhs2_dump_regs(struct sdhci_host *host)
{
if (!(mmc_card_uhs2(host->mmc)))
@@ -49,6 +53,39 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
+/*****************************************************************************\
+ * *
+ * Low level functions *
+ * *
+\*****************************************************************************/
+
+/**
+ * sdhci_uhs2_reset - invoke SW reset
+ * @host: SDHCI host
+ * @mask: Control mask
+ *
+ * Invoke SW reset, depending on a bit in @mask and wait for completion.
+ */
+void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
+{
+ u32 val;
+
+ sdhci_writew(host, mask, SDHCI_UHS2_SW_RESET);
+
+ if (mask & SDHCI_UHS2_SW_RESET_FULL)
+ host->clock = 0;
+
+ /* hw clears the bit when it's done */
+ if (read_poll_timeout_atomic(sdhci_readw, val, !(val & mask), 10,
+ UHS2_RESET_TIMEOUT_100MS, true, host, SDHCI_UHS2_SW_RESET)) {
+ pr_warn("%s: %s: Reset 0x%x never completed. %s: clean reset bit.\n", __func__,
+ mmc_hostname(host->mmc), (int)mask, mmc_hostname(host->mmc));
+ sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 2bfe18d29bca..caaf9fba4975 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -177,5 +177,6 @@
struct sdhci_host;
void sdhci_uhs2_dump_regs(struct sdhci_host *host);
+void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
#endif /* __SDHCI_UHS2_H */
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 06/16] mmc: sdhci-uhs2: add set_power() to support vdd2
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (4 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 05/16] mmc: sdhci-uhs2: add reset function Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 07/16] mmc: sdhci-uhs2: add set_timeout() Victor Shih
` (11 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This is a UHS-II version of sdhci's set_power operation.
Use sdhci_uhs2_set_power() to set VDD2 for support UHS2 interface.
VDD2, as well as VDD, is handled here.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V17:
- Export sdhci_uhs2_set_power() function.
Updates in V13:
- Drop use vmmc2.
- Modify comment message.
Updates in V10:
- Move some definitions of PatchV9[05/23] to PatchV10[11/23].
Updates in V9:
- Modify annotations in sdhci_get_vdd_value().
Updates in V8:
- Adjust the position of matching brackets.
- Add the initial value of the pwr in sdhci_uhs2_set_power().
Updates in V7:
- Add clear the power reg before setting a new value
in sdhci_uhs2_set_power().
- Add MMC_VDD_34_35 case and MMC_VDD_35_36 case in sdhci_get_vdd_value().
- Drop pwr variable in sdhci_get_vdd_value().
Updates in V6:
- Add mmc_opt_regulator_set_ocr().
- Remove unnecessary functions.
---
drivers/mmc/host/sdhci-uhs2.c | 49 ++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 1 +
drivers/mmc/host/sdhci.c | 61 +++++++++++++++++++----------------
drivers/mmc/host/sdhci.h | 1 +
4 files changed, 84 insertions(+), 28 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 71c60952a40a..756e44d84b87 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -59,6 +59,13 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
* *
\*****************************************************************************/
+static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
+ struct regulator *supply,
+ unsigned short vdd_bit)
+{
+ return IS_ERR_OR_NULL(supply) ? 0 : mmc_regulator_set_ocr(mmc, supply, vdd_bit);
+}
+
/**
* sdhci_uhs2_reset - invoke SW reset
* @host: SDHCI host
@@ -86,6 +93,48 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
+void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
+{
+ struct mmc_host *mmc = host->mmc;
+ u8 pwr = 0;
+
+ if (mode != MMC_POWER_OFF) {
+ pwr = sdhci_get_vdd_value(vdd);
+ if (!pwr)
+ WARN(1, "%s: Invalid vdd %#x\n",
+ mmc_hostname(host->mmc), vdd);
+ pwr |= SDHCI_VDD2_POWER_180;
+ }
+
+ if (host->pwr == pwr)
+ return;
+ host->pwr = pwr;
+
+ if (pwr == 0) {
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+ mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ mmc_regulator_set_vqmmc2(mmc, &mmc->ios);
+ } else {
+ mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+ /* support 1.8v only for now */
+ mmc_regulator_set_vqmmc2(mmc, &mmc->ios);
+
+ /* Clear the power reg before setting a new value */
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+ /* vdd first */
+ pwr |= SDHCI_POWER_ON;
+ sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
+ mdelay(5);
+
+ pwr |= SDHCI_VDD2_POWER_ON;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ mdelay(5);
+ }
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index caaf9fba4975..3efa8dd690bf 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -178,5 +178,6 @@ struct sdhci_host;
void sdhci_uhs2_dump_regs(struct sdhci_host *host);
void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
+void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 5a5fe3528bb4..366c3d30dba6 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -23,7 +23,7 @@
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
-
+#include <linux/bug.h>
#include <linux/leds.h>
#include <linux/mmc/mmc.h>
@@ -2061,41 +2061,46 @@ static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode,
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
}
+unsigned short sdhci_get_vdd_value(unsigned short vdd)
+{
+ switch (1 << vdd) {
+ case MMC_VDD_165_195:
+ /*
+ * Without a regulator, SDHCI does not support 2.0v
+ * so we only get here if the driver deliberately
+ * added the 2.0v range to ocr_avail. Map it to 1.8v
+ * for the purpose of turning on the power.
+ */
+ case MMC_VDD_20_21:
+ return SDHCI_POWER_180;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ return SDHCI_POWER_300;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ /*
+ * 3.4V ~ 3.6V are valid only for those platforms where it's
+ * known that the voltage range is supported by hardware.
+ */
+ case MMC_VDD_34_35:
+ case MMC_VDD_35_36:
+ return SDHCI_POWER_330;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(sdhci_get_vdd_value);
+
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd)
{
u8 pwr = 0;
if (mode != MMC_POWER_OFF) {
- switch (1 << vdd) {
- case MMC_VDD_165_195:
- /*
- * Without a regulator, SDHCI does not support 2.0v
- * so we only get here if the driver deliberately
- * added the 2.0v range to ocr_avail. Map it to 1.8v
- * for the purpose of turning on the power.
- */
- case MMC_VDD_20_21:
- pwr = SDHCI_POWER_180;
- break;
- case MMC_VDD_29_30:
- case MMC_VDD_30_31:
- pwr = SDHCI_POWER_300;
- break;
- case MMC_VDD_32_33:
- case MMC_VDD_33_34:
- /*
- * 3.4 ~ 3.6V are valid only for those platforms where it's
- * known that the voltage range is supported by hardware.
- */
- case MMC_VDD_34_35:
- case MMC_VDD_35_36:
- pwr = SDHCI_POWER_330;
- break;
- default:
+ pwr = sdhci_get_vdd_value(vdd);
+ if (!pwr) {
WARN(1, "%s: Invalid vdd %#x\n",
mmc_hostname(host->mmc), vdd);
- break;
}
}
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 66ab90bd4017..0f78708d0c70 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -836,6 +836,7 @@ void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
unsigned char mode,
unsigned short vdd);
+unsigned short sdhci_get_vdd_value(unsigned short vdd);
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
int sdhci_get_cd_nogpio(struct mmc_host *mmc);
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 07/16] mmc: sdhci-uhs2: add set_timeout()
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (5 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 06/16] mmc: sdhci-uhs2: add set_power() to support vdd2 Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 08/16] mmc: sdhci-uhs2: add add_host() and others to set up the driver Victor Shih
` (10 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This is a UHS-II version of sdhci's set_timeout() operation.
Use sdhci_uhs2_set_timeout() to set and calculate the timeout time.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V14:
- Use mmc_card_uhs2() to stead sdhci_uhs2_mode() in the
sdhci_uhs2_set_timeout().
Updates in V13:
- Modify comment message.
Updates in V8:
- Initialization be combined with declaration and realigned
in sdhci_calc_timeout_uhs2().
- Forward declare struct mmc_command in sdhci_uhs2.h.
Updates in V6:
- Use GENMASK() and FIELD_PREP() in some case.
- Use sdhci_uhs2_mode() to simplify code.
- Remove unnecessary functions.
---
drivers/mmc/host/sdhci-uhs2.c | 72 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
2 files changed, 74 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 756e44d84b87..6b249eb8395a 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/iopoll.h>
+#include <linux/bitfield.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -135,6 +136,77 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);
+static u8 sdhci_calc_timeout_uhs2(struct sdhci_host *host, u8 *cmd_res, u8 *dead_lock)
+{
+ /* timeout in us */
+ unsigned int dead_lock_timeout = 1 * 1000 * 1000;
+ unsigned int cmd_res_timeout = 5 * 1000;
+ unsigned int current_timeout;
+ u8 count;
+
+ /*
+ * Figure out needed cycles.
+ * We do this in steps in order to fit inside a 32 bit int.
+ * The first step is the minimum timeout, which will have a
+ * minimum resolution of 6 bits:
+ * (1) 2^13*1000 > 2^22,
+ * (2) host->timeout_clk < 2^16
+ * =>
+ * (1) / (2) > 2^6
+ */
+ count = 0;
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ while (current_timeout < cmd_res_timeout) {
+ count++;
+ current_timeout <<= 1;
+ if (count >= 0xF)
+ break;
+ }
+
+ if (count >= 0xF) {
+ DBG("%s: Too large timeout 0x%x requested for CMD_RES!\n",
+ mmc_hostname(host->mmc), count);
+ count = 0xE;
+ }
+ *cmd_res = count;
+
+ count = 0;
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ while (current_timeout < dead_lock_timeout) {
+ count++;
+ current_timeout <<= 1;
+ if (count >= 0xF)
+ break;
+ }
+
+ if (count >= 0xF) {
+ DBG("%s: Too large timeout 0x%x requested for DEADLOCK!\n",
+ mmc_hostname(host->mmc), count);
+ count = 0xE;
+ }
+ *dead_lock = count;
+
+ return count;
+}
+
+static void __sdhci_uhs2_set_timeout(struct sdhci_host *host)
+{
+ u8 cmd_res, dead_lock;
+
+ sdhci_calc_timeout_uhs2(host, &cmd_res, &dead_lock);
+ cmd_res |= FIELD_PREP(SDHCI_UHS2_TIMER_CTRL_DEADLOCK_MASK, dead_lock);
+ sdhci_writeb(host, cmd_res, SDHCI_UHS2_TIMER_CTRL);
+}
+
+void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ __sdhci_set_timeout(host, cmd);
+
+ if (mmc_card_uhs2(host->mmc))
+ __sdhci_uhs2_set_timeout(host);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_set_timeout);
+
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 3efa8dd690bf..9bd3f610cf8c 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -175,9 +175,11 @@
#define SDHCI_UHS2_VENDOR_PTR 0xE8
struct sdhci_host;
+struct mmc_command;
void sdhci_uhs2_dump_regs(struct sdhci_host *host);
void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd);
+void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
#endif /* __SDHCI_UHS2_H */
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 08/16] mmc: sdhci-uhs2: add add_host() and others to set up the driver
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (6 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 07/16] mmc: sdhci-uhs2: add set_timeout() Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 09/16] mmc: sdhci-uhs2: add set_ios() Victor Shih
` (9 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This is a UHS-II version of sdhci's add_host/remove_host operation.
Any sdhci drivers which are capable of handling UHS-II cards must
call those functions instead of the corresponding sdhci's.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V22:
- Adjust the patch order from v21 patch#18 to v22 patch#14 and
remove unnecessary code to avoid defined but not used warnings.
Updates in V14:
- Use mmc_card_uhs2() to stead sdhci_uhs2_mode() in the
__sdhci_uhs2_remove_host().
Updates in V13:
- Re-order function to avoid declaration.
- Use vqmmc2 to stead vmmc2.
Updates in V11:
- Remove unused ocr_avail_uhs2.
Updates in V10:
- Move some definitions of PatchV9[05/23] to PatchV10[20/23].
Updates in V8:
- Change return type to void for __sdhci_uhs2_add_host_v4().
- Remove unused variables in __sdhci_uhs2_add_host_v4().
Updates in V7:
- __sdhci_add_host() to instead of __sdhci_uhs2_add_host()
in sdhci_uhs2_add_host().
- Cancel export state of some functions.
Updates in V6:
- Add complete_work_fn/thread_irq_fn variables in struct sdhci_host.
- Use complete_work_fn/thread_irq_fn variables in
sdhci_alloc_host() and sdhci_uhs2_add_host().
- Use sdhci_uhs2_mode() to simplify code in __sdhci_uhs2_remove_host().
---
drivers/mmc/host/sdhci-uhs2.c | 91 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
2 files changed, 93 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 6b249eb8395a..d3af620c7b68 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/iopoll.h>
#include <linux/bitfield.h>
+#include <linux/regulator/consumer.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -224,6 +225,96 @@ static void __exit sdhci_uhs2_mod_exit(void)
}
module_exit(sdhci_uhs2_mod_exit);
+/*****************************************************************************\
+ *
+ * Device allocation/registration *
+ * *
+\*****************************************************************************/
+
+static void __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
+{
+ struct mmc_host *mmc;
+ u32 max_current_caps2;
+
+ mmc = host->mmc;
+
+ /* Support UHS2 */
+ if (caps1 & SDHCI_SUPPORT_UHS2)
+ mmc->caps2 |= MMC_CAP2_SD_UHS2;
+
+ max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
+
+ if ((caps1 & SDHCI_CAN_VDD2_180) &&
+ !max_current_caps2 &&
+ !IS_ERR(mmc->supply.vqmmc2)) {
+ /* UHS2 - VDD2 */
+ int curr = regulator_get_current_limit(mmc->supply.vqmmc2);
+
+ if (curr > 0) {
+ /* convert to SDHCI_MAX_CURRENT format */
+ curr = curr / 1000; /* convert to mA */
+ curr = curr / SDHCI_MAX_CURRENT_MULTIPLIER;
+ curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
+ max_current_caps2 = curr;
+ }
+ }
+
+ if (!(caps1 & SDHCI_CAN_VDD2_180))
+ mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
+}
+
+static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
+{
+ if (!mmc_card_uhs2(host->mmc))
+ return;
+
+ if (!dead)
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
+}
+
+int sdhci_uhs2_add_host(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ int ret;
+
+ ret = sdhci_setup_host(host);
+ if (ret)
+ return ret;
+
+ if (host->version >= SDHCI_SPEC_400)
+ __sdhci_uhs2_add_host_v4(host, host->caps1);
+
+ if ((mmc->caps2 & MMC_CAP2_SD_UHS2) && !host->v4_mode)
+ /* host doesn't want to enable UHS2 support */
+ mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
+
+ /* LED support not implemented for UHS2 */
+ host->quirks |= SDHCI_QUIRK_NO_LED;
+
+ ret = __sdhci_add_host(host);
+ if (ret)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ if (host->version >= SDHCI_SPEC_400)
+ __sdhci_uhs2_remove_host(host, 0);
+
+ sdhci_cleanup_host(host);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_add_host);
+
+void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
+{
+ __sdhci_uhs2_remove_host(host, dead);
+
+ sdhci_remove_host(host, dead);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_remove_host);
+
MODULE_AUTHOR("Intel, Genesys Logic, Linaro");
MODULE_DESCRIPTION("MMC UHS-II Support");
MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 9bd3f610cf8c..0e1866a6bdb6 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -181,5 +181,7 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host);
void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd);
void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
+int sdhci_uhs2_add_host(struct sdhci_host *host);
+void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead);
#endif /* __SDHCI_UHS2_H */
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 09/16] mmc: sdhci-uhs2: add set_ios()
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (7 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 08/16] mmc: sdhci-uhs2: add add_host() and others to set up the driver Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 10/16] mmc: sdhci-uhs2: add related functions to initialize the interface Victor Shih
` (8 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This is a sdhci version of mmc's set_ios operation.
This is used to handle basic IO bus setting.
It covers both UHS-I and UHS-II.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V22:
- Adjust the patch order from v21 patch#14 to v22 patch#15 and
add necessary code to avoid defined but not used warnings.
Updates in V14:
- Use mmc_card_uhs2() to stead sdhci_uhs2_mode() in the
sdhci_uhs2_set_ios().
Updates in V13:
- Add judgment condition for power mode in the __sdhci_uhs2_set_ios().
- Modify comment message.
Updates in V9:
- Simplity the turning_on_clk in sdhci_set_ios().
Updates in V8:
- Add the judgment formula for MMC_TIMING_SPEED_A_HD, MMC_TIMING_SPEED_B
and MMC_TIMING_SPEED_B_HD in __sdhci_uhs2_set_ios().
- Add the switch case for MMC_TIMING_SPEED_A_HD, MMC_TIMING_SPEED_B
and MMC_TIMING_SPEED_B_HD in sdhci_get_preset_value().
- mmc_opt_regulator_set_ocr() to instead of mmc_regulator_set_ocr()
in sdhci_uhs2_set_ios().
Updates in V7:
- Remove unnecessary functions.
Updates in V6:
- Modify return value in some functions.
- Remove unnecessary functions.
---
drivers/mmc/host/sdhci-uhs2.c | 128 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 1 +
drivers/mmc/host/sdhci.c | 55 +++++++++------
drivers/mmc/host/sdhci.h | 2 +
4 files changed, 165 insertions(+), 21 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index d3af620c7b68..ee46dac891e5 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -208,12 +208,136 @@ void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_set_timeout);
+/**
+ * sdhci_uhs2_clear_set_irqs - set Error Interrupt Status Enable register
+ * @host: SDHCI host
+ * @clear: bit-wise clear mask
+ * @set: bit-wise set mask
+ *
+ * Set/unset bits in UHS-II Error Interrupt Status Enable register
+ */
+void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+{
+ u32 ier;
+
+ ier = sdhci_readl(host, SDHCI_UHS2_INT_STATUS_ENABLE);
+ ier &= ~clear;
+ ier |= set;
+ sdhci_writel(host, ier, SDHCI_UHS2_INT_STATUS_ENABLE);
+ sdhci_writel(host, ier, SDHCI_UHS2_INT_SIGNAL_ENABLE);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_clear_set_irqs);
+
+static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u8 cmd_res, dead_lock;
+ u16 ctrl_2;
+
+ /* UHS2 Timeout Control */
+ sdhci_calc_timeout_uhs2(host, &cmd_res, &dead_lock);
+
+ /* change to use calculate value */
+ cmd_res |= FIELD_PREP(SDHCI_UHS2_TIMER_CTRL_DEADLOCK_MASK, dead_lock);
+
+ sdhci_uhs2_clear_set_irqs(host,
+ SDHCI_UHS2_INT_CMD_TIMEOUT |
+ SDHCI_UHS2_INT_DEADLOCK_TIMEOUT,
+ 0);
+ sdhci_writeb(host, cmd_res, SDHCI_UHS2_TIMER_CTRL);
+ sdhci_uhs2_clear_set_irqs(host, 0,
+ SDHCI_UHS2_INT_CMD_TIMEOUT |
+ SDHCI_UHS2_INT_DEADLOCK_TIMEOUT);
+
+ /* UHS2 timing. Note, UHS2 timing is disabled when powering off */
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (ios->power_mode != MMC_POWER_OFF &&
+ (ios->timing == MMC_TIMING_UHS2_SPEED_A ||
+ ios->timing == MMC_TIMING_UHS2_SPEED_A_HD ||
+ ios->timing == MMC_TIMING_UHS2_SPEED_B ||
+ ios->timing == MMC_TIMING_UHS2_SPEED_B_HD))
+ ctrl_2 |= SDHCI_CTRL_UHS2 | SDHCI_CTRL_UHS2_ENABLE;
+ else
+ ctrl_2 &= ~(SDHCI_CTRL_UHS2 | SDHCI_CTRL_UHS2_ENABLE);
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ host->timing = ios->timing;
+
+ if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
+ sdhci_enable_preset_value(host, true);
+
+ if (host->ops->set_power)
+ host->ops->set_power(host, ios->power_mode, ios->vdd);
+ else
+ sdhci_uhs2_set_power(host, ios->power_mode, ios->vdd);
+
+ sdhci_set_clock(host, host->clock);
+}
+
+static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n",
+ mmc_hostname(mmc), ios->clock, ios->power_mode, ios->vdd, ios->timing);
+
+ if (!mmc_card_uhs2(mmc)) {
+ sdhci_set_ios(mmc, ios);
+ return 0;
+ }
+
+ if (ios->power_mode == MMC_POWER_UNDEFINED)
+ return 0;
+
+ if (host->flags & SDHCI_DEVICE_DEAD) {
+ if (ios->power_mode == MMC_POWER_OFF) {
+ mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ mmc_regulator_set_vqmmc2(mmc, ios);
+ }
+ return -1;
+ }
+
+ sdhci_set_ios_common(mmc, ios);
+
+ __sdhci_uhs2_set_ios(mmc, ios);
+
+ return 0;
+}
+
+static int sdhci_uhs2_control(struct mmc_host *mmc, enum sd_uhs2_operation op)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct mmc_ios *ios = &mmc->ios;
+ int err = 0;
+
+ DBG("Begin uhs2 control, act %d.\n", op);
+
+ switch (op) {
+ case UHS2_SET_IOS:
+ err = sdhci_uhs2_set_ios(mmc, ios);
+ break;
+ default:
+ pr_err("%s: input sd uhs2 operation %d is wrong!\n",
+ mmc_hostname(host->mmc), op);
+ err = -EIO;
+ break;
+ }
+
+ return err;
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
* *
\*****************************************************************************/
+static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
+{
+ host->mmc_host_ops.uhs2_control = sdhci_uhs2_control;
+
+ return 0;
+}
+
static int __init sdhci_uhs2_mod_init(void)
{
return 0;
@@ -288,6 +412,10 @@ int sdhci_uhs2_add_host(struct sdhci_host *host)
/* host doesn't want to enable UHS2 support */
mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
+ /* overwrite ops */
+ if (mmc->caps2 & MMC_CAP2_SD_UHS2)
+ sdhci_uhs2_host_ops_init(host);
+
/* LED support not implemented for UHS2 */
host->quirks |= SDHCI_QUIRK_NO_LED;
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 0e1866a6bdb6..14713a31f8a5 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -183,5 +183,6 @@ void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned
void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
int sdhci_uhs2_add_host(struct sdhci_host *host);
void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead);
+void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 366c3d30dba6..63fa1714930a 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -47,8 +47,6 @@
static unsigned int debug_quirks = 0;
static unsigned int debug_quirks2;
-static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
-
static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
void sdhci_dumpregs(struct sdhci_host *host)
@@ -1877,6 +1875,12 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
case MMC_TIMING_MMC_HS400:
preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
break;
+ case MMC_TIMING_UHS2_SPEED_A:
+ case MMC_TIMING_UHS2_SPEED_A_HD:
+ case MMC_TIMING_UHS2_SPEED_B:
+ case MMC_TIMING_UHS2_SPEED_B_HD:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_UHS2);
+ break;
default:
pr_warn("%s: Invalid UHS-I mode selected\n",
mmc_hostname(host->mmc));
@@ -2323,24 +2327,9 @@ static bool sdhci_presetable_values_change(struct sdhci_host *host, struct mmc_i
(sdhci_preset_needed(host, ios->timing) || host->drv_type != ios->drv_type);
}
-void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
- bool reinit_uhs = host->reinit_uhs;
- bool turning_on_clk = false;
- u8 ctrl;
-
- host->reinit_uhs = false;
-
- if (ios->power_mode == MMC_POWER_UNDEFINED)
- return;
-
- if (host->flags & SDHCI_DEVICE_DEAD) {
- if (!IS_ERR(mmc->supply.vmmc) &&
- ios->power_mode == MMC_POWER_OFF)
- mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
- return;
- }
/*
* Reset the chip on each power off.
@@ -2357,8 +2346,6 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sdhci_enable_preset_value(host, false);
if (!ios->clock || ios->clock != host->clock) {
- turning_on_clk = ios->clock && !host->clock;
-
host->ops->set_clock(host, ios->clock);
host->clock = ios->clock;
@@ -2374,6 +2361,31 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
mmc->max_busy_timeout /= host->timeout_clk;
}
}
+}
+EXPORT_SYMBOL_GPL(sdhci_set_ios_common);
+
+void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ bool reinit_uhs = host->reinit_uhs;
+ bool turning_on_clk;
+ u8 ctrl;
+
+ host->reinit_uhs = false;
+
+ if (ios->power_mode == MMC_POWER_UNDEFINED)
+ return;
+
+ if (host->flags & SDHCI_DEVICE_DEAD) {
+ if (!IS_ERR(mmc->supply.vmmc) &&
+ ios->power_mode == MMC_POWER_OFF)
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ return;
+ }
+
+ turning_on_clk = ios->clock != host->clock && ios->clock && !host->clock;
+
+ sdhci_set_ios_common(mmc, ios);
if (host->ops->set_power)
host->ops->set_power(host, ios->power_mode, ios->vdd);
@@ -2942,7 +2954,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
}
EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
-static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
+void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
{
/* Host Controller v3.00 defines preset value registers */
if (host->version < SDHCI_SPEC_300)
@@ -2970,6 +2982,7 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
host->preset_enabled = enable;
}
}
+EXPORT_SYMBOL_GPL(sdhci_enable_preset_value);
static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
int err)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 0f78708d0c70..5c66927210bd 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -848,6 +848,8 @@ void sdhci_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode);
+void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
+void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios);
void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios);
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 10/16] mmc: sdhci-uhs2: add related functions to initialize the interface
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (8 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 09/16] mmc: sdhci-uhs2: add set_ios() Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 11/16] mmc: sdhci-uhs2: add irq() and others Victor Shih
` (7 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
UHS-II interface (related registers) will be initialized here.
The operations include mmc's uhs2_set_reg operations,
mmc's uhs2_detect_init operations, uhs2_[enable|disable]_clk operations.
After detected the UHS-II interface, the host's UHS-II capabilities will be set up here and
interrupts will also be enabled.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V22:
- Adjust the patch order from v21 patch#15 to v22 patch#16 and
remove unnecessary code to avoid defined but not used warnings.
- Modify commit message.
Updates in V15:
- Resolve merge conflicts and reduce unnecessary line breaks.
Updates in V13:
- Merge Patch#15, Patch#16 and Patch#17 of v12 version into Patch#15 in v13 version.
- Use definitions to simplify code.
- Modify comment message.
---
drivers/mmc/host/sdhci-uhs2.c | 204 ++++++++++++++++++++++++++++++++++
1 file changed, 204 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index ee46dac891e5..94e041520a54 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -26,6 +26,9 @@
pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
#define UHS2_RESET_TIMEOUT_100MS 100000
+#define UHS2_CHECK_DORMANT_TIMEOUT_100MS 100000
+#define UHS2_INTERFACE_DETECT_TIMEOUT_100MS 100000
+#define UHS2_LANE_SYNC_TIMEOUT_150MS 150000
void sdhci_uhs2_dump_regs(struct sdhci_host *host)
{
@@ -303,6 +306,186 @@ static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
return 0;
}
+static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
+{
+ u32 val;
+
+ if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IF_DETECT),
+ 100, UHS2_INTERFACE_DETECT_TIMEOUT_100MS, true,
+ host, SDHCI_PRESENT_STATE)) {
+ pr_warn("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+
+ /* Enable UHS2 error interrupts */
+ sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK, SDHCI_UHS2_INT_ERROR_MASK);
+
+ if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_LANE_SYNC),
+ 100, UHS2_LANE_SYNC_TIMEOUT_150MS, true, host, SDHCI_PRESENT_STATE)) {
+ pr_warn("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+
+ DBG("%s: UHS2 Lane synchronized in UHS2 mode, PHY is initialized.\n",
+ mmc_hostname(host->mmc));
+ return 0;
+}
+
+static int sdhci_uhs2_init(struct sdhci_host *host)
+{
+ u16 caps_ptr = 0;
+ u32 caps_gen = 0;
+ u32 caps_phy = 0;
+ u32 caps_tran[2] = {0, 0};
+ struct mmc_host *mmc = host->mmc;
+
+ caps_ptr = sdhci_readw(host, SDHCI_UHS2_CAPS_PTR);
+ if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
+ pr_err("%s: SDHCI_UHS2_CAPS_PTR(%d) is wrong.\n",
+ mmc_hostname(mmc), caps_ptr);
+ return -ENODEV;
+ }
+ caps_gen = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_OFFSET);
+ caps_phy = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_PHY_OFFSET);
+ caps_tran[0] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_OFFSET);
+ caps_tran[1] = sdhci_readl(host, caps_ptr + SDHCI_UHS2_CAPS_TRAN_1_OFFSET);
+
+ /* General Caps */
+ mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_CAPS_DAP_MASK;
+ mmc->uhs2_caps.gap = FIELD_GET(SDHCI_UHS2_CAPS_GAP_MASK, caps_gen);
+ mmc->uhs2_caps.n_lanes = FIELD_GET(SDHCI_UHS2_CAPS_LANE_MASK, caps_gen);
+ mmc->uhs2_caps.addr64 = (caps_gen & SDHCI_UHS2_CAPS_ADDR_64) ? 1 : 0;
+ mmc->uhs2_caps.card_type = FIELD_GET(SDHCI_UHS2_CAPS_DEV_TYPE_MASK, caps_gen);
+
+ /* PHY Caps */
+ mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_CAPS_PHY_REV_MASK;
+ mmc->uhs2_caps.speed_range = FIELD_GET(SDHCI_UHS2_CAPS_PHY_RANGE_MASK, caps_phy);
+ mmc->uhs2_caps.n_lss_sync = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_SYN_MASK, caps_phy);
+ mmc->uhs2_caps.n_lss_dir = FIELD_GET(SDHCI_UHS2_CAPS_PHY_N_LSS_DIR_MASK, caps_phy);
+ if (mmc->uhs2_caps.n_lss_sync == 0)
+ mmc->uhs2_caps.n_lss_sync = 16 << 2;
+ else
+ mmc->uhs2_caps.n_lss_sync <<= 2;
+ if (mmc->uhs2_caps.n_lss_dir == 0)
+ mmc->uhs2_caps.n_lss_dir = 16 << 3;
+ else
+ mmc->uhs2_caps.n_lss_dir <<= 3;
+
+ /* LINK/TRAN Caps */
+ mmc->uhs2_caps.link_rev = caps_tran[0] & SDHCI_UHS2_CAPS_TRAN_LINK_REV_MASK;
+ mmc->uhs2_caps.n_fcu = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_N_FCU_MASK, caps_tran[0]);
+ if (mmc->uhs2_caps.n_fcu == 0)
+ mmc->uhs2_caps.n_fcu = 256;
+ mmc->uhs2_caps.host_type = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_HOST_TYPE_MASK, caps_tran[0]);
+ mmc->uhs2_caps.maxblk_len = FIELD_GET(SDHCI_UHS2_CAPS_TRAN_BLK_LEN_MASK, caps_tran[0]);
+ mmc->uhs2_caps.n_data_gap = caps_tran[1] & SDHCI_UHS2_CAPS_TRAN_1_N_DATA_GAP_MASK;
+
+ return 0;
+}
+
+static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ DBG("Begin do uhs2 detect init.\n");
+
+ if (sdhci_uhs2_interface_detect(host)) {
+ pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
+ return -EIO;
+ }
+
+ if (sdhci_uhs2_init(host)) {
+ pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
+ return -EIO;
+ }
+
+ /* Init complete, do soft reset and enable UHS2 error irqs. */
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
+ sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK, SDHCI_UHS2_INT_ERROR_MASK);
+ /*
+ * N.B SDHCI_INT_ENABLE and SDHCI_SIGNAL_ENABLE was cleared
+ * by SDHCI_UHS2_SW_RESET_SD
+ */
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ return 0;
+}
+
+static int sdhci_uhs2_disable_clk(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ return 0;
+}
+
+static int sdhci_uhs2_enable_clk(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ int timeout_us = 20000; /* 20ms */
+ u32 val;
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ if (read_poll_timeout(sdhci_readw, val, (val & SDHCI_CLOCK_INT_STABLE),
+ 10, timeout_us, true, host, SDHCI_CLOCK_CONTROL)) {
+ pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void sdhci_uhs2_set_config(struct sdhci_host *host)
+{
+ u32 value;
+ u16 sdhci_uhs2_set_ptr = sdhci_readw(host, SDHCI_UHS2_SETTINGS_PTR);
+ u16 sdhci_uhs2_gen_set_reg = sdhci_uhs2_set_ptr;
+ u16 sdhci_uhs2_phy_set_reg = sdhci_uhs2_set_ptr + 4;
+ u16 sdhci_uhs2_tran_set_reg = sdhci_uhs2_set_ptr + 8;
+ u16 sdhci_uhs2_tran_set_1_reg = sdhci_uhs2_set_ptr + 12;
+
+ /* Set Gen Settings */
+ value = FIELD_PREP(SDHCI_UHS2_GEN_SETTINGS_N_LANES_MASK, host->mmc->uhs2_caps.n_lanes_set);
+ sdhci_writel(host, value, sdhci_uhs2_gen_set_reg);
+
+ /* Set PHY Settings */
+ value = FIELD_PREP(SDHCI_UHS2_PHY_N_LSS_DIR_MASK, host->mmc->uhs2_caps.n_lss_dir_set) |
+ FIELD_PREP(SDHCI_UHS2_PHY_N_LSS_SYN_MASK, host->mmc->uhs2_caps.n_lss_sync_set);
+ if (host->mmc->ios.timing == MMC_TIMING_UHS2_SPEED_B ||
+ host->mmc->ios.timing == MMC_TIMING_UHS2_SPEED_B_HD)
+ value |= SDHCI_UHS2_PHY_SET_SPEED_B;
+ sdhci_writel(host, value, sdhci_uhs2_phy_set_reg);
+
+ /* Set LINK-TRAN Settings */
+ value = FIELD_PREP(SDHCI_UHS2_TRAN_RETRY_CNT_MASK, host->mmc->uhs2_caps.max_retry_set) |
+ FIELD_PREP(SDHCI_UHS2_TRAN_N_FCU_MASK, host->mmc->uhs2_caps.n_fcu_set);
+ sdhci_writel(host, value, sdhci_uhs2_tran_set_reg);
+ sdhci_writel(host, host->mmc->uhs2_caps.n_data_gap_set, sdhci_uhs2_tran_set_1_reg);
+}
+
+static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
+{
+ u32 val;
+
+ if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IN_DORMANT_STATE),
+ 100, UHS2_CHECK_DORMANT_TIMEOUT_100MS, true, host,
+ SDHCI_PRESENT_STATE)) {
+ pr_warn("%s: UHS2 IN_DORMANT fail in 100ms.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return -EIO;
+ }
+ return 0;
+}
+
static int sdhci_uhs2_control(struct mmc_host *mmc, enum sd_uhs2_operation op)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -312,6 +495,27 @@ static int sdhci_uhs2_control(struct mmc_host *mmc, enum sd_uhs2_operation op)
DBG("Begin uhs2 control, act %d.\n", op);
switch (op) {
+ case UHS2_PHY_INIT:
+ err = sdhci_uhs2_do_detect_init(mmc);
+ break;
+ case UHS2_SET_CONFIG:
+ sdhci_uhs2_set_config(host);
+ break;
+ case UHS2_ENABLE_INT:
+ sdhci_uhs2_clear_set_irqs(host, 0, SDHCI_INT_CARD_INT);
+ break;
+ case UHS2_DISABLE_INT:
+ sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_CARD_INT, 0);
+ break;
+ case UHS2_CHECK_DORMANT:
+ err = sdhci_uhs2_check_dormant(host);
+ break;
+ case UHS2_DISABLE_CLK:
+ err = sdhci_uhs2_disable_clk(mmc);
+ break;
+ case UHS2_ENABLE_CLK:
+ err = sdhci_uhs2_enable_clk(mmc);
+ break;
case UHS2_SET_IOS:
err = sdhci_uhs2_set_ios(mmc, ios);
break;
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 11/16] mmc: sdhci-uhs2: add irq() and others
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (9 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 10/16] mmc: sdhci-uhs2: add related functions to initialize the interface Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 12/16] mmc: sdhci-uhs2: add request() " Victor Shih
` (6 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This is a UHS-II version of sdhci's request() operation.
It handles UHS-II related command interrupts and errors.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V22:
- Remove unnecessary code to avoid defined but not used warnings.
Updates in V19:
- Corrected sdhci_uhs2_reset() parameter errors
in the sdhci_uhs2_request_done().
Updates in V18:
- Modify the judgment condition in the sdhci_uhs2_reset_cmd_data() and
replace it from mmc_card_uhs2() to host->mmc->uhs2_sd_tran flag.
Updates in V17:
- Add sdhci_uhs2_reset_cmd_data() and sdhci_uhs2_needs_reset() to
resolve the data error or cmd error.
Updates in V14:
- Use mmc_card_uhs2() to stead sdhci_uhs2_mode() in the
sdhci_uhs2_complete_work(), sdhci_uhs2_irq() and
sdhci_uhs2_thread_irq().
Updates in V13:
- Re-order function to avoid declaration.
- Remove unnecessary definitions.
Updates in V9:
- Cancel export state of sdhci_set_mrq_done() function.
Updates in V8:
- Forward declare struct mmc_request in sdhci_uhs2.h.
- Remove forward declaration of sdhci_send_command().
- Use mmc_dev() to simplify code in sdhci_request_done_dma().
Updates in V7:
- Remove unnecessary functions.
- Use sdhci_uhs2_mode() to simplify code in sdhci_uhs2_irq().
- Modify descriptions in sdhci_uhs2_irq().
- Cancel export state of some functions.
Updates in V6:
- Remove unnecessary functions.
- Add sdhci_uhs2_mode() in sdhci_uhs2_complete_work().
- Add sdhci_uhs2_mode() in sdhci_uhs2_thread_irq().
---
drivers/mmc/host/sdhci-uhs2.c | 227 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
drivers/mmc/host/sdhci.c | 109 +++++++++-------
drivers/mmc/host/sdhci.h | 8 ++
4 files changed, 298 insertions(+), 48 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 94e041520a54..76f1af8b0486 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -98,6 +98,19 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
+static void sdhci_uhs2_reset_cmd_data(struct sdhci_host *host)
+{
+ sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ if (host->mmc->uhs2_sd_tran) {
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK, SDHCI_UHS2_INT_ERROR_MASK);
+ }
+}
+
void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
{
struct mmc_host *mmc = host->mmc;
@@ -529,6 +542,217 @@ static int sdhci_uhs2_control(struct mmc_host *mmc, enum sd_uhs2_operation op)
return err;
}
+/*****************************************************************************\
+ * *
+ * Request done *
+ * *
+\*****************************************************************************/
+
+static bool sdhci_uhs2_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ return sdhci_needs_reset(host, mrq) ||
+ (!(host->flags & SDHCI_DEVICE_DEAD) && mrq->data && mrq->data->error);
+}
+
+static bool sdhci_uhs2_request_done(struct sdhci_host *host)
+{
+ unsigned long flags;
+ struct mmc_request *mrq;
+ int i;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+ mrq = host->mrqs_done[i];
+ if (mrq)
+ break;
+ }
+
+ if (!mrq) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return true;
+ }
+
+ /*
+ * Always unmap the data buffers if they were mapped by
+ * sdhci_prepare_data() whenever we finish with a request.
+ * This avoids leaking DMA mappings on error.
+ */
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ sdhci_request_done_dma(host, mrq);
+
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (sdhci_uhs2_needs_reset(host, mrq)) {
+ /*
+ * Do not finish until command and data lines are available for
+ * reset. Note there can only be one other mrq, so it cannot
+ * also be in mrqs_done, otherwise host->cmd and host->data_cmd
+ * would both be null.
+ */
+ if (host->cmd || host->data_cmd) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return true;
+ }
+
+ if (mrq->cmd->error || mrq->data->error)
+ sdhci_uhs2_reset_cmd_data(host);
+ else
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
+ host->pending_reset = false;
+ }
+
+ host->mrqs_done[i] = NULL;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (host->ops->request_done)
+ host->ops->request_done(host, mrq);
+ else
+ mmc_request_done(host->mmc, mrq);
+
+ return false;
+}
+
+static void sdhci_uhs2_complete_work(struct work_struct *work)
+{
+ struct sdhci_host *host = container_of(work, struct sdhci_host,
+ complete_work);
+
+ if (!mmc_card_uhs2(host->mmc)) {
+ sdhci_complete_work(work);
+ return;
+ }
+
+ while (!sdhci_uhs2_request_done(host))
+ ;
+}
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
+static void __sdhci_uhs2_irq(struct sdhci_host *host, u32 uhs2mask)
+{
+ DBG("*** %s got UHS2 error interrupt: 0x%08x\n",
+ mmc_hostname(host->mmc), uhs2mask);
+
+ if (uhs2mask & SDHCI_UHS2_INT_CMD_ERR_MASK) {
+ if (!host->cmd) {
+ pr_err("%s: Got cmd interrupt 0x%08x but no cmd.\n",
+ mmc_hostname(host->mmc),
+ (unsigned int)uhs2mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+ host->cmd->error = -EILSEQ;
+ if (uhs2mask & SDHCI_UHS2_INT_CMD_TIMEOUT)
+ host->cmd->error = -ETIMEDOUT;
+ }
+
+ if (uhs2mask & SDHCI_UHS2_INT_DATA_ERR_MASK) {
+ if (!host->data) {
+ pr_err("%s: Got data interrupt 0x%08x but no data.\n",
+ mmc_hostname(host->mmc),
+ (unsigned int)uhs2mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+
+ if (uhs2mask & SDHCI_UHS2_INT_DEADLOCK_TIMEOUT) {
+ pr_err("%s: Got deadlock timeout interrupt 0x%08x\n",
+ mmc_hostname(host->mmc),
+ (unsigned int)uhs2mask);
+ host->data->error = -ETIMEDOUT;
+ } else if (uhs2mask & SDHCI_UHS2_INT_ADMA_ERROR) {
+ pr_err("%s: ADMA error = 0x %x\n",
+ mmc_hostname(host->mmc),
+ sdhci_readb(host, SDHCI_ADMA_ERROR));
+ host->data->error = -EIO;
+ } else {
+ host->data->error = -EILSEQ;
+ }
+ }
+}
+
+u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask)
+{
+ u32 mask = intmask, uhs2mask;
+
+ if (!mmc_card_uhs2(host->mmc))
+ goto out;
+
+ if (intmask & SDHCI_INT_ERROR) {
+ uhs2mask = sdhci_readl(host, SDHCI_UHS2_INT_STATUS);
+ if (!(uhs2mask & SDHCI_UHS2_INT_ERROR_MASK))
+ goto cmd_irq;
+
+ /* Clear error interrupts */
+ sdhci_writel(host, uhs2mask & SDHCI_UHS2_INT_ERROR_MASK,
+ SDHCI_UHS2_INT_STATUS);
+
+ /* Handle error interrupts */
+ __sdhci_uhs2_irq(host, uhs2mask);
+
+ /* Caller, sdhci_irq(), doesn't have to care about UHS-2 errors */
+ intmask &= ~SDHCI_INT_ERROR;
+ mask &= SDHCI_INT_ERROR;
+ }
+
+cmd_irq:
+ if (intmask & SDHCI_INT_CMD_MASK) {
+ /* Clear command interrupt */
+ sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS);
+
+ /* Caller, sdhci_irq(), doesn't have to care about UHS-2 commands */
+ intmask &= ~SDHCI_INT_CMD_MASK;
+ mask &= SDHCI_INT_CMD_MASK;
+ }
+
+ /* Clear already-handled interrupts. */
+ sdhci_writel(host, mask, SDHCI_INT_STATUS);
+
+out:
+ return intmask;
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_irq);
+
+static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
+{
+ struct sdhci_host *host = dev_id;
+ struct mmc_command *cmd;
+ unsigned long flags;
+ u32 isr;
+
+ if (!mmc_card_uhs2(host->mmc))
+ return sdhci_thread_irq(irq, dev_id);
+
+ while (!sdhci_uhs2_request_done(host))
+ ;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ isr = host->thread_isr;
+ host->thread_isr = 0;
+
+ cmd = host->deferred_cmd;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+ struct mmc_host *mmc = host->mmc;
+
+ mmc->ops->card_event(mmc);
+ mmc_detect_change(mmc, msecs_to_jiffies(200));
+ }
+
+ return IRQ_HANDLED;
+}
+
/*****************************************************************************\
* *
* Driver init/exit *
@@ -620,6 +844,9 @@ int sdhci_uhs2_add_host(struct sdhci_host *host)
if (mmc->caps2 & MMC_CAP2_SD_UHS2)
sdhci_uhs2_host_ops_init(host);
+ host->complete_work_fn = sdhci_uhs2_complete_work;
+ host->thread_irq_fn = sdhci_uhs2_thread_irq;
+
/* LED support not implemented for UHS2 */
host->quirks |= SDHCI_QUIRK_NO_LED;
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 14713a31f8a5..3205adaecfa4 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -176,6 +176,7 @@
struct sdhci_host;
struct mmc_command;
+struct mmc_request;
void sdhci_uhs2_dump_regs(struct sdhci_host *host);
void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
@@ -184,5 +185,6 @@ void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
int sdhci_uhs2_add_host(struct sdhci_host *host);
void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead);
void sdhci_uhs2_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set);
+u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask);
#endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 63fa1714930a..871b4fe2a1b2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -234,7 +234,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
}
EXPORT_SYMBOL_GPL(sdhci_reset);
-static bool sdhci_do_reset(struct sdhci_host *host, u8 mask)
+bool sdhci_do_reset(struct sdhci_host *host, u8 mask)
{
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
struct mmc_host *mmc = host->mmc;
@@ -247,6 +247,7 @@ static bool sdhci_do_reset(struct sdhci_host *host, u8 mask)
return true;
}
+EXPORT_SYMBOL_GPL(sdhci_do_reset);
static void sdhci_reset_for_all(struct sdhci_host *host)
{
@@ -1489,7 +1490,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
}
-static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
+bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
{
return (!(host->flags & SDHCI_DEVICE_DEAD) &&
((mrq->cmd && mrq->cmd->error) ||
@@ -1497,6 +1498,7 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
(mrq->data && mrq->data->stop && mrq->data->stop->error) ||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
}
+EXPORT_SYMBOL_GPL(sdhci_needs_reset);
static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
{
@@ -3076,6 +3078,53 @@ static const struct mmc_host_ops sdhci_ops = {
* *
\*****************************************************************************/
+void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ struct mmc_data *data = mrq->data;
+
+ if (data && data->host_cookie == COOKIE_MAPPED) {
+ if (host->bounce_buffer) {
+ /*
+ * On reads, copy the bounced data into the
+ * sglist
+ */
+ if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
+ unsigned int length = data->bytes_xfered;
+
+ if (length > host->bounce_buffer_size) {
+ pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
+ mmc_hostname(host->mmc),
+ host->bounce_buffer_size,
+ data->bytes_xfered);
+ /* Cap it down and continue */
+ length = host->bounce_buffer_size;
+ }
+ dma_sync_single_for_cpu(mmc_dev(host->mmc),
+ host->bounce_addr,
+ host->bounce_buffer_size,
+ DMA_FROM_DEVICE);
+ sg_copy_from_buffer(data->sg,
+ data->sg_len,
+ host->bounce_buffer,
+ length);
+ } else {
+ /* No copying, just switch ownership */
+ dma_sync_single_for_cpu(mmc_dev(host->mmc),
+ host->bounce_addr,
+ host->bounce_buffer_size,
+ mmc_get_dma_dir(data));
+ }
+ } else {
+ /* Unmap the raw data */
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len,
+ mmc_get_dma_dir(data));
+ }
+ data->host_cookie = COOKIE_UNMAPPED;
+ }
+}
+EXPORT_SYMBOL_GPL(sdhci_request_done_dma);
+
static bool sdhci_request_done(struct sdhci_host *host)
{
unsigned long flags;
@@ -3140,48 +3189,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
sdhci_set_mrq_done(host, mrq);
}
- if (data && data->host_cookie == COOKIE_MAPPED) {
- if (host->bounce_buffer) {
- /*
- * On reads, copy the bounced data into the
- * sglist
- */
- if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
- unsigned int length = data->bytes_xfered;
-
- if (length > host->bounce_buffer_size) {
- pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
- mmc_hostname(host->mmc),
- host->bounce_buffer_size,
- data->bytes_xfered);
- /* Cap it down and continue */
- length = host->bounce_buffer_size;
- }
- dma_sync_single_for_cpu(
- mmc_dev(host->mmc),
- host->bounce_addr,
- host->bounce_buffer_size,
- DMA_FROM_DEVICE);
- sg_copy_from_buffer(data->sg,
- data->sg_len,
- host->bounce_buffer,
- length);
- } else {
- /* No copying, just switch ownership */
- dma_sync_single_for_cpu(
- mmc_dev(host->mmc),
- host->bounce_addr,
- host->bounce_buffer_size,
- mmc_get_dma_dir(data));
- }
- } else {
- /* Unmap the raw data */
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
- mmc_get_dma_dir(data));
- }
- data->host_cookie = COOKIE_UNMAPPED;
- }
+ sdhci_request_done_dma(host, mrq);
}
host->mrqs_done[i] = NULL;
@@ -3196,7 +3204,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
return false;
}
-static void sdhci_complete_work(struct work_struct *work)
+void sdhci_complete_work(struct work_struct *work)
{
struct sdhci_host *host = container_of(work, struct sdhci_host,
complete_work);
@@ -3204,6 +3212,7 @@ static void sdhci_complete_work(struct work_struct *work)
while (!sdhci_request_done(host))
;
}
+EXPORT_SYMBOL_GPL(sdhci_complete_work);
static void sdhci_timeout_timer(struct timer_list *t)
{
@@ -3665,7 +3674,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
return result;
}
-static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
+irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
{
struct sdhci_host *host = dev_id;
struct mmc_command *cmd;
@@ -3695,6 +3704,7 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(sdhci_thread_irq);
/*****************************************************************************\
* *
@@ -4067,6 +4077,9 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->max_timeout_count = 0xE;
+ host->complete_work_fn = sdhci_complete_work;
+ host->thread_irq_fn = sdhci_thread_irq;
+
return host;
}
@@ -4831,7 +4844,7 @@ int __sdhci_add_host(struct sdhci_host *host)
if (!host->complete_wq)
return -ENOMEM;
- INIT_WORK(&host->complete_work, sdhci_complete_work);
+ INIT_WORK(&host->complete_work, host->complete_work_fn);
timer_setup(&host->timer, sdhci_timeout_timer, 0);
timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
@@ -4840,7 +4853,7 @@ int __sdhci_add_host(struct sdhci_host *host)
sdhci_init(host, 0);
- ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
+ ret = request_threaded_irq(host->irq, sdhci_irq, host->thread_irq_fn,
IRQF_SHARED, mmc_hostname(mmc), host);
if (ret) {
pr_err("%s: Failed to request IRQ %d: %d\n",
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 5c66927210bd..5f416bc783bd 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -625,6 +625,9 @@ struct sdhci_host {
struct timer_list timer; /* Timer for timeouts */
struct timer_list data_timer; /* Timer for data timeouts */
+ void (*complete_work_fn)(struct work_struct *work);
+ irqreturn_t (*thread_irq_fn)(int irq, void *dev_id);
+
#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
struct dma_chan *rx_chan;
struct dma_chan *tx_chan;
@@ -827,6 +830,7 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
__sdhci_read_caps(host, NULL, NULL, NULL);
}
+bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq);
u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
unsigned int *actual_clock);
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
@@ -845,6 +849,7 @@ void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
void sdhci_set_bus_width(struct sdhci_host *host, int width);
void sdhci_reset(struct sdhci_host *host, u8 mask);
+bool sdhci_do_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode);
@@ -854,6 +859,9 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios);
void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
+void sdhci_request_done_dma(struct sdhci_host *host, struct mmc_request *mrq);
+void sdhci_complete_work(struct work_struct *work);
+irqreturn_t sdhci_thread_irq(int irq, void *dev_id);
void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 12/16] mmc: sdhci-uhs2: add request() and others
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (10 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 11/16] mmc: sdhci-uhs2: add irq() and others Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 13/16] mmc: sdhci-uhs2: add pre-detect_init hook Victor Shih
` (5 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This is a sdhci version of mmc's request operation.
It covers both UHS-I and UHS-II.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
Updates in V22:
- Adjust the patch order from v21 patch#16 to v22 patch#18 and
add necessary code to avoid defined but not used warnings.
Updates in V19:
- cmd->uhs2_cmd->uhs2_resp is always true, remove it from safety check
in the __sdhci_uhs2_finish_command().
Updates in V17:
- Add new parameter to __sdhci_finish_data_common().
Updates in V16:
- Remove irrelevant definition in the sdhci_calc_timeout_uhs2().
Updates in V15:
- Adjust the parameters used in the __sdhci_uhs2_finish_command()
to match changes in the Patch#7.
Updates in V14:
- Use mmc_card_uhs2() to stead sdhci_uhs2_mode() in the
sdhci_uhs2_request().
Updates in V13:
- Re-order function to avoid declaration.
- Remove unnecessary function.
Updates in V11:
- Drop the check mmc_card_uhs2_hd_mode(host->mmc)
in sdhci_uhs2_set_transfer_mode().
Updates in V10:
- Use tmode_half_duplex to instead of uhs2_tmode0_flag
in sdhci_uhs2_set_transfer_mode().
Updates in V9:
- Modify the annotations in __sdhci_uhs2_send_command().
Updates in V8:
- Adjust the position of matching brackets in
sdhci_uhs2_send_command_retry().
- Modify CameCase definition in __sdhci_uhs2_finish_command().
- Modify error message in __sdhci_uhs2_finish_command().
- sdhci_uhs2_send_command_retry() to instead of sdhci_uhs2_send_command()
in sdhci_uhs2_request().
- Use sdhci_uhs2_mode() to simplify code in sdhci_uhs2_request_atomic().
- Add forward declaration for sdhci_send_command().
Updates in V7:
- Cancel export state of some functions.
- Remove unnecessary whitespace changes.
Updates in V6:
- Add uhs2_dev_cmd() to simplify code.
- Remove unnecessary functions.
- Cancel export state of some functions.
- Drop use CONFIG_MMC_DEBUG().
- Wrap at 100 columns in some functions.
---
drivers/mmc/host/sdhci-uhs2.c | 392 ++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 53 +++--
drivers/mmc/host/sdhci.h | 8 +
3 files changed, 437 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 76f1af8b0486..d99ea05098cb 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -15,6 +15,8 @@
#include <linux/iopoll.h>
#include <linux/bitfield.h>
#include <linux/regulator/consumer.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
#include "sdhci.h"
#include "sdhci-uhs2.h"
@@ -30,6 +32,8 @@
#define UHS2_INTERFACE_DETECT_TIMEOUT_100MS 100000
#define UHS2_LANE_SYNC_TIMEOUT_150MS 150000
+#define UHS2_ARG_IOADR_MASK 0xfff
+
void sdhci_uhs2_dump_regs(struct sdhci_host *host)
{
if (!(mmc_card_uhs2(host->mmc)))
@@ -64,6 +68,11 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
* *
\*****************************************************************************/
+static inline u16 uhs2_dev_cmd(struct mmc_command *cmd)
+{
+ return be16_to_cpu((__be16)cmd->uhs2_cmd->arg) & UHS2_ARG_IOADR_MASK;
+}
+
static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
struct regulator *supply,
unsigned short vdd_bit)
@@ -542,6 +551,374 @@ static int sdhci_uhs2_control(struct mmc_host *mmc, enum sd_uhs2_operation op)
return err;
}
+/*****************************************************************************\
+ * *
+ * Core functions *
+ * *
+\*****************************************************************************/
+
+static void sdhci_uhs2_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ struct mmc_data *data = cmd->data;
+
+ sdhci_initialize_data(host, data);
+
+ sdhci_prepare_dma(host, data);
+
+ sdhci_writew(host, data->blksz, SDHCI_UHS2_BLOCK_SIZE);
+ sdhci_writew(host, data->blocks, SDHCI_UHS2_BLOCK_COUNT);
+}
+
+static void sdhci_uhs2_finish_data(struct sdhci_host *host)
+{
+ struct mmc_data *data = host->data;
+
+ __sdhci_finish_data_common(host, true);
+
+ __sdhci_finish_mrq(host, data->mrq);
+}
+
+static void sdhci_uhs2_set_transfer_mode(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ u16 mode;
+ struct mmc_data *data = cmd->data;
+
+ if (!data) {
+ /* clear Auto CMD settings for no data CMDs */
+ if (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_TRANS_ABORT) {
+ mode = 0;
+ } else {
+ mode = sdhci_readw(host, SDHCI_UHS2_TRANS_MODE);
+ if (cmd->opcode == MMC_STOP_TRANSMISSION || cmd->opcode == MMC_ERASE)
+ mode |= SDHCI_UHS2_TRNS_WAIT_EBSY;
+ else
+ /* send status mode */
+ if (cmd->opcode == MMC_SEND_STATUS)
+ mode = 0;
+ }
+
+ DBG("UHS2 no data trans mode is 0x%x.\n", mode);
+
+ sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
+ return;
+ }
+
+ WARN_ON(!host->data);
+
+ mode = SDHCI_UHS2_TRNS_BLK_CNT_EN | SDHCI_UHS2_TRNS_WAIT_EBSY;
+ if (data->flags & MMC_DATA_WRITE)
+ mode |= SDHCI_UHS2_TRNS_DATA_TRNS_WRT;
+
+ if (data->blocks == 1 &&
+ data->blksz != 512 &&
+ cmd->opcode != MMC_READ_SINGLE_BLOCK &&
+ cmd->opcode != MMC_WRITE_BLOCK) {
+ mode &= ~SDHCI_UHS2_TRNS_BLK_CNT_EN;
+ mode |= SDHCI_UHS2_TRNS_BLK_BYTE_MODE;
+ }
+
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ mode |= SDHCI_UHS2_TRNS_DMA;
+
+ if (cmd->uhs2_cmd->tmode_half_duplex)
+ mode |= SDHCI_UHS2_TRNS_2L_HD;
+
+ sdhci_writew(host, mode, SDHCI_UHS2_TRANS_MODE);
+
+ DBG("UHS2 trans mode is 0x%x.\n", mode);
+}
+
+static void __sdhci_uhs2_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ int i, j;
+ int cmd_reg;
+
+ i = 0;
+ sdhci_writel(host,
+ ((u32)cmd->uhs2_cmd->arg << 16) |
+ (u32)cmd->uhs2_cmd->header,
+ SDHCI_UHS2_CMD_PACKET + i);
+ i += 4;
+
+ /*
+ * Per spec, payload (config) should be MSB before sending out.
+ * But we don't need convert here because had set payload as
+ * MSB when preparing config read/write commands.
+ */
+ for (j = 0; j < cmd->uhs2_cmd->payload_len / sizeof(u32); j++) {
+ sdhci_writel(host, *(cmd->uhs2_cmd->payload + j), SDHCI_UHS2_CMD_PACKET + i);
+ i += 4;
+ }
+
+ for ( ; i < SDHCI_UHS2_CMD_PACK_MAX_LEN; i += 4)
+ sdhci_writel(host, 0, SDHCI_UHS2_CMD_PACKET + i);
+
+ DBG("UHS2 CMD packet_len = %d.\n", cmd->uhs2_cmd->packet_len);
+ for (i = 0; i < cmd->uhs2_cmd->packet_len; i++)
+ DBG("UHS2 CMD_PACKET[%d] = 0x%x.\n", i,
+ sdhci_readb(host, SDHCI_UHS2_CMD_PACKET + i));
+
+ cmd_reg = FIELD_PREP(SDHCI_UHS2_CMD_PACK_LEN_MASK, cmd->uhs2_cmd->packet_len);
+ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)
+ cmd_reg |= SDHCI_UHS2_CMD_DATA;
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd_reg |= SDHCI_UHS2_CMD_CMD12;
+
+ /* UHS2 Native ABORT */
+ if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
+ (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_TRANS_ABORT))
+ cmd_reg |= SDHCI_UHS2_CMD_TRNS_ABORT;
+
+ /* UHS2 Native DORMANT */
+ if ((cmd->uhs2_cmd->header & UHS2_NATIVE_PACKET) &&
+ (uhs2_dev_cmd(cmd) == UHS2_DEV_CMD_GO_DORMANT_STATE))
+ cmd_reg |= SDHCI_UHS2_CMD_DORMANT;
+
+ DBG("0x%x is set to UHS2 CMD register.\n", cmd_reg);
+
+ sdhci_writew(host, cmd_reg, SDHCI_UHS2_CMD);
+}
+
+static bool sdhci_uhs2_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ int flags;
+ u32 mask;
+ unsigned long timeout;
+
+ WARN_ON(host->cmd);
+
+ /* Initially, a command has no error */
+ cmd->error = 0;
+
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd->flags |= MMC_RSP_BUSY;
+
+ mask = SDHCI_CMD_INHIBIT;
+
+ if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
+ return false;
+
+ host->cmd = cmd;
+ host->data_timeout = 0;
+ if (sdhci_data_line_cmd(cmd)) {
+ WARN_ON(host->data_cmd);
+ host->data_cmd = cmd;
+ __sdhci_uhs2_set_timeout(host);
+ }
+
+ if (cmd->data)
+ sdhci_uhs2_prepare_data(host, cmd);
+
+ sdhci_uhs2_set_transfer_mode(host, cmd);
+
+ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+ WARN_ONCE(1, "Unsupported response type!\n");
+ /*
+ * This does not happen in practice because 136-bit response
+ * commands never have busy waiting, so rather than complicate
+ * the error path, just remove busy waiting and continue.
+ */
+ cmd->flags &= ~MMC_RSP_BUSY;
+ }
+
+ if (!(cmd->flags & MMC_RSP_PRESENT))
+ flags = SDHCI_CMD_RESP_NONE;
+ else if (cmd->flags & MMC_RSP_136)
+ flags = SDHCI_CMD_RESP_LONG;
+ else if (cmd->flags & MMC_RSP_BUSY)
+ flags = SDHCI_CMD_RESP_SHORT_BUSY;
+ else
+ flags = SDHCI_CMD_RESP_SHORT;
+
+ if (cmd->flags & MMC_RSP_CRC)
+ flags |= SDHCI_CMD_CRC;
+ if (cmd->flags & MMC_RSP_OPCODE)
+ flags |= SDHCI_CMD_INDEX;
+
+ timeout = jiffies;
+ if (host->data_timeout)
+ timeout += nsecs_to_jiffies(host->data_timeout);
+ else if (!cmd->data && cmd->busy_timeout > 9000)
+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
+ else
+ timeout += 10 * HZ;
+ sdhci_mod_timer(host, cmd->mrq, timeout);
+
+ __sdhci_uhs2_send_command(host, cmd);
+
+ return true;
+}
+
+static bool sdhci_uhs2_send_command_retry(struct sdhci_host *host,
+ struct mmc_command *cmd,
+ unsigned long flags)
+ __releases(host->lock)
+ __acquires(host->lock)
+{
+ struct mmc_command *deferred_cmd = host->deferred_cmd;
+ int timeout = 10; /* Approx. 10 ms */
+ bool present;
+
+ while (!sdhci_uhs2_send_command(host, cmd)) {
+ if (!timeout--) {
+ pr_err("%s: Controller never released inhibit bit(s).\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ cmd->error = -EIO;
+ return false;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ usleep_range(1000, 1250);
+
+ present = host->mmc->ops->get_cd(host->mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* A deferred command might disappear, handle that */
+ if (cmd == deferred_cmd && cmd != host->deferred_cmd)
+ return true;
+
+ if (sdhci_present_error(host, cmd, present))
+ return false;
+ }
+
+ if (cmd == host->deferred_cmd)
+ host->deferred_cmd = NULL;
+
+ return true;
+}
+
+static void __sdhci_uhs2_finish_command(struct sdhci_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+ u8 resp;
+ u8 error_code;
+ bool breada0 = 0;
+ int i;
+
+ if (host->mmc->uhs2_sd_tran) {
+ resp = sdhci_readb(host, SDHCI_UHS2_RESPONSE + 2);
+ if (resp & UHS2_RES_NACK_MASK) {
+ error_code = (resp >> UHS2_RES_ECODE_POS) & UHS2_RES_ECODE_MASK;
+ pr_err("%s: NACK response, ECODE=0x%x.\n",
+ mmc_hostname(host->mmc), error_code);
+ }
+ breada0 = 1;
+ }
+
+ if (cmd->uhs2_cmd->uhs2_resp_len) {
+ int len = min_t(int, cmd->uhs2_cmd->uhs2_resp_len, UHS2_MAX_RESP_LEN);
+
+ /* Get whole response of some native CCMD, like
+ * DEVICE_INIT, ENUMERATE.
+ */
+ for (i = 0; i < len; i++)
+ cmd->uhs2_cmd->uhs2_resp[i] = sdhci_readb(host, SDHCI_UHS2_RESPONSE + i);
+ } else {
+ /* Get SD CMD response and Payload for some read
+ * CCMD, like INQUIRY_CFG.
+ */
+ /* Per spec (p136), payload field is divided into
+ * a unit of DWORD and transmission order within
+ * a DWORD is big endian.
+ */
+ if (!breada0)
+ sdhci_readl(host, SDHCI_UHS2_RESPONSE);
+ for (i = 4; i < 20; i += 4) {
+ cmd->resp[i / 4 - 1] =
+ (sdhci_readb(host,
+ SDHCI_UHS2_RESPONSE + i) << 24) |
+ (sdhci_readb(host,
+ SDHCI_UHS2_RESPONSE + i + 1)
+ << 16) |
+ (sdhci_readb(host,
+ SDHCI_UHS2_RESPONSE + i + 2)
+ << 8) |
+ sdhci_readb(host, SDHCI_UHS2_RESPONSE + i + 3);
+ }
+ }
+}
+
+static void sdhci_uhs2_finish_command(struct sdhci_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+
+ __sdhci_uhs2_finish_command(host);
+
+ host->cmd = NULL;
+
+ if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd)
+ mmc_command_done(host->mmc, cmd->mrq);
+
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * The busy signal uses DAT0 so this is similar to waiting
+ * for data to complete.
+ *
+ * Note: The 1.0 specification is a bit ambiguous about this
+ * feature so there might be some problems with older
+ * controllers.
+ */
+ if (cmd->flags & MMC_RSP_BUSY) {
+ if (cmd->data) {
+ DBG("Cannot wait for busy signal when also doing a data transfer");
+ } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
+ cmd == host->data_cmd) {
+ /* Command complete before busy is ended */
+ return;
+ }
+ }
+
+ /* Processed actual command. */
+ if (host->data && host->data_early)
+ sdhci_uhs2_finish_data(host);
+
+ if (!cmd->data)
+ __sdhci_finish_mrq(host, cmd->mrq);
+}
+
+static void sdhci_uhs2_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ unsigned long flags;
+ bool present;
+
+ if (!(mmc_card_uhs2(mmc))) {
+ sdhci_request(mmc, mrq);
+ return;
+ }
+
+ mrq->stop = NULL;
+ mrq->sbc = NULL;
+ if (mrq->data)
+ mrq->data->stop = NULL;
+
+ /* Firstly check card presence */
+ present = mmc->ops->get_cd(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (sdhci_present_error(host, mrq->cmd, present))
+ goto out_finish;
+
+ cmd = mrq->cmd;
+
+ if (!sdhci_uhs2_send_command_retry(host, cmd, flags))
+ goto out_finish;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return;
+
+out_finish:
+ sdhci_finish_mrq(host, mrq);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
/*****************************************************************************\
* *
* Request done *
@@ -638,6 +1015,8 @@ static void sdhci_uhs2_complete_work(struct work_struct *work)
static void __sdhci_uhs2_irq(struct sdhci_host *host, u32 uhs2mask)
{
+ struct mmc_command *cmd = host->cmd;
+
DBG("*** %s got UHS2 error interrupt: 0x%08x\n",
mmc_hostname(host->mmc), uhs2mask);
@@ -677,6 +1056,12 @@ static void __sdhci_uhs2_irq(struct sdhci_host *host, u32 uhs2mask)
host->data->error = -EILSEQ;
}
}
+
+ if (host->data && host->data->error)
+ sdhci_uhs2_finish_data(host);
+ else
+ sdhci_finish_mrq(host, cmd->mrq);
+
}
u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask)
@@ -708,6 +1093,10 @@ u32 sdhci_uhs2_irq(struct sdhci_host *host, u32 intmask)
/* Clear command interrupt */
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS);
+ /* Handle command interrupt */
+ if (intmask & SDHCI_INT_RESPONSE)
+ sdhci_uhs2_finish_command(host);
+
/* Caller, sdhci_irq(), doesn't have to care about UHS-2 commands */
intmask &= ~SDHCI_INT_CMD_MASK;
mask &= SDHCI_INT_CMD_MASK;
@@ -740,6 +1129,8 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
host->thread_isr = 0;
cmd = host->deferred_cmd;
+ if (cmd && !sdhci_uhs2_send_command_retry(host, cmd, flags))
+ sdhci_finish_mrq(host, cmd->mrq);
spin_unlock_irqrestore(&host->lock, flags);
@@ -762,6 +1153,7 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
{
host->mmc_host_ops.uhs2_control = sdhci_uhs2_control;
+ host->mmc_host_ops.request = sdhci_uhs2_request;
return 0;
}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 871b4fe2a1b2..f4a7733a8ad2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -147,10 +147,11 @@ void sdhci_enable_v4_mode(struct sdhci_host *host)
}
EXPORT_SYMBOL_GPL(sdhci_enable_v4_mode);
-static inline bool sdhci_data_line_cmd(struct mmc_command *cmd)
+bool sdhci_data_line_cmd(struct mmc_command *cmd)
{
return cmd->data || cmd->flags & MMC_RSP_BUSY;
}
+EXPORT_SYMBOL_GPL(sdhci_data_line_cmd);
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
@@ -503,14 +504,15 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
#endif
-static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
- unsigned long timeout)
+void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
+ unsigned long timeout)
{
if (sdhci_data_line_cmd(mrq->cmd))
mod_timer(&host->data_timer, timeout);
else
mod_timer(&host->timer, timeout);
}
+EXPORT_SYMBOL_GPL(sdhci_mod_timer);
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
@@ -1077,8 +1079,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
__sdhci_set_timeout(host, cmd);
}
-static void sdhci_initialize_data(struct sdhci_host *host,
- struct mmc_data *data)
+void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data)
{
WARN_ON(host->data);
@@ -1091,6 +1092,7 @@ static void sdhci_initialize_data(struct sdhci_host *host,
host->data_early = 0;
host->data->bytes_xfered = 0;
}
+EXPORT_SYMBOL_GPL(sdhci_initialize_data);
static inline void sdhci_set_block_info(struct sdhci_host *host,
struct mmc_data *data)
@@ -1113,12 +1115,8 @@ static inline void sdhci_set_block_info(struct sdhci_host *host,
}
}
-static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data)
{
- struct mmc_data *data = cmd->data;
-
- sdhci_initialize_data(host, data);
-
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
struct scatterlist *sg;
unsigned int length_mask, offset_mask;
@@ -1203,6 +1201,16 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
}
sdhci_set_transfer_irqs(host);
+}
+EXPORT_SYMBOL_GPL(sdhci_prepare_dma);
+
+static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ struct mmc_data *data = cmd->data;
+
+ sdhci_initialize_data(host, data);
+
+ sdhci_prepare_dma(host, data);
sdhci_set_block_info(host, data);
}
@@ -1521,7 +1529,7 @@ static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
WARN_ON(i >= SDHCI_MAX_MRQS);
}
-static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
+void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
if (host->cmd && host->cmd->mrq == mrq)
host->cmd = NULL;
@@ -1545,15 +1553,17 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
if (!sdhci_has_requests(host))
sdhci_led_deactivate(host);
}
+EXPORT_SYMBOL_GPL(__sdhci_finish_mrq);
-static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
+void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
__sdhci_finish_mrq(host, mrq);
queue_work(host->complete_wq, &host->complete_work);
}
+EXPORT_SYMBOL_GPL(sdhci_finish_mrq);
-static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
+void __sdhci_finish_data_common(struct sdhci_host *host, bool defer_reset)
{
struct mmc_command *data_cmd = host->data_cmd;
struct mmc_data *data = host->data;
@@ -1566,7 +1576,9 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
* conditions.
*/
if (data->error) {
- if (!host->cmd || host->cmd == data_cmd)
+ if (defer_reset)
+ host->pending_reset = true;
+ else if (!host->cmd || host->cmd == data_cmd)
sdhci_reset_for(host, REQUEST_ERROR);
else
sdhci_reset_for(host, REQUEST_ERROR_DATA_ONLY);
@@ -1587,6 +1599,14 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
data->bytes_xfered = 0;
else
data->bytes_xfered = data->blksz * data->blocks;
+}
+EXPORT_SYMBOL_GPL(__sdhci_finish_data_common);
+
+static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
+{
+ struct mmc_data *data = host->data;
+
+ __sdhci_finish_data_common(host, false);
/*
* Need to send CMD12 if -
@@ -1721,8 +1741,8 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
return true;
}
-static bool sdhci_present_error(struct sdhci_host *host,
- struct mmc_command *cmd, bool present)
+bool sdhci_present_error(struct sdhci_host *host,
+ struct mmc_command *cmd, bool present)
{
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
cmd->error = -ENOMEDIUM;
@@ -1731,6 +1751,7 @@ static bool sdhci_present_error(struct sdhci_host *host,
return false;
}
+EXPORT_SYMBOL_GPL(sdhci_present_error);
static bool sdhci_send_command_retry(struct sdhci_host *host,
struct mmc_command *cmd,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 5f416bc783bd..c636808139d5 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -831,6 +831,14 @@ static inline void sdhci_read_caps(struct sdhci_host *host)
}
bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq);
+bool sdhci_data_line_cmd(struct mmc_command *cmd);
+void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq, unsigned long timeout);
+void sdhci_initialize_data(struct sdhci_host *host, struct mmc_data *data);
+void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data);
+void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
+void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq);
+void __sdhci_finish_data_common(struct sdhci_host *host, bool defer_reset);
+bool sdhci_present_error(struct sdhci_host *host, struct mmc_command *cmd, bool present);
u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
unsigned int *actual_clock);
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 13/16] mmc: sdhci-uhs2: add pre-detect_init hook
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (11 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 12/16] mmc: sdhci-uhs2: add request() " Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 14/16] mmc: sdhci-pci: add UHS-II support framework Victor Shih
` (4 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro
From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
This "pre" hook for detect_init(), uhs2_pre_detect_init, will be required
to enable UHS-II support, at least, on GL9755.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci-uhs2.c | 3 +++
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index d99ea05098cb..c488c6d56015 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -413,6 +413,9 @@ static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
DBG("Begin do uhs2 detect init.\n");
+ if (host->ops && host->ops->uhs2_pre_detect_init)
+ host->ops->uhs2_pre_detect_init(host);
+
if (sdhci_uhs2_interface_detect(host)) {
pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
return -EIO;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c636808139d5..cd0e35a80542 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -723,6 +723,7 @@ struct sdhci_ops {
struct mmc_request *mrq);
void (*dump_vendor_regs)(struct sdhci_host *host);
void (*dump_uhs2_regs)(struct sdhci_host *host);
+ void (*uhs2_pre_detect_init)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 14/16] mmc: sdhci-pci: add UHS-II support framework
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (12 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 13/16] mmc: sdhci-uhs2: add pre-detect_init hook Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-18 10:53 ` [PATCH V23 15/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755 Victor Shih
` (3 subsequent siblings)
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih
From: Victor Shih <victor.shih@genesyslogic.com.tw>
This patch prepares for adding UHS-II support at a specific UHS-II
capable sdhci-pci controller, GL9755 for now.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
Updates in V8:
- Add config select MMC_SDHCI_UHS2 in Kconfig.
---
drivers/mmc/host/Kconfig | 1 +
drivers/mmc/host/sdhci-pci-core.c | 16 +++++++++++++++-
drivers/mmc/host/sdhci-pci.h | 3 +++
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 45df8ddb8918..fec470225584 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -111,6 +111,7 @@ config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
select MMC_CQHCI
+ select MMC_SDHCI_UHS2
select IOSF_MBI if X86
select MMC_SDHCI_IO_ACCESSORS
help
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index ed45ed0bdafd..2b300bc4a701 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -40,6 +40,7 @@
#include "sdhci.h"
#include "sdhci-cqhci.h"
#include "sdhci-pci.h"
+#include "sdhci-uhs2.h"
static void sdhci_pci_hw_reset(struct sdhci_host *host);
@@ -2181,7 +2182,10 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
if (scratch == (u32)-1)
dead = 1;
- sdhci_remove_host(slot->host, dead);
+ if (slot->chip->fixes && slot->chip->fixes->remove_host)
+ slot->chip->fixes->remove_host(slot, dead);
+ else
+ sdhci_remove_host(slot->host, dead);
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
slot->chip->fixes->remove_slot(slot, dead);
@@ -2189,6 +2193,16 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
sdhci_free_host(slot->host);
}
+int sdhci_pci_uhs2_add_host(struct sdhci_pci_slot *slot)
+{
+ return sdhci_uhs2_add_host(slot->host);
+}
+
+void sdhci_pci_uhs2_remove_host(struct sdhci_pci_slot *slot, int dead)
+{
+ sdhci_uhs2_remove_host(slot->host, dead);
+}
+
static void sdhci_pci_runtime_pm_allow(struct device *dev)
{
pm_suspend_ignore_children(dev, 1);
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 153704f812ed..e807c039a8b1 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -145,6 +145,7 @@ struct sdhci_pci_fixes {
int (*probe_slot) (struct sdhci_pci_slot *);
int (*add_host) (struct sdhci_pci_slot *);
void (*remove_slot) (struct sdhci_pci_slot *, int);
+ void (*remove_host) (struct sdhci_pci_slot *, int);
#ifdef CONFIG_PM_SLEEP
int (*suspend) (struct sdhci_pci_chip *);
@@ -189,6 +190,8 @@ static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
return (void *)slot->private;
}
+int sdhci_pci_uhs2_add_host(struct sdhci_pci_slot *slot);
+void sdhci_pci_uhs2_remove_host(struct sdhci_pci_slot *slot, int dead);
#ifdef CONFIG_PM_SLEEP
int sdhci_pci_resume_host(struct sdhci_pci_chip *chip);
#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH V23 15/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (13 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 14/16] mmc: sdhci-pci: add UHS-II support framework Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2025-02-19 21:32 ` Janne Grunau
2024-10-18 10:53 ` [PATCH V23 16/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9767 Victor Shih
` (2 subsequent siblings)
17 siblings, 1 reply; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, AKASHI Takahiro, Victor Shih,
Lucas Lai
From: Victor Shih <victor.shih@genesyslogic.com.tw>
Changes are:
* Disable GL9755 overcurrent interrupt when power on/off on UHS-II.
* Enable the internal clock when do reset on UHS-II mode.
* Increase timeout value before detecting UHS-II interface.
* Add vendor settings fro UHS-II mode.
* Remove sdhci_gli_enable_internal_clock functon unused clk_ctrl variable.
* Make a function sdhci_gli_wait_software_reset_done() for gl9755 reset.
* Remove unnecessary code from sdhci_gl9755_reset().
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
Signed-off-by: Lucas Lai <lucas.lai@genesyslogic.com.tw>
---
Updates in V21:
- Remove unnecessary code from sdhci_gl9755_reset().
Updates in V20:
- Remove sdhci_gli_enable_internal_clock functon unused clk_ctrl variable.
- Make a function sdhci_gli_wait_software_reset_done() for gl9755 reset.
Updates in V19:
- Add sdhci_gli_enable_internal_clock() to simplify the code
in the sdhci_gl9755_reset().
Updates in V17:
- Rname gl9755_overcurrent_event_enable() to
sdhci_gli_overcurrent_event_enable().
Updates in V15:
- Adjust gl9755_vendor_init() to the correct function.
Updates in V13:
- Ues uhs2_sd_tran to stead MMC_UHS2_SD_TRAN.
Updates in V9:
- Rename gl9755_pre_detect_init() to sdhci_gli_pre_detect_init().
- Rename gl9755_uhs2_reset_sd_tran() to sdhci_gli_uhs2_reset_sd_tran().
Updates in V8:
- Use sdhci_get_vdd_value() to simplify code in gl9755_set_power().
- Use read_poll_timeout_atomic() to simplify code in
sdhci_wait_clock_stable().
- Use read_poll_timeout_atomic() to simplify code in sdhci_gl9755_reset().
Updates in V7:
- Drop using gl9755_post_attach_sd().
---
drivers/mmc/host/sdhci-pci-gli.c | 235 ++++++++++++++++++++++++++++++-
1 file changed, 234 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index 0f81586a19df..708138eecaa7 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -18,6 +18,7 @@
#include "sdhci-cqhci.h"
#include "sdhci-pci.h"
#include "cqhci.h"
+#include "sdhci-uhs2.h"
/* Genesys Logic extra registers */
#define SDHCI_GLI_9750_WT 0x800
@@ -139,9 +140,36 @@
#define PCI_GLI_9755_PLLSSC 0x68
#define PCI_GLI_9755_PLLSSC_PPM GENMASK(15, 0)
+#define PCI_GLI_9755_PLLSSC_RTL BIT(24)
+#define GLI_9755_PLLSSC_RTL_VALUE 0x1
+#define PCI_GLI_9755_PLLSSC_TRANS_PASS BIT(27)
+#define GLI_9755_PLLSSC_TRANS_PASS_VALUE 0x1
+#define PCI_GLI_9755_PLLSSC_RECV GENMASK(29, 28)
+#define GLI_9755_PLLSSC_RECV_VALUE 0x0
+#define PCI_GLI_9755_PLLSSC_TRAN GENMASK(31, 30)
+#define GLI_9755_PLLSSC_TRAN_VALUE 0x3
+
+#define PCI_GLI_9755_UHS2_PLL 0x6C
+#define PCI_GLI_9755_UHS2_PLL_SSC GENMASK(9, 8)
+#define GLI_9755_UHS2_PLL_SSC_VALUE 0x0
+#define PCI_GLI_9755_UHS2_PLL_DELAY BIT(18)
+#define GLI_9755_UHS2_PLL_DELAY_VALUE 0x1
+#define PCI_GLI_9755_UHS2_PLL_PDRST BIT(27)
+#define GLI_9755_UHS2_PLL_PDRST_VALUE 0x1
#define PCI_GLI_9755_SerDes 0x70
+#define PCI_GLI_9755_UHS2_SERDES_INTR GENMASK(2, 0)
+#define GLI_9755_UHS2_SERDES_INTR_VALUE 0x3
+#define PCI_GLI_9755_UHS2_SERDES_ZC1 BIT(3)
+#define GLI_9755_UHS2_SERDES_ZC1_VALUE 0x0
+#define PCI_GLI_9755_UHS2_SERDES_ZC2 GENMASK(7, 4)
+#define GLI_9755_UHS2_SERDES_ZC2_DEFAULT 0xB
+#define GLI_9755_UHS2_SERDES_ZC2_SANDISK 0x0
#define PCI_GLI_9755_SCP_DIS BIT(19)
+#define PCI_GLI_9755_UHS2_SERDES_TRAN GENMASK(27, 24)
+#define GLI_9755_UHS2_SERDES_TRAN_VALUE 0xC
+#define PCI_GLI_9755_UHS2_SERDES_RECV GENMASK(31, 28)
+#define GLI_9755_UHS2_SERDES_RECV_VALUE 0xF
#define PCI_GLI_9755_MISC 0x78
#define PCI_GLI_9755_MISC_SSC_OFF BIT(26)
@@ -779,6 +807,203 @@ static void gl9755_hw_setting(struct sdhci_pci_slot *slot)
gl9755_wt_off(pdev);
}
+static void gl9755_vendor_init(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ u32 serdes;
+ u32 pllssc;
+ u32 uhs2_pll;
+
+ gl9755_wt_on(pdev);
+
+ pci_read_config_dword(pdev, PCI_GLI_9755_SerDes, &serdes);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_TRAN;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_TRAN,
+ GLI_9755_UHS2_SERDES_TRAN_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_RECV;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_RECV,
+ GLI_9755_UHS2_SERDES_RECV_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_INTR;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_INTR,
+ GLI_9755_UHS2_SERDES_INTR_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_ZC1;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_ZC1,
+ GLI_9755_UHS2_SERDES_ZC1_VALUE);
+ serdes &= ~PCI_GLI_9755_UHS2_SERDES_ZC2;
+ serdes |= FIELD_PREP(PCI_GLI_9755_UHS2_SERDES_ZC2,
+ GLI_9755_UHS2_SERDES_ZC2_DEFAULT);
+ pci_write_config_dword(pdev, PCI_GLI_9755_SerDes, serdes);
+
+ pci_read_config_dword(pdev, PCI_GLI_9755_UHS2_PLL, &uhs2_pll);
+ uhs2_pll &= ~PCI_GLI_9755_UHS2_PLL_SSC;
+ uhs2_pll |= FIELD_PREP(PCI_GLI_9755_UHS2_PLL_SSC,
+ GLI_9755_UHS2_PLL_SSC_VALUE);
+ uhs2_pll &= ~PCI_GLI_9755_UHS2_PLL_DELAY;
+ uhs2_pll |= FIELD_PREP(PCI_GLI_9755_UHS2_PLL_DELAY,
+ GLI_9755_UHS2_PLL_DELAY_VALUE);
+ uhs2_pll &= ~PCI_GLI_9755_UHS2_PLL_PDRST;
+ uhs2_pll |= FIELD_PREP(PCI_GLI_9755_UHS2_PLL_PDRST,
+ GLI_9755_UHS2_PLL_PDRST_VALUE);
+ pci_write_config_dword(pdev, PCI_GLI_9755_UHS2_PLL, uhs2_pll);
+
+ pci_read_config_dword(pdev, PCI_GLI_9755_PLLSSC, &pllssc);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_RTL;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_RTL,
+ GLI_9755_PLLSSC_RTL_VALUE);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_TRANS_PASS;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_TRANS_PASS,
+ GLI_9755_PLLSSC_TRANS_PASS_VALUE);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_RECV;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_RECV,
+ GLI_9755_PLLSSC_RECV_VALUE);
+ pllssc &= ~PCI_GLI_9755_PLLSSC_TRAN;
+ pllssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_TRAN,
+ GLI_9755_PLLSSC_TRAN_VALUE);
+ pci_write_config_dword(pdev, PCI_GLI_9755_PLLSSC, pllssc);
+
+ gl9755_wt_off(pdev);
+}
+
+static void sdhci_gli_pre_detect_init(struct sdhci_host *host)
+{
+ /* Need more time on UHS2 detect flow */
+ sdhci_writeb(host, 0xA7, SDHCI_UHS2_TIMER_CTRL);
+}
+
+static void sdhci_gli_overcurrent_event_enable(struct sdhci_host *host, bool enable)
+{
+ u32 mask;
+
+ mask = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
+ if (enable)
+ mask |= SDHCI_INT_BUS_POWER;
+ else
+ mask &= ~SDHCI_INT_BUS_POWER;
+
+ sdhci_writel(host, mask, SDHCI_SIGNAL_ENABLE);
+
+ mask = sdhci_readl(host, SDHCI_INT_ENABLE);
+ if (enable)
+ mask |= SDHCI_INT_BUS_POWER;
+ else
+ mask &= ~SDHCI_INT_BUS_POWER;
+
+ sdhci_writel(host, mask, SDHCI_INT_ENABLE);
+}
+
+static void gl9755_set_power(struct sdhci_host *host, unsigned char mode,
+ unsigned short vdd)
+{
+ u8 pwr = 0;
+
+ if (mode != MMC_POWER_OFF) {
+ pwr = sdhci_get_vdd_value(vdd);
+ if (!pwr)
+ WARN(1, "%s: Invalid vdd %#x\n", mmc_hostname(host->mmc), vdd);
+ pwr |= SDHCI_VDD2_POWER_180;
+ }
+
+ if (host->pwr == pwr)
+ return;
+
+ host->pwr = pwr;
+
+ if (pwr == 0) {
+ sdhci_gli_overcurrent_event_enable(host, false);
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ } else {
+ sdhci_gli_overcurrent_event_enable(host, false);
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+ pwr |= (SDHCI_POWER_ON | SDHCI_VDD2_POWER_ON);
+
+ sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
+ /* wait stable */
+ mdelay(5);
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ /* wait stable */
+ mdelay(5);
+ sdhci_gli_overcurrent_event_enable(host, true);
+ }
+}
+
+static bool sdhci_wait_clock_stable(struct sdhci_host *host)
+{
+ u16 clk = 0;
+
+ if (read_poll_timeout_atomic(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE),
+ 10, 20000, false, host, SDHCI_CLOCK_CONTROL)) {
+ pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return false;
+ }
+ return true;
+}
+
+static void sdhci_gli_enable_internal_clock(struct sdhci_host *host)
+{
+ u16 ctrl2;
+
+ ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ sdhci_writew(host, SDHCI_CLOCK_INT_EN, SDHCI_CLOCK_CONTROL);
+
+ if (!((ctrl2 & SDHCI_CTRL_V4_MODE) &&
+ (ctrl2 & SDHCI_CTRL_UHS2_ENABLE))) {
+ sdhci_wait_clock_stable(host);
+ sdhci_writew(host, SDHCI_CTRL_V4_MODE, SDHCI_HOST_CONTROL2);
+ }
+}
+
+static int sdhci_gli_wait_software_reset_done(struct sdhci_host *host, u8 mask)
+{
+ u8 rst;
+
+ /* hw clears the bit when it's done */
+ if (read_poll_timeout_atomic(sdhci_readb, rst, !(rst & mask),
+ 10, 100000, false, host, SDHCI_SOFTWARE_RESET)) {
+ pr_err("%s: Reset 0x%x never completed.\n", mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ /* manual clear */
+ sdhci_writeb(host, 0, SDHCI_SOFTWARE_RESET);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void sdhci_gli_uhs2_reset_sd_tran(struct sdhci_host *host)
+{
+ /* do this on UHS2 mode */
+ if (host->mmc->uhs2_sd_tran) {
+ sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_SD);
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ sdhci_uhs2_clear_set_irqs(host,
+ SDHCI_INT_ALL_MASK,
+ SDHCI_UHS2_INT_ERROR_MASK);
+ }
+}
+
+static void sdhci_gl9755_reset(struct sdhci_host *host, u8 mask)
+{
+ /* need internal clock */
+ if (mask & SDHCI_RESET_ALL)
+ sdhci_gli_enable_internal_clock(host);
+
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+
+ /* reset sd-tran on UHS2 mode if need to reset cmd/data */
+ if ((mask & SDHCI_RESET_CMD) | (mask & SDHCI_RESET_DATA))
+ sdhci_gli_uhs2_reset_sd_tran(host);
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+
+ sdhci_gli_wait_software_reset_done(host, mask);
+}
+
static inline void gl9767_vhs_read(struct pci_dev *pdev)
{
u32 vhs_enable;
@@ -1086,6 +1311,7 @@ static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
gli_pcie_enable_msi(slot);
slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
sdhci_enable_v4_mode(host);
+ gl9755_vendor_init(host);
return 0;
}
@@ -1524,17 +1750,24 @@ static const struct sdhci_ops sdhci_gl9755_ops = {
.read_w = sdhci_gli_readw,
.read_b = sdhci_gli_readb,
.set_clock = sdhci_gl9755_set_clock,
+ .set_power = gl9755_set_power,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_reset,
+ .reset = sdhci_gl9755_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.voltage_switch = sdhci_gli_voltage_switch,
+ .dump_uhs2_regs = sdhci_uhs2_dump_regs,
+ .set_timeout = sdhci_uhs2_set_timeout,
+ .irq = sdhci_uhs2_irq,
+ .uhs2_pre_detect_init = sdhci_gli_pre_detect_init,
};
const struct sdhci_pci_fixes sdhci_gl9755 = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50,
.probe_slot = gli_probe_slot_gl9755,
+ .add_host = sdhci_pci_uhs2_add_host,
+ .remove_host = sdhci_pci_uhs2_remove_host,
.ops = &sdhci_gl9755_ops,
#ifdef CONFIG_PM_SLEEP
.resume = sdhci_pci_gli_resume,
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH V23 15/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
2024-10-18 10:53 ` [PATCH V23 15/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755 Victor Shih
@ 2025-02-19 21:32 ` Janne Grunau
2025-03-05 0:56 ` Ben Chuang
0 siblings, 1 reply; 27+ messages in thread
From: Janne Grunau @ 2025-02-19 21:32 UTC (permalink / raw)
To: Victor Shih
Cc: ulf.hansson, adrian.hunter, linux-mmc, linux-kernel, benchuanggli,
Lucas.Lai, HL.Liu, Greg.tu, dlunev, Ben Chuang, AKASHI Takahiro,
Victor Shih, asahi
Hej,
On Fri, Oct 18, 2024 at 06:53:32PM +0800, Victor Shih wrote:
> From: Victor Shih <victor.shih@genesyslogic.com.tw>
>
> Changes are:
> * Disable GL9755 overcurrent interrupt when power on/off on UHS-II.
> * Enable the internal clock when do reset on UHS-II mode.
> * Increase timeout value before detecting UHS-II interface.
> * Add vendor settings fro UHS-II mode.
> * Remove sdhci_gli_enable_internal_clock functon unused clk_ctrl variable.
> * Make a function sdhci_gli_wait_software_reset_done() for gl9755 reset.
> * Remove unnecessary code from sdhci_gl9755_reset().
>
> Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
> Signed-off-by: Lucas Lai <lucas.lai@genesyslogic.com.tw>
> ---
>
> drivers/mmc/host/sdhci-pci-gli.c | 235 ++++++++++++++++++++++++++++++-
> 1 file changed, 234 insertions(+), 1 deletion(-)
This change results in error messages / timeout about UHS2 followed by
register dumps with the GL9755 integrated in Apple silicon Macbook Pros
and Mac Studio systems. Non UHS-II function of controller does not seem
to be affected. Apple advertises the the SDXC slot as UHS-II capable.
The only quirk we've experienced with gl9755 on this platform is that 8
and 16 bit MMIO reads do not work. Workaround added in commit
c064bb5c78c1b ("mmc: sdhci-pci-gli: GL975[50]: Issue 8/16-bit MMIO reads
as 32-bit reads.").
If you have ideas or patches to try I'm happy to do that. If not we can
look into what MacOS does.
See kernel log and lspci output below
Thanks,
Janne
[ 38.130033] kernel: sdhci: Secure Digital Host Controller Interface driver
[ 38.130141] kernel: sdhci: Copyright(c) Pierre Ossman
[ 38.133352] kernel: sdhci-pci 0000:02:00.0: Adding to iommu group 13
[ 38.160551] kernel: sdhci-pci 0000:02:00.0: SDHCI controller found [17a0:9755] (rev 1)
[ 38.160655] kernel: sdhci-pci 0000:02:00.0: enabling device (0000 -> 0002)
[ 38.160750] kernel: mmc0: SDHCI controller on PCI [0000:02:00.0] using ADMA 64-bit
[ 38.274617] kernel: mmc0: not detect UHS2 interface in 100ms.
[ 38.274717] kernel: mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
[ 38.274782] kernel: mmc0: sdhci: Sys addr: 0x00000000 | Version: 0x00000005
[ 38.277391] kernel: mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
[ 38.277475] kernel: mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
[ 38.280125] kernel: mmc0: sdhci: Present: 0x20070000 | Host ctl: 0x00000000
[ 38.280206] kernel: mmc0: sdhci: Power: 0x000000bf | Blk gap: 0x00000000
[ 38.284511] kernel: mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x0000032f
[ 38.284592] kernel: mmc0: sdhci: Timeout: 0x00000007 | Int stat: 0x00000000
[ 38.284636] kernel: mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
[ 38.287200] kernel: mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
[ 38.287281] kernel: mmc0: sdhci: Caps: 0x396a3281 | Caps_1: 0x1803057f
[ 38.291212] kernel: mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x000000c8
[ 38.291292] kernel: mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
[ 38.291335] kernel: mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
[ 38.293513] kernel: mmc0: sdhci: Host ctl2: 0x00009107
[ 38.293604] kernel: mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000
[ 38.297842] kernel: mmc0: sdhci_uhs2: ==================== UHS2 ==================
[ 38.297923] kernel: mmc0: sdhci_uhs2: Blk Size: 0x00000000 | Blk Cnt: 0x00000000
[ 38.297968] kernel: mmc0: sdhci_uhs2: Cmd: 0x00000000 | Trn mode: 0x00000000
[ 38.300773] kernel: mmc0: sdhci_uhs2: Int Stat: 0x00000000 | Dev Sel : 0x00000000
[ 38.300853] kernel: mmc0: sdhci_uhs2: Dev Int Code: 0x00000000
[ 38.304739] kernel: mmc0: sdhci_uhs2: Reset: 0x00000000 | Timer: 0x000000a7
[ 38.304811] kernel: mmc0: sdhci_uhs2: ErrInt: 0x00000000 | ErrIntEn: 0x00030000
[ 38.304856] kernel: mmc0: sdhci_uhs2: ErrSigEn: 0x00030000
[ 38.307110] kernel: mmc0: sdhci: ============================================
[ 38.307201] kernel: mmc0: cannot detect UHS2 interface.
[ 38.310110] kernel: mmc0: failed to initial phy for UHS-II!
[ 38.424645] kernel: mmc0: not detect UHS2 interface in 100ms.
[ 38.424731] kernel: mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
[ 38.424758] kernel: mmc0: sdhci: Sys addr: 0x00000000 | Version: 0x00000005
[ 38.424782] kernel: mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
[ 38.424840] kernel: mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
[ 38.427603] kernel: mmc0: sdhci: Present: 0x20070000 | Host ctl: 0x00000000
[ 38.427659] kernel: mmc0: sdhci: Power: 0x000000bf | Blk gap: 0x00000000
[ 38.430579] kernel: mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x0000032f
[ 38.430626] kernel: mmc0: sdhci: Timeout: 0x00000007 | Int stat: 0x00000000
[ 38.433504] kernel: mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
[ 38.433550] kernel: mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
[ 38.437596] kernel: mmc0: sdhci: Caps: 0x396a3281 | Caps_1: 0x1803057f
[ 38.437641] kernel: mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x000000c8
[ 38.437677] kernel: mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
[ 38.440318] kernel: mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
[ 38.440345] kernel: mmc0: sdhci: Host ctl2: 0x00009107
[ 38.444119] kernel: mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000
[ 38.444161] kernel: mmc0: sdhci_uhs2: ==================== UHS2 ==================
[ 38.444184] kernel: mmc0: sdhci_uhs2: Blk Size: 0x00000000 | Blk Cnt: 0x00000000
[ 38.446918] kernel: mmc0: sdhci_uhs2: Cmd: 0x00000000 | Trn mode: 0x00000000
[ 38.446958] kernel: mmc0: sdhci_uhs2: Int Stat: 0x00000000 | Dev Sel : 0x00000000
[ 38.450833] kernel: mmc0: sdhci_uhs2: Dev Int Code: 0x00000000
[ 38.450874] kernel: mmc0: sdhci_uhs2: Reset: 0x00000000 | Timer: 0x000000a7
[ 38.450907] kernel: mmc0: sdhci_uhs2: ErrInt: 0x00000000 | ErrIntEn: 0x00030000
[ 38.454625] kernel: mmc0: sdhci_uhs2: ErrSigEn: 0x00030000
[ 38.454665] kernel: mmc0: sdhci: ============================================
[ 38.454699] kernel: mmc0: cannot detect UHS2 interface.
[ 38.456705] kernel: mmc0: failed to initial phy for UHS-II!
`lspci -vvvnn -d 17a0:9755` output:
02:00.0 SD Host controller [0805]: Genesys Logic, Inc GL9755 SD Host Controller [17a0:9755] (rev 01) (prog-if 01)
Subsystem: Genesys Logic, Inc GL9755 SD Host Controller [17a0:9755]
Device tree node: /sys/firmware/devicetree/base/soc@200000000/pcie@590000000/pci@1,0/mmc@0,0
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 168
IOMMU group: 13
Region 0: Memory at 5c1e00000 (32-bit, non-prefetchable) [size=4K]
Capabilities: [80] Express (v2) Endpoint, IntMsgNum 0
DevCap: MaxPayload 256 bytes, PhantFunc 0, Latency L0s <4us, L1 unlimited
ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset- SlotPowerLimit 0W TEE-IO-
DevCtl: CorrErr+ NonFatalErr+ FatalErr+ UnsupReq+
RlxdOrd+ ExtTag+ PhantFunc- AuxPwr- NoSnoop+
MaxPayload 128 bytes, MaxReadReq 512 bytes
DevSta: CorrErr- NonFatalErr- FatalErr- UnsupReq- AuxPwr+ TransPend-
LnkCap: Port #85, Speed 5GT/s, Width x1, ASPM L0s L1, Exit Latency L0s <4us, L1 unlimited
ClockPM+ Surprise- LLActRep- BwNot- ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes, LnkDisable- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 5GT/s, Width x1
TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Range ABCD, TimeoutDis+ NROPrPrP- LTR+
10BitTagComp- 10BitTagReq- OBFF Via message/WAKE#, ExtFmt- EETLPPrefix-
EmergencyPowerReduction Not Supported, EmergencyPowerReductionInit-
FRS- TPHComp- ExtTPHComp-
AtomicOpsCap: 32bit- 64bit- 128bitCAS-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-
AtomicOpsCtl: ReqEn-
IDOReq- IDOCompl- LTR+ EmergencyPowerReductionReq-
10BitTagReq- OBFF Disabled, EETLPPrefixBlk-
LnkCap2: Supported Link Speeds: 2.5-5GT/s, Crosslink- Retimer- 2Retimers- DRS-
LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance Preset/De-emphasis: -6dB de-emphasis, 0dB preshoot
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete- EqualizationPhase1-
EqualizationPhase2- EqualizationPhase3- LinkEqualizationRequest-
Retimer- 2Retimers- CrosslinkRes: unsupported
Capabilities: [e0] MSI: Enable+ Count=1/1 Maskable- 64bit+
Address: 00000000fffff000 Data: 0018
Capabilities: [f8] Power Management version 3
Flags: PMEClk- DSI- D1+ D2+ AuxCurrent=375mA PME(D0-,D1+,D2+,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME+
Capabilities: [100 v1] Vendor Specific Information: ID=17a0 Rev=1 Len=008 <?>
Capabilities: [108 v1] Latency Tolerance Reporting
Max snoop latency: 0ns
Max no snoop latency: 0ns
Capabilities: [110 v1] L1 PM Substates
L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
PortCommonModeRestoreTime=255us PortTPowerOnTime=3100us
L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2- ASPM_L1.1-
T_CommonMode=0us LTR1.2_Threshold=3375104ns
L1SubCtl2: T_PwrOn=3100us
Capabilities: [200 v1] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
ECRC- UnsupReq- ACSViol- UncorrIntErr- BlockedTLP- AtomicOpBlocked- TLPBlockedErr-
PoisonTLPBlocked- DMWrReqBlocked- IDECheck- MisIDETLP- PCRC_CHECK- TLPXlatBlocked-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
ECRC- UnsupReq- ACSViol- UncorrIntErr- BlockedTLP- AtomicOpBlocked- TLPBlockedErr-
PoisonTLPBlocked- DMWrReqBlocked- IDECheck- MisIDETLP- PCRC_CHECK- TLPXlatBlocked-
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+
ECRC- UnsupReq- ACSViol- UncorrIntErr- BlockedTLP- AtomicOpBlocked- TLPBlockedErr-
PoisonTLPBlocked- DMWrReqBlocked- IDECheck- MisIDETLP- PCRC_CHECK- TLPXlatBlocked-
CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- AdvNonFatalErr- CorrIntErr- HeaderOF-
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout+ AdvNonFatalErr+ CorrIntErr- HeaderOF-
AERCap: First Error Pointer: 00, ECRCGenCap+ ECRCGenEn- ECRCChkCap+ ECRCChkEn-
MultHdrRecCap- MultHdrRecEn- TLPPfxPres- HdrLogCap-
HeaderLog: 00000000 00000000 00000000 00000000
Kernel driver in use: sdhci-pci
Kernel modules: sdhci_pci
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH V23 15/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
2025-02-19 21:32 ` Janne Grunau
@ 2025-03-05 0:56 ` Ben Chuang
2025-03-06 14:27 ` Adrian Hunter
0 siblings, 1 reply; 27+ messages in thread
From: Ben Chuang @ 2025-03-05 0:56 UTC (permalink / raw)
To: Janne Grunau
Cc: Victor Shih, ulf.hansson, adrian.hunter, linux-mmc, linux-kernel,
Lucas.Lai, HL.Liu, Greg.tu, dlunev, Ben Chuang, AKASHI Takahiro,
Victor Shih, asahi
Hi Janne,
On Thu, Feb 20, 2025 at 5:32 AM Janne Grunau <j@jannau.net> wrote:
>
> Hej,
>
> On Fri, Oct 18, 2024 at 06:53:32PM +0800, Victor Shih wrote:
> > From: Victor Shih <victor.shih@genesyslogic.com.tw>
> >
> > Changes are:
> > * Disable GL9755 overcurrent interrupt when power on/off on UHS-II.
> > * Enable the internal clock when do reset on UHS-II mode.
> > * Increase timeout value before detecting UHS-II interface.
> > * Add vendor settings fro UHS-II mode.
> > * Remove sdhci_gli_enable_internal_clock functon unused clk_ctrl variable.
> > * Make a function sdhci_gli_wait_software_reset_done() for gl9755 reset.
> > * Remove unnecessary code from sdhci_gl9755_reset().
> >
> > Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
> > Signed-off-by: Lucas Lai <lucas.lai@genesyslogic.com.tw>
> > ---
> >
> > drivers/mmc/host/sdhci-pci-gli.c | 235 ++++++++++++++++++++++++++++++-
> > 1 file changed, 234 insertions(+), 1 deletion(-)
>
> This change results in error messages / timeout about UHS2 followed by
> register dumps with the GL9755 integrated in Apple silicon Macbook Pros
> and Mac Studio systems. Non UHS-II function of controller does not seem
> to be affected. Apple advertises the the SDXC slot as UHS-II capable.
>
> The only quirk we've experienced with gl9755 on this platform is that 8
> and 16 bit MMIO reads do not work. Workaround added in commit
> c064bb5c78c1b ("mmc: sdhci-pci-gli: GL975[50]: Issue 8/16-bit MMIO reads
> as 32-bit reads.").
>
> If you have ideas or patches to try I'm happy to do that. If not we can
> look into what MacOS does.
>
> See kernel log and lspci output below
>
> Thanks,
> Janne
>
> [ 38.130033] kernel: sdhci: Secure Digital Host Controller Interface driver
> [ 38.130141] kernel: sdhci: Copyright(c) Pierre Ossman
> [ 38.133352] kernel: sdhci-pci 0000:02:00.0: Adding to iommu group 13
> [ 38.160551] kernel: sdhci-pci 0000:02:00.0: SDHCI controller found [17a0:9755] (rev 1)
> [ 38.160655] kernel: sdhci-pci 0000:02:00.0: enabling device (0000 -> 0002)
> [ 38.160750] kernel: mmc0: SDHCI controller on PCI [0000:02:00.0] using ADMA 64-bit
> [ 38.274617] kernel: mmc0: not detect UHS2 interface in 100ms.
> [ 38.274717] kernel: mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
> [ 38.274782] kernel: mmc0: sdhci: Sys addr: 0x00000000 | Version: 0x00000005
> [ 38.277391] kernel: mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
> [ 38.277475] kernel: mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
> [ 38.280125] kernel: mmc0: sdhci: Present: 0x20070000 | Host ctl: 0x00000000
> [ 38.280206] kernel: mmc0: sdhci: Power: 0x000000bf | Blk gap: 0x00000000
> [ 38.284511] kernel: mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x0000032f
> [ 38.284592] kernel: mmc0: sdhci: Timeout: 0x00000007 | Int stat: 0x00000000
> [ 38.284636] kernel: mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
> [ 38.287200] kernel: mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
> [ 38.287281] kernel: mmc0: sdhci: Caps: 0x396a3281 | Caps_1: 0x1803057f
> [ 38.291212] kernel: mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x000000c8
> [ 38.291292] kernel: mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
> [ 38.291335] kernel: mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
> [ 38.293513] kernel: mmc0: sdhci: Host ctl2: 0x00009107
> [ 38.293604] kernel: mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000
> [ 38.297842] kernel: mmc0: sdhci_uhs2: ==================== UHS2 ==================
> [ 38.297923] kernel: mmc0: sdhci_uhs2: Blk Size: 0x00000000 | Blk Cnt: 0x00000000
> [ 38.297968] kernel: mmc0: sdhci_uhs2: Cmd: 0x00000000 | Trn mode: 0x00000000
> [ 38.300773] kernel: mmc0: sdhci_uhs2: Int Stat: 0x00000000 | Dev Sel : 0x00000000
> [ 38.300853] kernel: mmc0: sdhci_uhs2: Dev Int Code: 0x00000000
> [ 38.304739] kernel: mmc0: sdhci_uhs2: Reset: 0x00000000 | Timer: 0x000000a7
> [ 38.304811] kernel: mmc0: sdhci_uhs2: ErrInt: 0x00000000 | ErrIntEn: 0x00030000
> [ 38.304856] kernel: mmc0: sdhci_uhs2: ErrSigEn: 0x00030000
> [ 38.307110] kernel: mmc0: sdhci: ============================================
> [ 38.307201] kernel: mmc0: cannot detect UHS2 interface.
> [ 38.310110] kernel: mmc0: failed to initial phy for UHS-II!
> [ 38.424645] kernel: mmc0: not detect UHS2 interface in 100ms.
> [ 38.424731] kernel: mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
> [ 38.424758] kernel: mmc0: sdhci: Sys addr: 0x00000000 | Version: 0x00000005
> [ 38.424782] kernel: mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
> [ 38.424840] kernel: mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
> [ 38.427603] kernel: mmc0: sdhci: Present: 0x20070000 | Host ctl: 0x00000000
> [ 38.427659] kernel: mmc0: sdhci: Power: 0x000000bf | Blk gap: 0x00000000
> [ 38.430579] kernel: mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x0000032f
> [ 38.430626] kernel: mmc0: sdhci: Timeout: 0x00000007 | Int stat: 0x00000000
> [ 38.433504] kernel: mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
> [ 38.433550] kernel: mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
> [ 38.437596] kernel: mmc0: sdhci: Caps: 0x396a3281 | Caps_1: 0x1803057f
> [ 38.437641] kernel: mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x000000c8
> [ 38.437677] kernel: mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
> [ 38.440318] kernel: mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
> [ 38.440345] kernel: mmc0: sdhci: Host ctl2: 0x00009107
> [ 38.444119] kernel: mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000
> [ 38.444161] kernel: mmc0: sdhci_uhs2: ==================== UHS2 ==================
> [ 38.444184] kernel: mmc0: sdhci_uhs2: Blk Size: 0x00000000 | Blk Cnt: 0x00000000
> [ 38.446918] kernel: mmc0: sdhci_uhs2: Cmd: 0x00000000 | Trn mode: 0x00000000
> [ 38.446958] kernel: mmc0: sdhci_uhs2: Int Stat: 0x00000000 | Dev Sel : 0x00000000
> [ 38.450833] kernel: mmc0: sdhci_uhs2: Dev Int Code: 0x00000000
> [ 38.450874] kernel: mmc0: sdhci_uhs2: Reset: 0x00000000 | Timer: 0x000000a7
> [ 38.450907] kernel: mmc0: sdhci_uhs2: ErrInt: 0x00000000 | ErrIntEn: 0x00030000
> [ 38.454625] kernel: mmc0: sdhci_uhs2: ErrSigEn: 0x00030000
> [ 38.454665] kernel: mmc0: sdhci: ============================================
> [ 38.454699] kernel: mmc0: cannot detect UHS2 interface.
> [ 38.456705] kernel: mmc0: failed to initial phy for UHS-II!
>
On which UHS-II card does this message appear? Please share the card
information.
If it is a UHS-I card, this message is normal.
Best regards,
Ben Chuang
>
> `lspci -vvvnn -d 17a0:9755` output:
> 02:00.0 SD Host controller [0805]: Genesys Logic, Inc GL9755 SD Host Controller [17a0:9755] (rev 01) (prog-if 01)
> Subsystem: Genesys Logic, Inc GL9755 SD Host Controller [17a0:9755]
> Device tree node: /sys/firmware/devicetree/base/soc@200000000/pcie@590000000/pci@1,0/mmc@0,0
> Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
> Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
> Latency: 0
> Interrupt: pin A routed to IRQ 168
> IOMMU group: 13
> Region 0: Memory at 5c1e00000 (32-bit, non-prefetchable) [size=4K]
> Capabilities: [80] Express (v2) Endpoint, IntMsgNum 0
> DevCap: MaxPayload 256 bytes, PhantFunc 0, Latency L0s <4us, L1 unlimited
> ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset- SlotPowerLimit 0W TEE-IO-
> DevCtl: CorrErr+ NonFatalErr+ FatalErr+ UnsupReq+
> RlxdOrd+ ExtTag+ PhantFunc- AuxPwr- NoSnoop+
> MaxPayload 128 bytes, MaxReadReq 512 bytes
> DevSta: CorrErr- NonFatalErr- FatalErr- UnsupReq- AuxPwr+ TransPend-
> LnkCap: Port #85, Speed 5GT/s, Width x1, ASPM L0s L1, Exit Latency L0s <4us, L1 unlimited
> ClockPM+ Surprise- LLActRep- BwNot- ASPMOptComp+
> LnkCtl: ASPM Disabled; RCB 64 bytes, LnkDisable- CommClk+
> ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
> LnkSta: Speed 5GT/s, Width x1
> TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
> DevCap2: Completion Timeout: Range ABCD, TimeoutDis+ NROPrPrP- LTR+
> 10BitTagComp- 10BitTagReq- OBFF Via message/WAKE#, ExtFmt- EETLPPrefix-
> EmergencyPowerReduction Not Supported, EmergencyPowerReductionInit-
> FRS- TPHComp- ExtTPHComp-
> AtomicOpsCap: 32bit- 64bit- 128bitCAS-
> DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-
> AtomicOpsCtl: ReqEn-
> IDOReq- IDOCompl- LTR+ EmergencyPowerReductionReq-
> 10BitTagReq- OBFF Disabled, EETLPPrefixBlk-
> LnkCap2: Supported Link Speeds: 2.5-5GT/s, Crosslink- Retimer- 2Retimers- DRS-
> LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-
> Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
> Compliance Preset/De-emphasis: -6dB de-emphasis, 0dB preshoot
> LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete- EqualizationPhase1-
> EqualizationPhase2- EqualizationPhase3- LinkEqualizationRequest-
> Retimer- 2Retimers- CrosslinkRes: unsupported
> Capabilities: [e0] MSI: Enable+ Count=1/1 Maskable- 64bit+
> Address: 00000000fffff000 Data: 0018
> Capabilities: [f8] Power Management version 3
> Flags: PMEClk- DSI- D1+ D2+ AuxCurrent=375mA PME(D0-,D1+,D2+,D3hot+,D3cold+)
> Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME+
> Capabilities: [100 v1] Vendor Specific Information: ID=17a0 Rev=1 Len=008 <?>
> Capabilities: [108 v1] Latency Tolerance Reporting
> Max snoop latency: 0ns
> Max no snoop latency: 0ns
> Capabilities: [110 v1] L1 PM Substates
> L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
> PortCommonModeRestoreTime=255us PortTPowerOnTime=3100us
> L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2- ASPM_L1.1-
> T_CommonMode=0us LTR1.2_Threshold=3375104ns
> L1SubCtl2: T_PwrOn=3100us
> Capabilities: [200 v1] Advanced Error Reporting
> UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
> ECRC- UnsupReq- ACSViol- UncorrIntErr- BlockedTLP- AtomicOpBlocked- TLPBlockedErr-
> PoisonTLPBlocked- DMWrReqBlocked- IDECheck- MisIDETLP- PCRC_CHECK- TLPXlatBlocked-
> UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP-
> ECRC- UnsupReq- ACSViol- UncorrIntErr- BlockedTLP- AtomicOpBlocked- TLPBlockedErr-
> PoisonTLPBlocked- DMWrReqBlocked- IDECheck- MisIDETLP- PCRC_CHECK- TLPXlatBlocked-
> UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+
> ECRC- UnsupReq- ACSViol- UncorrIntErr- BlockedTLP- AtomicOpBlocked- TLPBlockedErr-
> PoisonTLPBlocked- DMWrReqBlocked- IDECheck- MisIDETLP- PCRC_CHECK- TLPXlatBlocked-
> CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- AdvNonFatalErr- CorrIntErr- HeaderOF-
> CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout+ AdvNonFatalErr+ CorrIntErr- HeaderOF-
> AERCap: First Error Pointer: 00, ECRCGenCap+ ECRCGenEn- ECRCChkCap+ ECRCChkEn-
> MultHdrRecCap- MultHdrRecEn- TLPPfxPres- HdrLogCap-
> HeaderLog: 00000000 00000000 00000000 00000000
> Kernel driver in use: sdhci-pci
> Kernel modules: sdhci_pci
>
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH V23 15/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755
2025-03-05 0:56 ` Ben Chuang
@ 2025-03-06 14:27 ` Adrian Hunter
0 siblings, 0 replies; 27+ messages in thread
From: Adrian Hunter @ 2025-03-06 14:27 UTC (permalink / raw)
To: Ben Chuang
Cc: Victor Shih, ulf.hansson, linux-mmc, linux-kernel, Lucas.Lai,
HL.Liu, Greg.tu, dlunev, Ben Chuang, AKASHI Takahiro, Victor Shih,
asahi, Janne Grunau
On 5/03/25 02:56, Ben Chuang wrote:
> Hi Janne,
>
> On Thu, Feb 20, 2025 at 5:32 AM Janne Grunau <j@jannau.net> wrote:
>>
>> Hej,
>>
>> On Fri, Oct 18, 2024 at 06:53:32PM +0800, Victor Shih wrote:
>>> From: Victor Shih <victor.shih@genesyslogic.com.tw>
>>>
>>> Changes are:
>>> * Disable GL9755 overcurrent interrupt when power on/off on UHS-II.
>>> * Enable the internal clock when do reset on UHS-II mode.
>>> * Increase timeout value before detecting UHS-II interface.
>>> * Add vendor settings fro UHS-II mode.
>>> * Remove sdhci_gli_enable_internal_clock functon unused clk_ctrl variable.
>>> * Make a function sdhci_gli_wait_software_reset_done() for gl9755 reset.
>>> * Remove unnecessary code from sdhci_gl9755_reset().
>>>
>>> Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
>>> Signed-off-by: Lucas Lai <lucas.lai@genesyslogic.com.tw>
>>> ---
>>>
>>> drivers/mmc/host/sdhci-pci-gli.c | 235 ++++++++++++++++++++++++++++++-
>>> 1 file changed, 234 insertions(+), 1 deletion(-)
>>
>> This change results in error messages / timeout about UHS2 followed by
>> register dumps with the GL9755 integrated in Apple silicon Macbook Pros
>> and Mac Studio systems. Non UHS-II function of controller does not seem
>> to be affected. Apple advertises the the SDXC slot as UHS-II capable.
>>
>> The only quirk we've experienced with gl9755 on this platform is that 8
>> and 16 bit MMIO reads do not work. Workaround added in commit
>> c064bb5c78c1b ("mmc: sdhci-pci-gli: GL975[50]: Issue 8/16-bit MMIO reads
>> as 32-bit reads.").
>>
>> If you have ideas or patches to try I'm happy to do that. If not we can
>> look into what MacOS does.
>>
>> See kernel log and lspci output below
>>
>> Thanks,
>> Janne
>>
>> [ 38.130033] kernel: sdhci: Secure Digital Host Controller Interface driver
>> [ 38.130141] kernel: sdhci: Copyright(c) Pierre Ossman
>> [ 38.133352] kernel: sdhci-pci 0000:02:00.0: Adding to iommu group 13
>> [ 38.160551] kernel: sdhci-pci 0000:02:00.0: SDHCI controller found [17a0:9755] (rev 1)
>> [ 38.160655] kernel: sdhci-pci 0000:02:00.0: enabling device (0000 -> 0002)
>> [ 38.160750] kernel: mmc0: SDHCI controller on PCI [0000:02:00.0] using ADMA 64-bit
>> [ 38.274617] kernel: mmc0: not detect UHS2 interface in 100ms.
>> [ 38.274717] kernel: mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
>> [ 38.274782] kernel: mmc0: sdhci: Sys addr: 0x00000000 | Version: 0x00000005
>> [ 38.277391] kernel: mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
>> [ 38.277475] kernel: mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
>> [ 38.280125] kernel: mmc0: sdhci: Present: 0x20070000 | Host ctl: 0x00000000
>> [ 38.280206] kernel: mmc0: sdhci: Power: 0x000000bf | Blk gap: 0x00000000
>> [ 38.284511] kernel: mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x0000032f
>> [ 38.284592] kernel: mmc0: sdhci: Timeout: 0x00000007 | Int stat: 0x00000000
>> [ 38.284636] kernel: mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
>> [ 38.287200] kernel: mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
>> [ 38.287281] kernel: mmc0: sdhci: Caps: 0x396a3281 | Caps_1: 0x1803057f
>> [ 38.291212] kernel: mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x000000c8
>> [ 38.291292] kernel: mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
>> [ 38.291335] kernel: mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
>> [ 38.293513] kernel: mmc0: sdhci: Host ctl2: 0x00009107
>> [ 38.293604] kernel: mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000
>> [ 38.297842] kernel: mmc0: sdhci_uhs2: ==================== UHS2 ==================
>> [ 38.297923] kernel: mmc0: sdhci_uhs2: Blk Size: 0x00000000 | Blk Cnt: 0x00000000
>> [ 38.297968] kernel: mmc0: sdhci_uhs2: Cmd: 0x00000000 | Trn mode: 0x00000000
>> [ 38.300773] kernel: mmc0: sdhci_uhs2: Int Stat: 0x00000000 | Dev Sel : 0x00000000
>> [ 38.300853] kernel: mmc0: sdhci_uhs2: Dev Int Code: 0x00000000
>> [ 38.304739] kernel: mmc0: sdhci_uhs2: Reset: 0x00000000 | Timer: 0x000000a7
>> [ 38.304811] kernel: mmc0: sdhci_uhs2: ErrInt: 0x00000000 | ErrIntEn: 0x00030000
>> [ 38.304856] kernel: mmc0: sdhci_uhs2: ErrSigEn: 0x00030000
>> [ 38.307110] kernel: mmc0: sdhci: ============================================
>> [ 38.307201] kernel: mmc0: cannot detect UHS2 interface.
>> [ 38.310110] kernel: mmc0: failed to initial phy for UHS-II!
>> [ 38.424645] kernel: mmc0: not detect UHS2 interface in 100ms.
>> [ 38.424731] kernel: mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
>> [ 38.424758] kernel: mmc0: sdhci: Sys addr: 0x00000000 | Version: 0x00000005
>> [ 38.424782] kernel: mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
>> [ 38.424840] kernel: mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
>> [ 38.427603] kernel: mmc0: sdhci: Present: 0x20070000 | Host ctl: 0x00000000
>> [ 38.427659] kernel: mmc0: sdhci: Power: 0x000000bf | Blk gap: 0x00000000
>> [ 38.430579] kernel: mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x0000032f
>> [ 38.430626] kernel: mmc0: sdhci: Timeout: 0x00000007 | Int stat: 0x00000000
>> [ 38.433504] kernel: mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
>> [ 38.433550] kernel: mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
>> [ 38.437596] kernel: mmc0: sdhci: Caps: 0x396a3281 | Caps_1: 0x1803057f
>> [ 38.437641] kernel: mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x000000c8
>> [ 38.437677] kernel: mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
>> [ 38.440318] kernel: mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
>> [ 38.440345] kernel: mmc0: sdhci: Host ctl2: 0x00009107
>> [ 38.444119] kernel: mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000
>> [ 38.444161] kernel: mmc0: sdhci_uhs2: ==================== UHS2 ==================
>> [ 38.444184] kernel: mmc0: sdhci_uhs2: Blk Size: 0x00000000 | Blk Cnt: 0x00000000
>> [ 38.446918] kernel: mmc0: sdhci_uhs2: Cmd: 0x00000000 | Trn mode: 0x00000000
>> [ 38.446958] kernel: mmc0: sdhci_uhs2: Int Stat: 0x00000000 | Dev Sel : 0x00000000
>> [ 38.450833] kernel: mmc0: sdhci_uhs2: Dev Int Code: 0x00000000
>> [ 38.450874] kernel: mmc0: sdhci_uhs2: Reset: 0x00000000 | Timer: 0x000000a7
>> [ 38.450907] kernel: mmc0: sdhci_uhs2: ErrInt: 0x00000000 | ErrIntEn: 0x00030000
>> [ 38.454625] kernel: mmc0: sdhci_uhs2: ErrSigEn: 0x00030000
>> [ 38.454665] kernel: mmc0: sdhci: ============================================
>> [ 38.454699] kernel: mmc0: cannot detect UHS2 interface.
>> [ 38.456705] kernel: mmc0: failed to initial phy for UHS-II!
>>
>
> On which UHS-II card does this message appear? Please share the card
> information.
> If it is a UHS-I card, this message is normal.
If it is normal, we should not be producing error messages
and register dumps. Please change all these messages to be
debug only. The register dump can go behind DYNAMIC_DEBUG_BRANCH().
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH V23 16/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9767
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (14 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 15/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9755 Victor Shih
@ 2024-10-18 10:53 ` Victor Shih
2024-10-24 8:51 ` [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Adrian Hunter
2024-10-24 12:57 ` Ulf Hansson
17 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-18 10:53 UTC (permalink / raw)
To: ulf.hansson, adrian.hunter
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih, Ben Chuang, Victor Shih, Lucas Lai
From: Victor Shih <victor.shih@genesyslogic.com.tw>
Changes are:
* Enable the internal clock when do reset on UHS-II mode.
* Increase timeout value before detecting UHS-II interface.
* Add vendor settings for UHS-II mode.
* Use the function sdhci_gli_wait_software_reset_done() for gl9767 reset.
* Remove unnecessary code from sdhci_gl9767_reset().
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
Signed-off-by: Lucas Lai <lucas.lai@genesyslogic.com.tw>
--
Updates in V21:
- Remove unnecessary code from sdhci_gl9767_reset().
Updates in V20:
- Use the function sdhci_gli_wait_software_reset_done() for gl9767 reset.
Updates in V19:
- Use sdhci_gli_enable_internal_clock() to simplify the code
in the sdhci_gl9767_reset().
- Directly reading the SDHCI_PRESENT_STATE register to replace
the get_cd() avoids the possibility of the get_cd() sleeping
in the sdhci_gl9767_set_card_detect_debounce_time().
- Refine the position of gli_set_9767() in the sdhci_gl9767_reset().
Updates in V18:
- Add new register settings for gl9767.
- Add card_event in the sdhci_ops for gl9767.
- Add sdhci_gl9767_set_card_detect_debounce_time()
to configure the gl9767.
- Adjust the sdhci_gl9767_reset() process for gl9767.
Updates in V17:
- Use mmc_card_uhs2() to simplify the code in the sdhci_gl9767_reset().
- Use mmc_card_uhs2() to simplify the code in the
sdhci_gl9767_set_power().
- Add sdhci_gli_overcurrent_event_enable() to sdhci_gl9767_set_power().
Updates in V15:
- Add gl9767 to support uhs2 function.
---
drivers/mmc/host/sdhci-pci-gli.c | 202 ++++++++++++++++++++++++++++++-
1 file changed, 201 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index 708138eecaa7..cf2486ec7b9a 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -174,6 +174,15 @@
#define PCI_GLI_9755_MISC 0x78
#define PCI_GLI_9755_MISC_SSC_OFF BIT(26)
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL 0x508
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_CMD_CONFLICT_CHECK BIT(0)
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE GENMASK(21, 16)
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_PLUG_IN_VALUE 0x05
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_PLUG_OUT_VALUE 0x3F
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE GENMASK(23, 22)
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE_1MS 0x2
+#define SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE_10MS 0x3
+
#define SDHCI_GLI_9767_GM_BURST_SIZE 0x510
#define SDHCI_GLI_9767_GM_BURST_SIZE_AXI_ALWAYS_SET BIT(8)
@@ -210,6 +219,13 @@
#define PCIE_GLI_9767_SCR_CORE_PWR_D3_OFF BIT(21)
#define PCIE_GLI_9767_SCR_CFG_RST_DATA_LINK_DOWN BIT(30)
+#define PCIE_GLI_9767_RESET_REG 0x8E4
+#define PCIE_GLI_9767_RESET_REG_SD_HOST_SW_RESET BIT(0)
+
+#define PCIE_GLI_9767_UHS2_PHY_SET_REG1 0x90C
+#define PCIE_GLI_9767_UHS2_PHY_SET_REG1_SERDES_INTR GENMASK(31, 29)
+#define PCIE_GLI_9767_UHS2_PHY_SET_REG1_SERDES_INTR_VALUE 0x3
+
#define PCIE_GLI_9767_SDHC_CAP 0x91C
#define PCIE_GLI_9767_SDHC_CAP_SDEI_RESULT BIT(5)
@@ -228,9 +244,15 @@
#define PCIE_GLI_9767_SD_EXPRESS_CTL_SD_EXPRESS_MODE BIT(1)
#define PCIE_GLI_9767_SD_DATA_MULTI_CTL 0x944
+#define PCIE_GLI_9767_SD_DATA_MULTI_CTL_SELECT_UHS2 BIT(5)
+#define PCIE_GLI_9767_SD_DATA_MULTI_CTL_UHS2_SWITCH_CTL BIT(8)
#define PCIE_GLI_9767_SD_DATA_MULTI_CTL_DISCONNECT_TIME GENMASK(23, 16)
#define PCIE_GLI_9767_SD_DATA_MULTI_CTL_DISCONNECT_TIME_VALUE 0x64
+#define PCIE_GLI_9767_UHS2_PHY_SET_REG2 0x948
+#define PCIE_GLI_9767_UHS2_PHY_SET_REG2_SSC_PPM_SETTING GENMASK(22, 21)
+#define PCIE_GLI_9767_UHS2_PHY_SET_REG2_SSC_PPM_SETTING_VALUE 0x0
+
#define PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2 0x950
#define PCIE_GLI_9767_NORMAL_ERR_INT_STATUS_REG2_SDEI_COMPLETE BIT(0)
@@ -240,6 +262,28 @@
#define PCIE_GLI_9767_NORMAL_ERR_INT_SIGNAL_EN_REG2 0x958
#define PCIE_GLI_9767_NORMAL_ERR_INT_SIGNAL_EN_REG2_SDEI_COMPLETE_SIGNAL_EN BIT(0)
+#define PCIE_GLI_9767_UHS2_CTL1 0x95C
+#define PCIE_GLI_9767_UHS2_CTL1_TRANS_PASS BIT(5)
+#define PCIE_GLI_9767_UHS2_CTL1_TRANS_PASS_VALUE 0x1
+#define PCIE_GLI_9767_UHS2_CTL1_DECODING_CTL BIT(6)
+#define PCIE_GLI_9767_UHS2_CTL1_DECODING_CTL_VALUE 0x1
+#define PCIE_GLI_9767_UHS2_CTL1_SERDES_TRAN GENMASK(10, 7)
+#define PCIE_GLI_9767_UHS2_CTL1_SERDES_TRAN_VALUE 0x3
+#define PCIE_GLI_9767_UHS2_CTL1_SERDES_RECV GENMASK(14, 11)
+#define PCIE_GLI_9767_UHS2_CTL1_SERDES_RECV_VALUE 0xf
+#define PCIE_GLI_9767_UHS2_CTL1_DIR_TRANS GENMASK(16, 15)
+#define PCIE_GLI_9767_UHS2_CTL1_DIR_TRANS_VALUE 0x0
+#define PCIE_GLI_9767_UHS2_CTL1_DIR_RECV GENMASK(18, 17)
+#define PCIE_GLI_9767_UHS2_CTL1_DIR_RECV_VALUE 0x0
+#define PCIE_GLI_9767_UHS2_CTL1_PDRST BIT(25)
+#define PCIE_GLI_9767_UHS2_CTL1_PDRST_VALUE 0x1
+
+#define PCIE_GLI_9767_UHS2_CTL2 0x964
+#define PCIE_GLI_9767_UHS2_CTL2_ZC GENMASK(3, 0)
+#define PCIE_GLI_9767_UHS2_CTL2_ZC_VALUE 0xb
+#define PCIE_GLI_9767_UHS2_CTL2_ZC_CTL BIT(6)
+#define PCIE_GLI_9767_UHS2_CTL2_ZC_CTL_VALUE 0x1
+
#define GLI_MAX_TUNING_LOOP 40
/* Genesys Logic chipset */
@@ -1155,6 +1199,31 @@ static void sdhci_gl9767_set_clock(struct sdhci_host *host, unsigned int clock)
gl9767_vhs_read(pdev);
}
+static void sdhci_gl9767_set_card_detect_debounce_time(struct sdhci_host *host)
+{
+ u32 value;
+
+ value = sdhci_readl(host, SDHCI_GLI_9767_SD_HOST_OPERATION_CTL);
+ value &= ~(SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE |
+ SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE);
+ if (sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
+ value |= FIELD_PREP(SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE,
+ SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_PLUG_IN_VALUE) |
+ FIELD_PREP(SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE,
+ SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE_1MS);
+ else
+ value |= FIELD_PREP(SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE,
+ SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_PLUG_OUT_VALUE) |
+ FIELD_PREP(SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE,
+ SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_DEBOUNCE_SCALE_10MS);
+ sdhci_writel(host, value, SDHCI_GLI_9767_SD_HOST_OPERATION_CTL);
+}
+
+static void sdhci_gl9767_card_event(struct sdhci_host *host)
+{
+ sdhci_gl9767_set_card_detect_debounce_time(host);
+}
+
static void gli_set_9767(struct sdhci_host *host)
{
u32 value;
@@ -1162,6 +1231,12 @@ static void gli_set_9767(struct sdhci_host *host)
value = sdhci_readl(host, SDHCI_GLI_9767_GM_BURST_SIZE);
value &= ~SDHCI_GLI_9767_GM_BURST_SIZE_AXI_ALWAYS_SET;
sdhci_writel(host, value, SDHCI_GLI_9767_GM_BURST_SIZE);
+
+ value = sdhci_readl(host, SDHCI_GLI_9767_SD_HOST_OPERATION_CTL);
+ value &= ~SDHCI_GLI_9767_SD_HOST_OPERATION_CTL_CMD_CONFLICT_CHECK;
+ sdhci_writel(host, value, SDHCI_GLI_9767_SD_HOST_OPERATION_CTL);
+
+ sdhci_gl9767_set_card_detect_debounce_time(host);
}
static void gl9767_hw_setting(struct sdhci_pci_slot *slot)
@@ -1200,7 +1275,43 @@ static void gl9767_hw_setting(struct sdhci_pci_slot *slot)
static void sdhci_gl9767_reset(struct sdhci_host *host, u8 mask)
{
- sdhci_reset(host, mask);
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ u32 value;
+
+ /* need internal clock */
+ if (mask & SDHCI_RESET_ALL) {
+ sdhci_gli_enable_internal_clock(host);
+
+ gl9767_vhs_write(pdev);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9767_RESET_REG, &value);
+ value &= ~PCIE_GLI_9767_RESET_REG_SD_HOST_SW_RESET;
+ pci_write_config_dword(pdev, PCIE_GLI_9767_RESET_REG, value);
+
+ if (read_poll_timeout_atomic(pci_read_config_dword, value,
+ !(value & PCIE_GLI_9767_RESET_REG_SD_HOST_SW_RESET),
+ 1, 5, true, pdev, PCIE_GLI_9767_RESET_REG, &value)) {
+ pr_warn("%s: %s: Reset SDHC AHB and TL-AMBA failure.\n",
+ __func__, mmc_hostname(host->mmc));
+ gl9767_vhs_read(pdev);
+ return;
+ }
+ gl9767_vhs_read(pdev);
+ }
+
+ if (mmc_card_uhs2(host->mmc)) {
+ if (mask & (SDHCI_RESET_CMD | SDHCI_RESET_DATA)) {
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+ sdhci_gli_uhs2_reset_sd_tran(host);
+ sdhci_gli_wait_software_reset_done(host, mask);
+ } else {
+ sdhci_uhs2_reset(host, mask);
+ }
+ } else {
+ sdhci_reset(host, mask);
+ }
+
gli_set_9767(host);
}
@@ -1291,6 +1402,86 @@ static int gl9767_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios)
return 0;
}
+static void gl9767_vendor_init(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ u32 value;
+
+ gl9767_vhs_write(pdev);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9767_UHS2_PHY_SET_REG1, &value);
+ value |= FIELD_PREP(PCIE_GLI_9767_UHS2_PHY_SET_REG1_SERDES_INTR,
+ PCIE_GLI_9767_UHS2_PHY_SET_REG1_SERDES_INTR_VALUE);
+ pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_PHY_SET_REG1, value);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9767_UHS2_PHY_SET_REG2, &value);
+ value |= FIELD_PREP(PCIE_GLI_9767_UHS2_PHY_SET_REG2_SSC_PPM_SETTING,
+ PCIE_GLI_9767_UHS2_PHY_SET_REG2_SSC_PPM_SETTING_VALUE);
+ pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_PHY_SET_REG2, value);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL1, &value);
+ value |= FIELD_PREP(PCIE_GLI_9767_UHS2_CTL1_TRANS_PASS,
+ PCIE_GLI_9767_UHS2_CTL1_TRANS_PASS_VALUE) |
+ FIELD_PREP(PCIE_GLI_9767_UHS2_CTL1_DECODING_CTL,
+ PCIE_GLI_9767_UHS2_CTL1_DECODING_CTL_VALUE) |
+ FIELD_PREP(PCIE_GLI_9767_UHS2_CTL1_SERDES_TRAN,
+ PCIE_GLI_9767_UHS2_CTL1_SERDES_TRAN_VALUE) |
+ FIELD_PREP(PCIE_GLI_9767_UHS2_CTL1_SERDES_RECV,
+ PCIE_GLI_9767_UHS2_CTL1_SERDES_RECV_VALUE) |
+ FIELD_PREP(PCIE_GLI_9767_UHS2_CTL1_DIR_TRANS,
+ PCIE_GLI_9767_UHS2_CTL1_DIR_TRANS_VALUE) |
+ FIELD_PREP(PCIE_GLI_9767_UHS2_CTL1_DIR_RECV,
+ PCIE_GLI_9767_UHS2_CTL1_DIR_RECV_VALUE) |
+ FIELD_PREP(PCIE_GLI_9767_UHS2_CTL1_PDRST,
+ PCIE_GLI_9767_UHS2_CTL1_PDRST_VALUE);
+ pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL1, value);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, &value);
+ value |= FIELD_PREP(PCIE_GLI_9767_UHS2_CTL2_ZC,
+ PCIE_GLI_9767_UHS2_CTL2_ZC_VALUE) |
+ FIELD_PREP(PCIE_GLI_9767_UHS2_CTL2_ZC_CTL,
+ PCIE_GLI_9767_UHS2_CTL2_ZC_CTL_VALUE);
+ pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, value);
+
+ gl9767_vhs_read(pdev);
+}
+
+static void sdhci_gl9767_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ u32 value;
+
+ if (mmc_card_uhs2(host->mmc)) {
+ gl9767_vhs_write(pdev);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9767_SD_DATA_MULTI_CTL, &value);
+ value |= PCIE_GLI_9767_SD_DATA_MULTI_CTL_SELECT_UHS2 |
+ PCIE_GLI_9767_SD_DATA_MULTI_CTL_UHS2_SWITCH_CTL;
+ pci_write_config_dword(pdev, PCIE_GLI_9767_SD_DATA_MULTI_CTL, value);
+
+ gl9767_vhs_read(pdev);
+
+ sdhci_gli_overcurrent_event_enable(host, false);
+ sdhci_uhs2_set_power(host, mode, vdd);
+ sdhci_gli_overcurrent_event_enable(host, true);
+ } else {
+ gl9767_vhs_write(pdev);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9767_SD_DATA_MULTI_CTL, &value);
+ value &= ~(PCIE_GLI_9767_SD_DATA_MULTI_CTL_SELECT_UHS2 |
+ PCIE_GLI_9767_SD_DATA_MULTI_CTL_UHS2_SWITCH_CTL);
+ pci_write_config_dword(pdev, PCIE_GLI_9767_SD_DATA_MULTI_CTL, value);
+
+ gl9767_vhs_read(pdev);
+
+ sdhci_gli_overcurrent_event_enable(host, false);
+ sdhci_set_power(host, mode, vdd);
+ sdhci_gli_overcurrent_event_enable(host, true);
+ }
+}
+
static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
{
struct sdhci_host *host = slot->host;
@@ -1327,6 +1518,7 @@ static int gli_probe_slot_gl9767(struct sdhci_pci_slot *slot)
host->mmc->caps2 |= MMC_CAP2_SD_EXP;
host->mmc_host_ops.init_sd_express = gl9767_init_sd_express;
sdhci_enable_v4_mode(host);
+ gl9767_vendor_init(host);
return 0;
}
@@ -1830,12 +2022,20 @@ static const struct sdhci_ops sdhci_gl9767_ops = {
.reset = sdhci_gl9767_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.voltage_switch = sdhci_gl9767_voltage_switch,
+ .dump_uhs2_regs = sdhci_uhs2_dump_regs,
+ .set_timeout = sdhci_uhs2_set_timeout,
+ .irq = sdhci_uhs2_irq,
+ .set_power = sdhci_gl9767_set_power,
+ .uhs2_pre_detect_init = sdhci_gli_pre_detect_init,
+ .card_event = sdhci_gl9767_card_event,
};
const struct sdhci_pci_fixes sdhci_gl9767 = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50,
.probe_slot = gli_probe_slot_gl9767,
+ .add_host = sdhci_pci_uhs2_add_host,
+ .remove_host = sdhci_pci_uhs2_remove_host,
.ops = &sdhci_gl9767_ops,
#ifdef CONFIG_PM_SLEEP
.resume = sdhci_pci_gli_resume,
--
2.25.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (15 preceding siblings ...)
2024-10-18 10:53 ` [PATCH V23 16/16] mmc: sdhci-pci-gli: enable UHS-II mode for GL9767 Victor Shih
@ 2024-10-24 8:51 ` Adrian Hunter
2024-10-24 12:57 ` Ulf Hansson
17 siblings, 0 replies; 27+ messages in thread
From: Adrian Hunter @ 2024-10-24 8:51 UTC (permalink / raw)
To: Victor Shih, ulf.hansson
Cc: linux-mmc, linux-kernel, benchuanggli, Lucas.Lai, HL.Liu, Greg.tu,
dlunev, Victor Shih
On 18/10/24 13:53, Victor Shih wrote:
> From: Victor Shih <victor.shih@genesyslogic.com.tw>
>
> Summary
> =======
> These patches[1] support UHS-II and fix GL9755 and GL9767
> UHS-II compatibility.
>
> About UHS-II, roughly deal with the following three parts:
> 1) A UHS-II detection and initialization:
> - Host setup to support UHS-II (Section 3.13.1 Host Controller Setup
> Sequence[2]).
> - Detect a UHS-II I/F (Section 3.13.2 Card Interface Detection Sequence
> [2]).
> - In step(9) of Section 3.13.2 in [2], UHS-II initialization is include
> Section 3.13.3 UHS-II Card Initialization and Section 3.13.4 UHS-II
> Setting Register Setup Sequence.
>
> 2) Send Legacy SD command through SD-TRAN
> - Encapsulated SD packets are defined in SD-TRAN in order to ensure Legacy
> SD compatibility and preserve Legacy SD infrastructures (Section 7.1.1
> Packet Types and Format Overview[3]).
> - Host issue a UHS-II CCMD packet or a UHS-II DCMD (Section 3.13.5 UHS-II
> CCMD Packet issuing and Section 3.13.6 UHS-II DCMD Packet issuing[2]).
>
> 3) UHS-II Interrupt
> - Except for UHS-II error interrupts, most interrupts share the original
> interrupt registers.
>
> Patch structure
> ===============
> patch#1: for core
> patch#2-#14: for sdhci
> patch#15: for GL9755
> patch#16: for GL9767
>
> Tests
> =====
> Ran 'dd' command to evaluate the performance 3 times:
> (SanDisk UHS-II card on GL9755 controller)
> Read Write
> UHS-II disabled (UHS-I): 81.9MB/s 51.4MB/s
> UHS-II enabled : 206MB/s 80.5MB/s
> Read Write
> UHS-II disabled (UHS-I): 82.3MB/s 49.7MB/s
> UHS-II enabled : 208MB/s 80.8MB/s
> Read Write
> UHS-II disabled (UHS-I): 82.9MB/s 50.8MB/s
> UHS-II enabled : 205MB/s 90.0MB/s
> (SanDisk UHS-II card on GL9767 controller)
> Read Write
> UHS-II disabled (UHS-I): 83.5MB/s 50.5MB/s
> UHS-II enabled : 200MB/s 75.3MB/s
> Read Write
> UHS-II disabled (UHS-I): 85.2MB/s 56.3MB/s
> UHS-II enabled : 203MB/s 75.8MB/s
> Read Write
> UHS-II disabled (UHS-I): 82.9MB/s 51.1MB/s
> UHS-II enabled : 196MB/s 77.8MB/s
>
> Test command
> =====
> Read: dd if=/dev/mmcxxx of=/dev/null bs=4096k count=2000 iflag=direct
> Write:dd if=/dev/zero of=/dev/mmcxxx bs=4096k count=2000 oflag=direct
>
> Changes in v23 (October. 18, 2024)
> * Rebase on latest mmc/next.
> * Version 22 patch#1-patch#6 have already been applied to the mmc/next
> branch, so the patch order for version 23 has been shifted forward.
> * Patch#1: Remove mmc_uhs2_card_prepare_cmd() function.
> Remove mmc_sd_can_poweroff_notify() function.
> Modify ios.timing setting in the sd_uhs2_power_off() function.
> Restore the position of assign the host->card to original
> position in the sd_uhs2_init_card() function.
> Remove unnecessary error handle in the sd_uhs2_init_card()
> function.
> Add oldcard judgment to skip some programs in the
> sd_uhs2_legacy_init() function.
> Remove unnecessary error handle in the sd_uhs2_legacy_init()
> function.
> Remove mmc_card_set_present() function in the
> sd_uhs2_reinit()function.
>
> Reference
> =========
> [1] https://gitlab.com/VictorShih/linux-uhs2.git
> [2] SD Host Controller Simplified Specification 4.20
> [3] UHS-II Simplified Addendum 1.02
> [4] https://patchwork.kernel.org/project/linux-mmc/cover/20240913102836.6144-1-victorshihgli@gmail.com/
Not all SDHCI patches show my Acked-by although I did give it
in V22 for SDHCI patches. So again for SDHCI:
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Ulf mentioned checkpatch warnings. Here is a summary:
---------------------------------------------------------------------------------------
heads/head-2024-10-24-01/0002-mmc-sdhci-add-UHS-II-related-definitions-in-headers.patch
---------------------------------------------------------------------------------------
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#20:
new file mode 100644
Nope - already covered by drivers/mmc/host/sdhci*
WARNING: It's generally not useful to have the filename in the file
#27: FILE: drivers/mmc/host/sdhci-uhs2.h:3:
+ * linux/drivers/mmc/host/sdhci-uhs2.h - Secure Digital Host Controller Interface driver
Could drop the file name
----------------------------------------------------------------------------------------
heads/head-2024-10-24-01/0003-mmc-sdhci-add-UHS-II-module-and-add-a-kernel-configu.patch
----------------------------------------------------------------------------------------
WARNING: please write a help paragraph that fully describes the config symbol
#33: FILE: drivers/mmc/host/Kconfig:101:
+config MMC_SDHCI_UHS2
+ tristate "UHS2 support on SDHCI controller"
+ depends on MMC_SDHCI
+ help
+ This option is selected by SDHCI controller drivers that want to
+ support UHS2-capable devices.
+
+ If you have a controller with this feature, say Y or M here.
+
A web-search for uhs2 says what it is, so I have no strong feelings
about adding more.
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#58:
new file mode 100644
Nope - already covered by drivers/mmc/host/sdhci*
----------------------------------------------------------------------------------------
heads/head-2024-10-24-01/0010-mmc-sdhci-uhs2-add-related-functions-to-initialize-t.patch
----------------------------------------------------------------------------------------
WARNING: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#11:
After detected the UHS-II interface, the host's UHS-II capabilities will be set up here and
Commit message could be wrapped as requested
There is also the ones below, but please _leave_ the code as it is
consistent with the current code in the struct.
------------------------------------------------------------------------------
heads/head-2024-10-24-01/0014-mmc-sdhci-pci-add-UHS-II-support-framework.patch
------------------------------------------------------------------------------
WARNING: Unnecessary space before function pointer arguments
#81: FILE: drivers/mmc/host/sdhci-pci.h:148:
+ void (*remove_host) (struct sdhci_pci_slot *, int);
WARNING: function definition argument 'struct sdhci_pci_slot *' should also have an identifier name
#81: FILE: drivers/mmc/host/sdhci-pci.h:148:
+ void (*remove_host) (struct sdhci_pci_slot *, int);
WARNING: function definition argument 'int' should also have an identifier name
#81: FILE: drivers/mmc/host/sdhci-pci.h:148:
+ void (*remove_host) (struct sdhci_pci_slot *, int);
total: 0 errors, 3 warnings, 0 checks, 56 lines checked
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767
2024-10-18 10:53 [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Victor Shih
` (16 preceding siblings ...)
2024-10-24 8:51 ` [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767 Adrian Hunter
@ 2024-10-24 12:57 ` Ulf Hansson
2024-10-25 4:00 ` Victor Shih
17 siblings, 1 reply; 27+ messages in thread
From: Ulf Hansson @ 2024-10-24 12:57 UTC (permalink / raw)
To: Victor Shih
Cc: adrian.hunter, linux-mmc, linux-kernel, benchuanggli, Lucas.Lai,
HL.Liu, Greg.tu, dlunev, Victor Shih
On Fri, 18 Oct 2024 at 12:53, Victor Shih <victorshihgli@gmail.com> wrote:
>
> From: Victor Shih <victor.shih@genesyslogic.com.tw>
>
> Summary
> =======
> These patches[1] support UHS-II and fix GL9755 and GL9767
> UHS-II compatibility.
>
> About UHS-II, roughly deal with the following three parts:
> 1) A UHS-II detection and initialization:
> - Host setup to support UHS-II (Section 3.13.1 Host Controller Setup
> Sequence[2]).
> - Detect a UHS-II I/F (Section 3.13.2 Card Interface Detection Sequence
> [2]).
> - In step(9) of Section 3.13.2 in [2], UHS-II initialization is include
> Section 3.13.3 UHS-II Card Initialization and Section 3.13.4 UHS-II
> Setting Register Setup Sequence.
>
> 2) Send Legacy SD command through SD-TRAN
> - Encapsulated SD packets are defined in SD-TRAN in order to ensure Legacy
> SD compatibility and preserve Legacy SD infrastructures (Section 7.1.1
> Packet Types and Format Overview[3]).
> - Host issue a UHS-II CCMD packet or a UHS-II DCMD (Section 3.13.5 UHS-II
> CCMD Packet issuing and Section 3.13.6 UHS-II DCMD Packet issuing[2]).
>
> 3) UHS-II Interrupt
> - Except for UHS-II error interrupts, most interrupts share the original
> interrupt registers.
>
> Patch structure
> ===============
> patch#1: for core
> patch#2-#14: for sdhci
> patch#15: for GL9755
> patch#16: for GL9767
>
> Tests
> =====
> Ran 'dd' command to evaluate the performance 3 times:
> (SanDisk UHS-II card on GL9755 controller)
> Read Write
> UHS-II disabled (UHS-I): 81.9MB/s 51.4MB/s
> UHS-II enabled : 206MB/s 80.5MB/s
> Read Write
> UHS-II disabled (UHS-I): 82.3MB/s 49.7MB/s
> UHS-II enabled : 208MB/s 80.8MB/s
> Read Write
> UHS-II disabled (UHS-I): 82.9MB/s 50.8MB/s
> UHS-II enabled : 205MB/s 90.0MB/s
> (SanDisk UHS-II card on GL9767 controller)
> Read Write
> UHS-II disabled (UHS-I): 83.5MB/s 50.5MB/s
> UHS-II enabled : 200MB/s 75.3MB/s
> Read Write
> UHS-II disabled (UHS-I): 85.2MB/s 56.3MB/s
> UHS-II enabled : 203MB/s 75.8MB/s
> Read Write
> UHS-II disabled (UHS-I): 82.9MB/s 51.1MB/s
> UHS-II enabled : 196MB/s 77.8MB/s
>
> Test command
> =====
> Read: dd if=/dev/mmcxxx of=/dev/null bs=4096k count=2000 iflag=direct
> Write:dd if=/dev/zero of=/dev/mmcxxx bs=4096k count=2000 oflag=direct
>
> Changes in v23 (October. 18, 2024)
> * Rebase on latest mmc/next.
> * Version 22 patch#1-patch#6 have already been applied to the mmc/next
> branch, so the patch order for version 23 has been shifted forward.
> * Patch#1: Remove mmc_uhs2_card_prepare_cmd() function.
> Remove mmc_sd_can_poweroff_notify() function.
> Modify ios.timing setting in the sd_uhs2_power_off() function.
> Restore the position of assign the host->card to original
> position in the sd_uhs2_init_card() function.
> Remove unnecessary error handle in the sd_uhs2_init_card()
> function.
> Add oldcard judgment to skip some programs in the
> sd_uhs2_legacy_init() function.
> Remove unnecessary error handle in the sd_uhs2_legacy_init()
> function.
> Remove mmc_card_set_present() function in the
> sd_uhs2_reinit()function.
I have queued this up for next, with a few amendments and added
Adrian's acks to the SDHCI patches.
When applying, I made a couple of cleanups and fixed
sd_uhs2_power_off() to patch 1. I also took care of the checkpatch
warnings for the SDHCI patches.
That said, there are still some issues that remain to be fixed, but
let's do that on top of the $subject series. See below:
*) The error/re-init path of the card is broken, as also pointed out
by Adrian. I will work on this shortly and send patches to fix this
up, please help to review and test.
*) We should read the card's ext registers, to for example enable the
card's internal cache to be used.
*) We should avoid unnecessary commands during re-init of the card, to
decrease latency.
*) During suspend, we should probably send the go-dormant command with
the hibernate bit, rather than just doing a plain power-off.
[...]
Thanks and kind regards
Uffe
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH V23 00/16] Add support UHS-II for GL9755 and GL9767
2024-10-24 12:57 ` Ulf Hansson
@ 2024-10-25 4:00 ` Victor Shih
0 siblings, 0 replies; 27+ messages in thread
From: Victor Shih @ 2024-10-25 4:00 UTC (permalink / raw)
To: Ulf Hansson
Cc: adrian.hunter, linux-mmc, linux-kernel, benchuanggli, Lucas.Lai,
HL.Liu, Greg.tu, dlunev, Victor Shih
On Thu, Oct 24, 2024 at 8:57 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Fri, 18 Oct 2024 at 12:53, Victor Shih <victorshihgli@gmail.com> wrote:
> >
> > From: Victor Shih <victor.shih@genesyslogic.com.tw>
> >
> > Summary
> > =======
> > These patches[1] support UHS-II and fix GL9755 and GL9767
> > UHS-II compatibility.
> >
> > About UHS-II, roughly deal with the following three parts:
> > 1) A UHS-II detection and initialization:
> > - Host setup to support UHS-II (Section 3.13.1 Host Controller Setup
> > Sequence[2]).
> > - Detect a UHS-II I/F (Section 3.13.2 Card Interface Detection Sequence
> > [2]).
> > - In step(9) of Section 3.13.2 in [2], UHS-II initialization is include
> > Section 3.13.3 UHS-II Card Initialization and Section 3.13.4 UHS-II
> > Setting Register Setup Sequence.
> >
> > 2) Send Legacy SD command through SD-TRAN
> > - Encapsulated SD packets are defined in SD-TRAN in order to ensure Legacy
> > SD compatibility and preserve Legacy SD infrastructures (Section 7.1.1
> > Packet Types and Format Overview[3]).
> > - Host issue a UHS-II CCMD packet or a UHS-II DCMD (Section 3.13.5 UHS-II
> > CCMD Packet issuing and Section 3.13.6 UHS-II DCMD Packet issuing[2]).
> >
> > 3) UHS-II Interrupt
> > - Except for UHS-II error interrupts, most interrupts share the original
> > interrupt registers.
> >
> > Patch structure
> > ===============
> > patch#1: for core
> > patch#2-#14: for sdhci
> > patch#15: for GL9755
> > patch#16: for GL9767
> >
> > Tests
> > =====
> > Ran 'dd' command to evaluate the performance 3 times:
> > (SanDisk UHS-II card on GL9755 controller)
> > Read Write
> > UHS-II disabled (UHS-I): 81.9MB/s 51.4MB/s
> > UHS-II enabled : 206MB/s 80.5MB/s
> > Read Write
> > UHS-II disabled (UHS-I): 82.3MB/s 49.7MB/s
> > UHS-II enabled : 208MB/s 80.8MB/s
> > Read Write
> > UHS-II disabled (UHS-I): 82.9MB/s 50.8MB/s
> > UHS-II enabled : 205MB/s 90.0MB/s
> > (SanDisk UHS-II card on GL9767 controller)
> > Read Write
> > UHS-II disabled (UHS-I): 83.5MB/s 50.5MB/s
> > UHS-II enabled : 200MB/s 75.3MB/s
> > Read Write
> > UHS-II disabled (UHS-I): 85.2MB/s 56.3MB/s
> > UHS-II enabled : 203MB/s 75.8MB/s
> > Read Write
> > UHS-II disabled (UHS-I): 82.9MB/s 51.1MB/s
> > UHS-II enabled : 196MB/s 77.8MB/s
> >
> > Test command
> > =====
> > Read: dd if=/dev/mmcxxx of=/dev/null bs=4096k count=2000 iflag=direct
> > Write:dd if=/dev/zero of=/dev/mmcxxx bs=4096k count=2000 oflag=direct
> >
> > Changes in v23 (October. 18, 2024)
> > * Rebase on latest mmc/next.
> > * Version 22 patch#1-patch#6 have already been applied to the mmc/next
> > branch, so the patch order for version 23 has been shifted forward.
> > * Patch#1: Remove mmc_uhs2_card_prepare_cmd() function.
> > Remove mmc_sd_can_poweroff_notify() function.
> > Modify ios.timing setting in the sd_uhs2_power_off() function.
> > Restore the position of assign the host->card to original
> > position in the sd_uhs2_init_card() function.
> > Remove unnecessary error handle in the sd_uhs2_init_card()
> > function.
> > Add oldcard judgment to skip some programs in the
> > sd_uhs2_legacy_init() function.
> > Remove unnecessary error handle in the sd_uhs2_legacy_init()
> > function.
> > Remove mmc_card_set_present() function in the
> > sd_uhs2_reinit()function.
>
> I have queued this up for next, with a few amendments and added
> Adrian's acks to the SDHCI patches.
>
> When applying, I made a couple of cleanups and fixed
> sd_uhs2_power_off() to patch 1. I also took care of the checkpatch
> warnings for the SDHCI patches.
>
> That said, there are still some issues that remain to be fixed, but
> let's do that on top of the $subject series. See below:
>
> *) The error/re-init path of the card is broken, as also pointed out
> by Adrian. I will work on this shortly and send patches to fix this
> up, please help to review and test.
> *) We should read the card's ext registers, to for example enable the
> card's internal cache to be used.
> *) We should avoid unnecessary commands during re-init of the card, to
> decrease latency.
> *) During suspend, we should probably send the go-dormant command with
> the hibernate bit, rather than just doing a plain power-off.
>
> [...]
>
> Thanks and kind regards
> Uffe
Hi, Ulf and Adrian
I got it.
I will continue to discuss and implement the above issues with you and
offer any help I can.
Finally, thank you very much for your help and advice on this series of patches.
Thanks, Victor Shih
^ permalink raw reply [flat|nested] 27+ messages in thread