"On Tue, 13 Dec 2022 at 10:01, Victor Shih <victorshihgli@xxxxxxxxx> wrote: > > Embed UHS-II access/control functionality into the MMC request > processing flow. > > Signed-off-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx> > Signed-off-by: Jason Lai <jason.lai@xxxxxxxxxxxxxxxxxxx> > Signed-off-by: Victor Shih <victor.shih@xxxxxxxxxxxxxxxxxxx> > --- > drivers/mmc/core/block.c | 6 +- > drivers/mmc/core/core.c | 20 + > drivers/mmc/core/mmc_ops.c | 25 +- > drivers/mmc/core/mmc_ops.h | 1 + > drivers/mmc/core/sd.c | 11 +- > drivers/mmc/core/sd.h | 3 + > drivers/mmc/core/sd_ops.c | 13 + > drivers/mmc/core/sd_ops.h | 3 + > drivers/mmc/core/sd_uhs2.c | 1171 +++++++++++++++++++++++++++++++++++- > 9 files changed, 1206 insertions(+), 47 deletions(-) > > diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c > index 20da7ed43e6d..d3e8ec43cdd5 100644 > --- a/drivers/mmc/core/block.c > +++ b/drivers/mmc/core/block.c > @@ -1596,6 +1596,9 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, > struct request *req = mmc_queue_req_to_req(mqrq); > struct mmc_blk_data *md = mq->blkdata; > bool do_rel_wr, do_data_tag; > + bool do_multi; > + > + do_multi = (card->uhs2_state & MMC_UHS2_INITIALIZED) ? true : false; > > mmc_blk_data_prep(mq, mqrq, recovery_mode, &do_rel_wr, &do_data_tag); > > @@ -1606,7 +1609,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, > brq->cmd.arg <<= 9; > brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; > > - if (brq->data.blocks > 1 || do_rel_wr) { > + if (brq->data.blocks > 1 || do_rel_wr || do_multi) { This looks wrong to me. UHS2 can use single block read/writes too. Right? > /* SPI multiblock writes terminate using a special > * token, not a STOP_TRANSMISSION request. > */ > @@ -1619,6 +1622,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, > brq->mrq.stop = NULL; > readcmd = MMC_READ_SINGLE_BLOCK; > writecmd = MMC_WRITE_BLOCK; > + brq->cmd.uhs2_tmode0_flag = 1; As "do_multi" is always set for UHS2, setting this flag here seems to be wrong/redundant. Anyway, if I understand correctly, the flag is intended to be used to inform the host driver whether the so-called 2L_HD_mode (half-duplex or full-duplex) should be used for the I/O request or not. To fix the above behaviour, I suggest we try to move the entire control of the flag into mmc_uhs2_prepare_cmd(). We want the flag to be set for multi block read/writes (CMD18 and CMD25), but only if the host and card supports the 2L_HD_mode too. According to my earlier suggestions, we should be able to check that via the bits we set earlier in the ios->timing. Moreover, by making mmc_uhs2_prepare_cmd() responsible for setting the flag, I think we can also move the definition of the flag into the struct uhs2_command. While at it, I suggest we also rename the flag into "tmode_half_duplex", to better describe its purpose, which also means the interpretation of the flag becomes inverted. > } > brq->cmd.opcode = rq_data_dir(req) == READ ? readcmd : writecmd; > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c > index ad1a40446813..01f8216885ad 100644 > --- a/drivers/mmc/core/core.c > +++ b/drivers/mmc/core/core.c > @@ -334,6 +334,8 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq) > > int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) > { > + struct uhs2_command uhs2_cmd; > + __be32 payload[4]; /* for maximum size */ > int err; > > init_completion(&mrq->cmd_completion); > @@ -351,6 +353,14 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) > if (err) > return err; > > + if (host->card) { > + if (host->card->uhs2_state & MMC_UHS2_INITIALIZED) { > + uhs2_cmd.payload = payload; > + mrq->cmd->uhs2_cmd = &uhs2_cmd; > + mmc_uhs2_prepare_cmd(host, mrq); > + } > + } To avoid open coding, please move this into mmc_mrq_prep() or add a new helper function for it. > + > led_trigger_event(host->led, LED_FULL); > __mmc_start_request(host, mrq); > > @@ -430,6 +440,8 @@ EXPORT_SYMBOL(mmc_wait_for_req_done); > */ > int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq) > { > + struct uhs2_command uhs2_cmd; > + __be32 payload[4]; /* for maximum size */ > int err; > > /* > @@ -450,6 +462,14 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq) > if (err) > goto out_err; > > + if (host->card) { > + if (host->card->uhs2_state & MMC_UHS2_INITIALIZED) { > + uhs2_cmd.payload = payload; > + mrq->cmd->uhs2_cmd = &uhs2_cmd; > + mmc_uhs2_prepare_cmd(host, mrq); > + } > + } Ditto. > + > err = host->cqe_ops->cqe_request(host, mrq); > if (err) > goto out_err; > diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c > index 81c55bfd6e0c..daa1f4ccd99a 100644 > --- a/drivers/mmc/core/mmc_ops.c > +++ b/drivers/mmc/core/mmc_ops.c > @@ -144,10 +144,24 @@ int mmc_set_dsr(struct mmc_host *host) > return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); > } > > +int __mmc_go_idle(struct mmc_host *host) > +{ > + struct mmc_command cmd = {}; > + int err; > + > + cmd.opcode = MMC_GO_IDLE_STATE; > + cmd.arg = 0; > + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + mmc_delay(1); > + > + return err; > +} > + > int mmc_go_idle(struct mmc_host *host) > { > int err; > - struct mmc_command cmd = {}; > > /* > * Non-SPI hosts need to prevent chipselect going active during > @@ -163,13 +177,7 @@ int mmc_go_idle(struct mmc_host *host) > mmc_delay(1); > } > > - cmd.opcode = MMC_GO_IDLE_STATE; > - cmd.arg = 0; > - cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; > - > - err = mmc_wait_for_cmd(host, &cmd, 0); > - > - mmc_delay(1); > + err = __mmc_go_idle(host); The above changes for mmc_go_idle() are pure refactorings, please move these changes into a separate patch that precedes $subject patch. This will help to simplify the review. > > if (!mmc_host_is_spi(host)) { > mmc_set_chip_select(host, MMC_CS_DONTCARE); > @@ -300,6 +308,7 @@ int mmc_send_adtc_data(struct mmc_card *card, struct mmc_host *host, u32 opcode, > * not R1 plus a data block. > */ > cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; > + cmd.uhs2_tmode0_flag = 1; As I stated above, I think we can drop this and rather manage the flag from mmc_uhs2_prepare_cmd() instead. From now on, I will stop to comment more on the use of the uhs2_tmode0_flag, as the similar comments applies to more places. [...] > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h > index 3ba7b3cf4652..29c802dec988 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; Looks like this should be "struct mmc_request;" instead. > struct mmc_host; > > int mmc_app_set_bus_width(struct mmc_card *card, int width); > @@ -19,10 +20,12 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr); > int mmc_send_if_cond_pcie(struct mmc_host *host, u32 ocr); > int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca); > int mmc_app_send_scr(struct mmc_card *card); > +int mmc_decode_scr(struct mmc_card *card); > int mmc_sd_switch(struct mmc_card *card, int mode, int group, > u8 value, u8 *resp); > int mmc_app_sd_status(struct mmc_card *card, void *ssr); > int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card); > +void mmc_uhs2_prepare_cmd(struct mmc_host *host, struct mmc_request *mrq); > > #endif > > diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c > index 800957f74632..a79eb08ec540 100644 > --- a/drivers/mmc/core/sd_uhs2.c > +++ b/drivers/mmc/core/sd_uhs2.c > @@ -1,48 +1,125 @@ > // SPDX-License-Identifier: GPL-2.0-only > /* > * Copyright (C) 2021 Linaro Ltd > - * > * Author: Ulf Hansson <ulf.hansson@xxxxxxxxxx> > * > + * Copyright (C) 2014 Intel Corp, All Rights Reserved. > + * Author: Yi Sun <yi.y.sun@xxxxxxxxx> > + * > + * Copyright (C) 2020 Genesys Logic, Inc. > + * Authors: Ben Chuang <ben.chuang@xxxxxxxxxxxxxxxxxxx> > + * > + * Copyright (C) 2020 Linaro Limited > + * Author: AKASHI Takahiro <takahiro.akashi@xxxxxxxxxx> > + * > + * Copyright (C) 2022 Genesys Logic, Inc. > + * Authors: Jason Lai <jason.lai@xxxxxxxxxxxxxxxxxxx> > + * > * 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) /* 1ms */ > +#define UHS2_WAIT_CFG_COMPLETE_TIMEOUT_MS 100 /* 100ms */ > + > static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 }; > +int sd_uhs2_reinit(struct mmc_host *host); This should be static - and please don't use forward declaration, unless it's really necessary. > > -static int sd_uhs2_set_ios(struct mmc_host *host) > +/* > + * Internal function that does the actual ios call to the host driver, > + * optionally printing some debug output. > + */ > +static inline int sd_uhs2_set_ios(struct mmc_host *host) > { > struct mmc_ios *ios = &host->ios; > > + pr_debug("%s: clock %uHz powermode %u Vdd %u timing %u\n", > + mmc_hostname(host), ios->clock, ios->power_mode, ios->vdd, ios->timing); > + > return host->ops->uhs2_set_ios(host, ios); I have made some more thinking around the uhs2 host callbacks. As we are using the ->uhs2_control() callback for all the other uhs2 operations, it seems silly to have a separate callback for the ios. In other words, I suggest that we drop the ->uhs2_set_ios() callback altogether and replace the two users of the above function with direct calls to the ->uhs2_control() callback. See more below. > } > > static int sd_uhs2_power_up(struct mmc_host *host) > { > + int err; > + > + if (host->ios.power_mode == MMC_POWER_ON) > + return 0; > + > host->ios.vdd = fls(host->ocr_avail) - 1; > host->ios.clock = host->f_init; > host->ios.timing = MMC_TIMING_SD_UHS2; > - host->ios.power_mode = MMC_POWER_UP; > + host->ios.power_mode = MMC_POWER_ON; > > - return sd_uhs2_set_ios(host); > + err = sd_uhs2_set_ios(host); As stated above, I suggest we replace the above call with: err = host->ops->uhs2_control(host, UHS2_SET_IOS); > + > + mmc_delay(host->uhs2_ios.power_delay_ms); > + > + return err; > } > > -static void sd_uhs2_power_off(struct mmc_host *host) > +static int sd_uhs2_power_off(struct mmc_host *host) > { > + if (host->ios.power_mode == MMC_POWER_OFF) > + return 0; > + > host->ios.vdd = 0; > host->ios.clock = 0; > host->ios.timing = MMC_TIMING_LEGACY; > host->ios.power_mode = MMC_POWER_OFF; > > - sd_uhs2_set_ios(host); > + return sd_uhs2_set_ios(host); Similar to the above, I suggest we replace the above call with: return host->ops->uhs2_control(host, UHS2_SET_IOS); > > /* > @@ -61,6 +146,88 @@ static int sd_uhs2_phy_init(struct mmc_host *host) > */ > static int sd_uhs2_dev_init(struct mmc_host *host) > { > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u32 cnt; > + u32 dap, gap, resp_gap; > + u16 header, arg; > + __be32 payload[UHS2_DEV_INIT_PAYLOAD_LEN]; > + u8 gd = 0; > + u8 resp[UHS2_DEV_ENUM_RESP_LEN] = {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) > + */ > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD; > + arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_4B | > + (UHS2_DEV_CMD_DEVICE_INIT >> 8); > + > + /* > + * Refer to UHS-II Addendum Version 1.02 section 6.3.1. > + * Max. time from DEVICE_INIT CCMD EOP reception on Device > + * Rx to its SOP transmission on Device Tx(Tfwd_init_cmd) is > + * 1 second. > + */ > + cmd.busy_timeout = 1000; > + > + /* > + * Refer to UHS-II Addendum Version 1.02 section 6.2.6.3. > + * When the number of the DEVICE_INIT commands is reach to > + * 30 tiems, Host shall stop issuing DEVICE_INIT command > + * and regard it as an error. > + */ > + for (cnt = 0; cnt < 30; cnt++) { > + payload[0] = ((dap & 0xF) << 12) | > + UHS2_DEV_INIT_COMPLETE_FLAG | > + ((gd & 0xF) << 4) | > + (gap & 0xF); > + > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, > + payload, UHS2_DEV_INIT_PAYLOAD_LEN, > + resp, UHS2_DEV_INIT_RESP_LEN); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + Nitpick: Unnecessary line break. > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return err; > + } > + > + if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) { > + pr_err("%s: DEVICE_INIT response is wrong!\n", > + mmc_hostname(host)); > + return -EIO; > + } > + > + if (resp[5] & 0x8) { > + host->uhs2_caps.group_desc = gd; > + return 0; > + } > + resp_gap = resp[4] & 0x0F; > + if (gap == resp_gap) > + gd++; > + } > + if (cnt == 30) { There is no need to check cnt here. It's always 30 if we reach this point. > + pr_err("%s: DEVICE_INIT fail, already 30 times!\n", > + mmc_hostname(host)); > + return -EIO; > + } > + > return 0; > } [...] > static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card) > { > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u16 header, arg; > + __be32 payload[UHS2_CFG_WRITE_PAYLOAD_LEN]; > + u8 nMinDataGap; > + int err; > + u8 resp[5] = {0}; > + > + /* > + * 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 > + */ > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | card->uhs2_config.node_id; > + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_8B | > + (UHS2_DEV_CONFIG_GEN_SET >> 8); > + > + if (card->uhs2_config.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD && > + host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) { > + /* Support HD */ > + host->uhs2_ios.is_2L_HD_mode = true; > + nMinDataGap = 1; > + } else { > + /* Only support 2L-FD so far */ > + host->uhs2_ios.is_2L_HD_mode = false; > + nMinDataGap = 3; > + } > + > + /* > + * Most UHS-II cards only support FD and 2L-HD mode. Other lane numbers > + * defined in UHS-II addendem Ver1.01 are optional. > + */ > + host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; > + card->uhs2_config.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; > + > + payload[0] = card->uhs2_config.n_lanes_set << UHS2_DEV_CONFIG_N_LANES_POS; > + payload[1] = 0; > + payload[0] = cpu_to_be32(payload[0]); > + payload[1] = cpu_to_be32(payload[1]); > + > + /* > + * There is no payload because per spec, there should be > + * no payload field for read CCMD. > + * Plen is set in arg. Per spec, plen for read CCMD > + * represents the len of read data which is assigned in payload > + * of following RES (p136). > + */ > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN, > + NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return err; > + } > + > + /* > + * 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 > + */ > + 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) { > + card->uhs2_state |= MMC_UHS2_SPEED_B; As I suggested earlier, let's set a corresponding bit in ios->timing instead. > + card->uhs2_config.speed_range_set = > + UHS2_DEV_CONFIG_PHY_SET_SPEED_B; > + } else { > + card->uhs2_config.speed_range_set = UHS2_DEV_CONFIG_PHY_SET_SPEED_A; > + card->uhs2_state &= ~MMC_UHS2_SPEED_B; As I suggested earlier, I think it should be sufficient to clear this in sd_uhs2_power_off() (by resetting ios->timing). > + } > + > + payload[0] = card->uhs2_config.speed_range_set << UHS2_DEV_CONFIG_PHY_SET_SPEED_POS; > + > + card->uhs2_config.n_lss_sync_set = (max(card->uhs2_config.n_lss_sync, > + host->uhs2_caps.n_lss_sync) >> 2) & > + UHS2_DEV_CONFIG_N_LSS_SYN_MASK; > + host->uhs2_caps.n_lss_sync_set = card->uhs2_config.n_lss_sync_set; > + > + card->uhs2_config.n_lss_dir_set = (max(card->uhs2_config.n_lss_dir, > + host->uhs2_caps.n_lss_dir) >> 3) & > + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; > + host->uhs2_caps.n_lss_dir_set = card->uhs2_config.n_lss_dir_set; > + > + payload[1] = (card->uhs2_config.n_lss_dir_set << UHS2_DEV_CONFIG_N_LSS_DIR_POS) | > + card->uhs2_config.n_lss_sync_set; > + payload[0] = cpu_to_be32(payload[0]); > + payload[1] = cpu_to_be32(payload[1]); > + > + memset(resp, 0, sizeof(resp)); > + > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN, > + resp, 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 ((resp[2] & 0x80)) { > + pr_err("%s: %s: UHS2 CMD not accepted, resp= 0x%x!\n", > + mmc_hostname(host), __func__, 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 > + */ > + 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; > + > + payload[0] = (card->uhs2_config.maxblk_len_set << UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) | > + (card->uhs2_config.max_retry_set << UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS) | > + (card->uhs2_config.n_fcu_set << UHS2_DEV_CONFIG_N_FCU_POS); > + payload[1] = card->uhs2_config.n_data_gap_set; > + payload[0] = cpu_to_be32(payload[0]); > + payload[1] = cpu_to_be32(payload[1]); > + > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN, > + NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return err; > + } > + > + /* > + * 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. > + */ > + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_8B | > + (UHS2_DEV_CONFIG_GEN_SET >> 8); > + > + payload[0] = 0; > + payload[1] = UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE; > + payload[0] = cpu_to_be32(payload[0]); > + payload[1] = cpu_to_be32(payload[1]); > + > + memset(resp, 0, sizeof(resp)); > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, UHS2_CFG_WRITE_PAYLOAD_LEN, > + resp, 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_change_speed(struct mmc_host *host, u32 node_id) > +{ > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u16 header, arg; > + int err; > + struct sd_uhs2_wait_active_state_data cb_data = { > + .host = host, > + .cmd = &cmd > + }; > + > + /* Change Speed Range at controller side. */ > + err = host->ops->uhs2_control(host, UHS2_SET_SPEED_B); As I asked for in patch5, is this call really needed for sdhci? > + if (err) { > + pr_err("%s: %s: UHS2 SET_SPEED fail!\n", mmc_hostname(host), __func__); > + return err; > + } > + > + 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. > + */ > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | node_id; > + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) | > + UHS2_NATIVE_CMD_READ | > + UHS2_NATIVE_CMD_PLEN_8B | > + (UHS2_DEV_CONFIG_GEN_SET >> 8); > + > + sd_uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 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 int sd_uhs2_get_ro(struct mmc_host *host) > +{ > + /* > + * Some systems don't feature a write-protect pin and don't need one. > + * E.g. because they only have micro-SD card slot. For those systems > + * assume that the SD card is always read-write. > + */ > + if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT) > + return 0; > + > + if (!host->ops->get_ro) > + return -1; > + > + return host->ops->get_ro(host); Let's avoid the open coding and use mmc_sd_get_ro() instead. > +} > + > +/* > + * Mask off any voltages we don't support and select > + * the lowest voltage > + */ > +u32 sd_uhs2_select_voltage(struct mmc_host *host, u32 ocr) > +{ > + int bit; > + int err; > + > + /* > + * Sanity check the voltages that the card claims to > + * support. > + */ > + if (ocr & 0x7F) { > + dev_warn(mmc_dev(host), "card claims to support voltages below defined range\n"); > + ocr &= ~0x7F; > + } > + > + ocr &= host->ocr_avail; > + if (!ocr) { > + dev_warn(mmc_dev(host), "no support for card's volts\n"); > + return 0; > + } > + > + if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) { > + bit = ffs(ocr) - 1; > + ocr &= 3 << bit; > + /* Power cycle */ > + err = sd_uhs2_power_off(host); > + if (err) > + return 0; > + err = sd_uhs2_reinit(host); As also pointed out by Adrian, this path becomes circular and doesn't work as expected. At this point, I would prefer to keep things as simple as possible, so I suggest that we drop this path entirely for UHS2. > + if (err) > + return 0; > + } else { > + bit = fls(ocr) - 1; > + ocr &= 3 << bit; Note that, in mmc_select_voltage() the corresponding code has been updated in commit 39a72dbfe188 ("mmc: core: properly select voltage range without power cycle") that fixes a real problem for us. By taking the above observations into consideration, it looks like it may be better to reuse mmc_select_voltage() for UHS-II after all. To prevent the power cycle path for UHS-II, we can just add a check for the ios->timings in there. That should work, I think. > + if (bit != host->ios.vdd) > + dev_warn(mmc_dev(host), "exceeding card's volts\n"); > + } > + > + return ocr; > +} > + > /* > * Initialize the UHS-II card through the SD-TRAN transport layer. This enables > * commands/requests to be backwards compatible through the legacy SD protocol. > @@ -107,14 +893,149 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card) > */ > static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card) I decided to postpone the review of the following parts below, which consists of the legacy card initialization (SD-tran) and the power-management support - as it looks like there are enough for you to work on for a while. Let me also ask about the power management support - did you test the system suspend/resume path? Just to understand, if this is more an attempt to make it work or whether it's actually tested/works? [...] Kind regards Uffe