Received: by 2002:a05:6a10:a0d1:0:0:0:0 with SMTP id j17csp3022488pxa; Tue, 18 Aug 2020 04:43:52 -0700 (PDT) X-Google-Smtp-Source: ABdhPJw0gHIE5qFKW1vK4gbRxU3A4ND9k/Gs2oajhstmVFUo1+L2Q2mr9t7ueuyz+hw2WKR4PKkL X-Received: by 2002:a17:906:12cd:: with SMTP id l13mr19346897ejb.385.1597751032111; Tue, 18 Aug 2020 04:43:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1597751032; cv=none; d=google.com; s=arc-20160816; b=RACW4lMhRrUlJEy68qKEyBzzVQ9JOmHxrmbZrIva3GrFM1F69Y2p5h32jIn251LnP3 3ddeY44fGFdL23yl216Zit9Kg8XFSSal3IfEUmnq/SC7URJxorfBf4X0m1BXW33ssYUM Xo0pAApGVrZc34qQBvmfSrS9dxeMSSPc1DvdBuY0R2Is0BysVV0UOhQSgY6hEGgQHc4H P49lmXJ8IkCcW7zbzUUlY4gDMR6FPA/FPQZ0poNFTiwK1p2G05O53OLQF9fLZNxujHPQ j3RGY+GvaNfv6yPRh3z1NgadCww6HFrvb5Ij8FiMhwcGP78GUu1rjoZrc5WXqB0o16aF 36uQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=AokTIXk0nUaVujg9upO0c+9NhEOmuhyemAzB/sUEvK4=; b=NFfFK/dmrCKrglgATFFpr9CX3oOeFkW6a7N5q384t268kcw437XUU/4jYIzwOav249 zv/XKP9UQPPuMKomD/esccN25AFnJE1Bn48QVXQYXJCoFNZXI3bg7vZ0wcMdrI1OrNZD M0TbIH6zUM4EK13wSR77aQBkLhm5qJkPkaW0JAZbr0PcxjneQQC565XDvqgi6QRrXG0Y CDScuxwgbj6tcwmwvJejCifN9K7KLhpb3Bc/I9HGauw42MTl+mFBWymYWrCYhybhw4eB K6qSUvSaeOdro9qNiPJWuTBD1tm5jHRW4ayUAaCj4QyYGlZi0y68xQS2twRUX88115mG PkcQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=BSt3N6OC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id co5si7501635edb.210.2020.08.18.04.43.26; Tue, 18 Aug 2020 04:43:52 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=BSt3N6OC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726612AbgHRLmr (ORCPT + 99 others); Tue, 18 Aug 2020 07:42:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53552 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726145AbgHRLma (ORCPT ); Tue, 18 Aug 2020 07:42:30 -0400 Received: from mail-vs1-xe41.google.com (mail-vs1-xe41.google.com [IPv6:2607:f8b0:4864:20::e41]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CA3EEC061389 for ; Tue, 18 Aug 2020 04:42:29 -0700 (PDT) Received: by mail-vs1-xe41.google.com with SMTP id o184so9928238vsc.0 for ; Tue, 18 Aug 2020 04:42:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=AokTIXk0nUaVujg9upO0c+9NhEOmuhyemAzB/sUEvK4=; b=BSt3N6OCdJZFmE/Pj2ga6gSl07wlabu8iWajwO0WzZGbQXnYVQG5mIod0PsRLb3qWq 7nZ+vfouyKuOPFABFs8cTygPm8EHnQ4AFsj5LnVwyV/ooyshCRu16RMwkT84emlV2G8M GV17uHhkoQomRb3x/QuPOddxZDHiy+pEdksH/9FVHGkdO5zhCXk/sU6wos7jvD/bbaxt hIhk4VOEZfFEsfF6fGM5kWkxjQvqNqMhtRa/dUJdOFsInCKztVZ4c5fS8aNoxP9eytko hWNza2v3H1R3FNJYWzTyxTt7ohGJF72dp3hetBKvekATP59BRdxWSTO7S7SN22IDwBUW m5sA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=AokTIXk0nUaVujg9upO0c+9NhEOmuhyemAzB/sUEvK4=; b=VZf680UASNAXIfWKCfvmsXtOwu0TsFzesrZt5WoOlHAurBUwjWPwV/BX9EBy/Ilz1+ tG0v0mNWlj2fBHyg5Q0kTzWEVVQEZOgbbBTX8fxiMT1g6PJlNdAP20ISbMX8TPRc9Uyp s7+RiL06/5GdwvcKdZVgM+c2qVk5zmeQkAeABp6yPw+dWN5nrH9SS/RB2u0qh5C+F9zQ jOuvdYZ/NgasBwdSF3T/WZJAVGF59oTcvgoIhjdwLE1fdrz0qyzMy+a72TGq++hN4Fwz EevWgtAkBmc17qYo+yf+Z5S5Y8N2wQRojfix7SLOpPe33egnGzkHjYx2E7EtNT2JVYxR qYuQ== X-Gm-Message-State: AOAM530AhNe9zGamAb4zj+mVfalVf3s/VrNlB3Ul1/Hu/hwsziW5S2++ YJPCWmZOlc9cPaFvPa+tRlVzoKiR/58eMkgJ7p0EgA== X-Received: by 2002:a67:6305:: with SMTP id x5mr10682766vsb.35.1597750944032; Tue, 18 Aug 2020 04:42:24 -0700 (PDT) MIME-Version: 1.0 References: <20200710110848.29114-1-benchuanggli@gmail.com> In-Reply-To: <20200710110848.29114-1-benchuanggli@gmail.com> From: Ulf Hansson Date: Tue, 18 Aug 2020 13:41:46 +0200 Message-ID: Subject: Re: [RFC PATCH V3 04/21] mmc: core: UHS-II support, try to select UHS-II interface To: Ben Chuang Cc: Adrian Hunter , "linux-mmc@vger.kernel.org" , Linux Kernel Mailing List , Ben Chuang , Takahiro Akashi , greg.tu@genesyslogic.com.tw Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, 10 Jul 2020 at 13:08, Ben Chuang wrote: > > From: Ben Chuang > > The flow of "interface selection and initialization" was a bit modified > for UHS-II card. This commit follows the sequence defined in SD > specification (Part 1). > See section 7.2.3 in "UHS-II Simplified Addendum." > > Signed-off-by: Ben Chuang > Signed-off-by: AKASHI Takahiro > --- > drivers/mmc/core/Makefile | 2 +- > drivers/mmc/core/bus.c | 5 +- > drivers/mmc/core/core.c | 33 +- > drivers/mmc/core/sd.c | 26 ++ > drivers/mmc/core/uhs2.c | 800 ++++++++++++++++++++++++++++++++++++++ > drivers/mmc/core/uhs2.h | 20 + > 6 files changed, 882 insertions(+), 4 deletions(-) > create mode 100644 drivers/mmc/core/uhs2.c > create mode 100644 drivers/mmc/core/uhs2.h > > diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile > index 95ffe008ebdf..e2a90dc98afc 100644 > --- a/drivers/mmc/core/Makefile > +++ b/drivers/mmc/core/Makefile > @@ -8,7 +8,7 @@ mmc_core-y := core.o bus.o host.o \ > mmc.o mmc_ops.o sd.o sd_ops.o \ > sdio.o sdio_ops.o sdio_bus.o \ > sdio_cis.o sdio_io.o sdio_irq.o \ > - slot-gpio.o regulator.o > + slot-gpio.o regulator.o uhs2.o > mmc_core-$(CONFIG_OF) += pwrseq.o > obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o > obj-$(CONFIG_PWRSEQ_SD8787) += pwrseq_sd8787.o > diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c > index 70207f11a654..66d3e942f3fc 100644 > --- a/drivers/mmc/core/bus.c > +++ b/drivers/mmc/core/bus.c > @@ -350,8 +350,9 @@ int mmc_add_card(struct mmc_card *card) > } else { > pr_info("%s: new %s%s%s%s%s%s card at address %04x\n", > mmc_hostname(card->host), > - mmc_card_uhs(card) ? "ultra high speed " : > - (mmc_card_hs(card) ? "high speed " : ""), > + mmc_card_uhs2(card) ? "ultra high speed 2 " : > + (mmc_card_uhs(card) ? "ultra high speed 1 " : Perhaps use "UHS-I" and "UHS-II" instead. > + (mmc_card_hs(card) ? "high speed " : "")), > mmc_card_hs400(card) ? "HS400 " : > (mmc_card_hs200(card) ? "HS200 " : ""), > mmc_card_hs400es(card) ? "Enhanced strobe " : "", > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c > index c6540d56646b..e2534f3446ce 100644 > --- a/drivers/mmc/core/core.c > +++ b/drivers/mmc/core/core.c > @@ -31,6 +31,7 @@ > #include > #include > #include > +#include > > #define CREATE_TRACE_POINTS > #include > @@ -41,6 +42,7 @@ > #include "host.h" > #include "sdio_bus.h" > #include "pwrseq.h" > +#include "uhs2.h" > > #include "mmc_ops.h" > #include "sd_ops.h" > @@ -51,6 +53,7 @@ > #define SD_DISCARD_TIMEOUT_MS (250) > > static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; > +static const unsigned int uhs2_freqs[] = { 52000000, 26000000 }; > > /* > * Enabling software CRCs on the data blocks can be a significant (30%) > @@ -2165,9 +2168,10 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) > if (!mmc_attach_sdio(host)) > return 0; > > - if (!(host->caps2 & MMC_CAP2_NO_SD)) > + if (!(host->caps2 & MMC_CAP2_NO_SD)) { > if (!mmc_attach_sd(host)) > return 0; > + } > > if (!(host->caps2 & MMC_CAP2_NO_MMC)) > if (!mmc_attach_mmc(host)) > @@ -2300,6 +2304,33 @@ void mmc_rescan(struct work_struct *work) > goto out; > } > > + if (host->caps & MMC_CAP_UHS2) { > + /* > + * Start to try UHS-II initialization from 52MHz to 26MHz > + * (RCLK range) per spec. > + */ > + for (i = 0; i < ARRAY_SIZE(uhs2_freqs); i++) { > + unsigned int freq = uhs2_freqs[i]; > + int err; > + > + err = mmc_uhs2_rescan_try_freq(host, > + max(freq, host->f_min)); > + if (!err) { > + mmc_release_host(host); > + goto out; > + } > + > + if (err == UHS2_PHY_INIT_ERR) > + /* UHS2 IF detect or Lane Sync error. > + * Try legacy interface. > + */ > + break; > + > + if (freq <= host->f_min) > + break; > + } Assuming we change the initialization order, trying to initialize the legacy SD interface first to figure out if UHS-II is supported, then I think we should be able to move the entire code above into a the UHS-II specific code/path. > + } > + > for (i = 0; i < ARRAY_SIZE(freqs); i++) { > unsigned int freq = freqs[i]; > if (freq > host->f_max) { > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c > index 5a2210c25aa7..c5b071bd98b3 100644 > --- a/drivers/mmc/core/sd.c > +++ b/drivers/mmc/core/sd.c > @@ -901,6 +901,20 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, > err = mmc_read_switch(card); > if (err) > return err; > + if (host->flags & MMC_UHS2_INITIALIZED) { Rather than using host->flags, to tweak the behavior of mmc_sd_setup_card() to support UHS-II, I would prefer to give mmc_sd_setup_card() a new in-parameter that it can look at instead. > + u8 status[64]; > + /* > + * send CMD6 to set Maximum Power Consumption > + * to get better performance > + */ > + err = mmc_sd_switch(card, 0, 3, > + SD_SET_CURRENT_LIMIT_1000, status); > + if (!err) { > + err = mmc_sd_switch(card, 1, 3, > + SD_SET_CURRENT_LIMIT_1000, > + status); > + } > + } > } > > /* > @@ -1043,6 +1057,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, > if (err) > goto free_card; > > + /* For UHS2, skip the UHS-I initialization. */ > + if ((host->flags & MMC_UHS2_SUPPORT) && > + (host->flags & MMC_UHS2_INITIALIZED)) > + goto done; mmc_sd_init_card() is also called at system resume, to re-initialize the card. Re-initialization is going to be needed for UHS-II cards as well, since we should ideally avoid wasting energy in system suspend state for these cards as well. Perhaps you haven't looked at that part yet? Or I may be missing something here? > + > /* > * If the card has not been power cycled, it may still be using 1.8V > * signaling. Detect that situation and try to initialize a UHS-I (1.8V) > @@ -1334,6 +1353,13 @@ int mmc_attach_sd(struct mmc_host *host) > > 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 ((host->flags & MMC_UHS2_INITIALIZED) && !rocr) > + rocr = host->ocr_avail; As stated already earlier above, we should probably be able to move any UHS-II specific bits to the UHS-II code/path. > + > /* > * Can we support the voltage(s) of the card(s)? > */ > diff --git a/drivers/mmc/core/uhs2.c b/drivers/mmc/core/uhs2.c > new file mode 100644 > index 000000000000..a3b20ac6f315 > --- /dev/null > +++ b/drivers/mmc/core/uhs2.c > @@ -0,0 +1,800 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * linux/drivers/mmc/core/uhs2.c - UHS-II driver > + * > + * Copyright (C) 2014 Intel Corp, All Rights Reserved. > + * Copyright (C) 2020 Genesys Logic, Inc. > + * Authors: Ben Chuang > + * Copyright (C) 2020 Linaro Limited > + * Author: AKASHI Takahiro > + */ > + > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "uhs2.h" > +#include "mmc_ops.h" > +#include "sd_ops.h" > +#include "core.h" > + > +#define DBG(f, x...) \ > + pr_warn("[%s()]: " f, __func__, ## x) > + > +/** > + * uhs2_cmd_assemble - assemble and build up uhs2 command > + * @cmd: MMC command > + * @uhs2_cmd: UHS2 command > + * @header: Value of packet header > + * @arg: Argument of packet > + * @payload: Payload of packet > + * @plen: Payload length > + * @resp: Buffer for response > + * @resp_len: Response buffer length > + * > + * resp is inputted outside which should be a variable created by caller > + * so caller should handle it. For SD command, there is no uhs2_resp and > + * response should be stored in resp of mmc_command. > + */ > +static void uhs2_cmd_assemble(struct mmc_command *cmd, > + struct uhs2_command *uhs2_cmd, > + u16 header, u16 arg, > + u32 *payload, u8 plen, u8 *resp, u8 resp_len) > +{ > + uhs2_cmd->header = header; > + uhs2_cmd->arg = arg; > + uhs2_cmd->payload = payload; > + uhs2_cmd->payload_len = plen * sizeof(u32); > + uhs2_cmd->packet_len = uhs2_cmd->payload_len + 4; > + > + cmd->uhs2_cmd = uhs2_cmd; > + cmd->uhs2_resp = resp; > + cmd->uhs2_resp_len = resp_len; > + > + pr_debug("%s: uhs2_cmd: header=0x%x arg=0x%x\n", > + __func__, uhs2_cmd->header, uhs2_cmd->arg); > + pr_debug("%s: payload_len=%d packet_len=%d resp_len=%d\n", > + __func__, uhs2_cmd->payload_len, uhs2_cmd->packet_len, > + cmd->uhs2_resp_len); I noticed that there is lots of debug code around. I understand that it's useful during development, but I think we should be able to remove some of it. Can you please have a look and clean up that? > +} > + > +static int uhs2_dev_init(struct mmc_host *host) > +{ > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u32 cnt; > + u32 dap, gap, gap1; > + u16 header = 0, arg = 0; > + u32 payload[1]; > + u8 plen = 1; > + u8 gd = 0, cf = 1; > + u8 resp[6] = {0}; > + u8 resp_len = 6; > + int err; > + > + dap = host->uhs2_caps.dap; > + gap = host->uhs2_caps.gap; > + > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD; > + arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_4B | > + (UHS2_DEV_CMD_DEVICE_INIT >> 8); > + > + /* need this for some cards */ > + cmd.busy_timeout = 1000; Is this according to spec? Please add defines for timeout values. > + > + for (cnt = 0; cnt < 30; cnt++) { > + payload[0] = ((dap & 0xF) << 12) | > + (cf << 11) | > + ((gd & 0xF) << 4) | > + (gap & 0xF); > + > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, > + payload, plen, resp, resp_len); > + > + DBG("Begin DEVICE_INIT, header=0x%x, arg=0x%x, payload=0x%x.\n", > + header, arg, payload[0]); > + > + DBG("Sending DEVICE_INIT. Count = %d\n", cnt); > + err = mmc_wait_for_cmd(host, &cmd, 0); Debug code, please clean this up... > + > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { Debug code, please clean this up. I am stopping from commenting on that, as stated, please have a look at all the code to clean this up. > + int i; > + > + pr_warn("%s: DEVICE_INIT response is: ", > + mmc_hostname(host)); > + for (i = 0; i < resp_len; i++) > + pr_warn("0x%x ", resp[i]); > + pr_warn("\n"); > + } > + > + 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) { > + DBG("CF is set, device is initialized!\n"); > + host->group_desc = gd; > + break; > + } > + gap1 = resp[4] & 0x0F; > + if (gap == gap1) > + gd++; > + } > + if (cnt == 30) { > + pr_err("%s: DEVICE_INIT fail, already 30 times!\n", > + mmc_hostname(host)); > + return -EIO; > + } > + > + return 0; > +} > + > +static int uhs2_enum(struct mmc_host *host) > +{ > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u16 header = 0, arg = 0; > + u32 payload[1]; > + u8 plen = 1; > + u8 id_f = 0xF, id_l = 0x0; > + u8 resp[8] = {0}; > + u8 resp_len = 8; > + int err; > + > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD; > + arg = ((UHS2_DEV_CMD_ENUMERATE & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_4B | > + (UHS2_DEV_CMD_ENUMERATE >> 8); > + > + payload[0] = (id_f << 4) | id_l; > + > + DBG("Begin ENUMERATE, header=0x%x, arg=0x%x, payload=0x%x.\n", > + header, arg, payload[0]); > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, > + resp, resp_len); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { > + int i; > + > + pr_warn("%s: ENUMERATE response is: ", mmc_hostname(host)); > + for (i = 0; i < resp_len; i++) > + pr_warn("0x%x ", resp[i]); > + pr_warn("\n"); > + } > + > + if (resp[3] != (UHS2_DEV_CMD_ENUMERATE & 0xFF)) { > + pr_err("%s: ENUMERATE response is wrong!\n", > + mmc_hostname(host)); > + return -EIO; > + } > + > + id_f = (resp[4] >> 4) & 0xF; > + id_l = resp[4] & 0xF; > + DBG("id_f = %d, id_l = %d.\n", id_f, id_l); > + DBG("Enumerate Cmd Completed. No. of Devices connected = %d\n", > + id_l - id_f + 1); > + host->uhs2_dev_prop.node_id = id_f; > + > + return 0; > +} > + > +static int uhs2_config_read(struct mmc_host *host) > +{ > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u16 header = 0, arg = 0; > + u32 cap; > + int err; > + > + DBG("INQUIRY_CFG: read Generic Caps.\n"); > + header = UHS2_NATIVE_PACKET | > + UHS2_PACKET_TYPE_CCMD | > + host->uhs2_dev_prop.node_id; > + arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) | > + UHS2_NATIVE_CMD_READ | > + UHS2_NATIVE_CMD_PLEN_4B | > + (UHS2_DEV_CONFIG_GEN_CAPS >> 8); > + > + DBG("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n", > + header, arg); > + /* 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). > + */ > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { > + int i; > + > + pr_warn("%s: INQUIRY_CFG generic response is: ", > + mmc_hostname(host)); > + for (i = 0; i < 2; i++) > + pr_warn("0x%x ", cmd.resp[i]); > + pr_warn("\n"); > + } > + > + cap = cmd.resp[0]; > + DBG("Device Generic Caps (0-31) is: 0x%x.\n", cap); > + host->uhs2_dev_prop.n_lanes = (cap >> UHS2_DEV_CONFIG_N_LANES_POS) & > + UHS2_DEV_CONFIG_N_LANES_MASK; > + host->uhs2_dev_prop.dadr_len = (cap >> UHS2_DEV_CONFIG_DADR_POS) & > + UHS2_DEV_CONFIG_DADR_MASK; > + host->uhs2_dev_prop.app_type = (cap >> UHS2_DEV_CONFIG_APP_POS) & > + UHS2_DEV_CONFIG_APP_MASK; > + > + DBG("INQUIRY_CFG: read PHY Caps.\n"); > + arg = ((UHS2_DEV_CONFIG_PHY_CAPS & 0xFF) << 8) | > + UHS2_NATIVE_CMD_READ | > + UHS2_NATIVE_CMD_PLEN_8B | > + (UHS2_DEV_CONFIG_PHY_CAPS >> 8); > + > + DBG("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n", > + header, arg); > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { > + int i; > + > + pr_warn("%s: INQUIRY_CFG PHY response is: ", > + mmc_hostname(host)); > + for (i = 0; i < 2; i++) > + pr_warn("0x%x ", cmd.resp[i]); > + pr_warn("\n"); > + } > + > + cap = cmd.resp[0]; > + DBG("Device PHY Caps (0-31) is: 0x%x.\n", cap); > + host->uhs2_dev_prop.phy_minor_rev = cap & > + UHS2_DEV_CONFIG_PHY_MINOR_MASK; > + host->uhs2_dev_prop.phy_major_rev = (cap >> > + UHS2_DEV_CONFIG_PHY_MAJOR_POS) & > + UHS2_DEV_CONFIG_PHY_MAJOR_MASK; > + host->uhs2_dev_prop.can_hibernate = (cap >> > + UHS2_DEV_CONFIG_CAN_HIBER_POS) & > + UHS2_DEV_CONFIG_CAN_HIBER_MASK; > + > + cap = cmd.resp[1]; > + DBG("Device PHY Caps (32-63) is: 0x%x.\n", cap); > + host->uhs2_dev_prop.n_lss_sync = cap & UHS2_DEV_CONFIG_N_LSS_SYN_MASK; > + host->uhs2_dev_prop.n_lss_dir = (cap >> > + UHS2_DEV_CONFIG_N_LSS_DIR_POS) & > + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; > + if (host->uhs2_dev_prop.n_lss_sync == 0) > + host->uhs2_dev_prop.n_lss_sync = 16 << 2; > + else > + host->uhs2_dev_prop.n_lss_sync <<= 2; > + > + if (host->uhs2_dev_prop.n_lss_dir == 0) > + host->uhs2_dev_prop.n_lss_dir = 16 << 3; > + else > + host->uhs2_dev_prop.n_lss_dir <<= 3; > + > + DBG("INQUIRY_CFG: read LINK-TRAN Caps.\n"); > + 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); > + > + DBG("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n", > + header, arg); > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { > + int i; > + > + pr_warn("%s: INQUIRY_CFG Link-Tran response is: ", > + mmc_hostname(host)); > + for (i = 0; i < 2; i++) > + pr_warn("0x%x ", cmd.resp[i]); > + pr_warn("\n"); > + } > + > + cap = cmd.resp[0]; > + DBG("Device LINK-TRAN Caps (0-31) is: 0x%x.\n", cap); > + host->uhs2_dev_prop.link_minor_rev = cap & > + UHS2_DEV_CONFIG_LT_MINOR_MASK; > + host->uhs2_dev_prop.link_major_rev = (cap >> > + UHS2_DEV_CONFIG_LT_MAJOR_POS) & > + UHS2_DEV_CONFIG_LT_MAJOR_MASK; > + host->uhs2_dev_prop.n_fcu = (cap >> UHS2_DEV_CONFIG_N_FCU_POS) & > + UHS2_DEV_CONFIG_N_FCU_MASK; > + host->uhs2_dev_prop.dev_type = (cap >> UHS2_DEV_CONFIG_DEV_TYPE_POS) & > + UHS2_DEV_CONFIG_DEV_TYPE_MASK; > + host->uhs2_dev_prop.maxblk_len = (cap >> > + UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) & > + UHS2_DEV_CONFIG_MAX_BLK_LEN_MASK; > + > + cap = cmd.resp[1]; > + DBG("Device LINK-TRAN Caps (32-63) is: 0x%x.\n", cap); > + host->uhs2_dev_prop.n_data_gap = cap & UHS2_DEV_CONFIG_N_DATA_GAP_MASK; > + if (host->uhs2_dev_prop.n_fcu == 0) > + host->uhs2_dev_prop.n_fcu = 256; > + > + return 0; > +} > + > +static int uhs2_config_write(struct mmc_host *host) > +{ > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u16 header = 0, arg = 0; > + u32 nTry; > + u32 payload[2]; > + u8 nMinDataGap; > + u8 plen; > + int err; > + u8 resp[5] = {0}; > + u8 resp_len = 5; > + /* > + * must long enough for RECV_SW_DIR == 2; > + * need lss_dir at least 4 for GL9755 device > + * max compitable g_lss_dir = 0 > + */ > + u32 g_lss_dir = 0; > + > + DBG("SET_COMMON_CFG: write Generic Settings.\n"); > + header = UHS2_NATIVE_PACKET | > + UHS2_PACKET_TYPE_CCMD | host->uhs2_dev_prop.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 (host->uhs2_dev_prop.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD && > + host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) { > + /* Support HD */ > + DBG("Both Host and device support 2L-HD.\n"); > + host->flags |= MMC_UHS2_2L_HD; > + host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; > + host->uhs2_dev_prop.n_lanes_set = > + UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; > + nMinDataGap = 1; > + } else { > + /* Only support 2L-FD so far */ > + host->flags &= ~MMC_UHS2_2L_HD; > + host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; > + host->uhs2_dev_prop.n_lanes_set = > + UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; > + nMinDataGap = 3; > + } > + > + plen = 2; > + payload[0] = host->uhs2_dev_prop.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]); > + > + DBG("Begin SET_COMMON_CFG, header=0x%x, arg=0x%x\n", header, arg); > + DBG("UHS2 write Generic Settings %08x %08x\n", > + payload[0], payload[1]); > + DBG("flags=%08x dev_prop.n_lanes_set=%x host_caps.n_lanes_set=%x\n", > + host->flags, > + host->uhs2_dev_prop.n_lanes_set, > + host->uhs2_caps.n_lanes_set); > + > + /* > + * 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). > + */ > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + DBG("SET_COMMON_CFG: PHY Settings.\n"); > + arg = ((UHS2_DEV_CONFIG_PHY_SET & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_8B | > + (UHS2_DEV_CONFIG_PHY_SET >> 8); > + > + for (nTry = 0; nTry < 2; nTry++) { > + plen = 2; > + > + if (host->uhs2_caps.speed_range == > + UHS2_DEV_CONFIG_PHY_SET_SPEED_B) { > + host->flags |= MMC_UHS2_SPEED_B; > + host->uhs2_dev_prop.speed_range_set = > + UHS2_DEV_CONFIG_PHY_SET_SPEED_B; > + DBG("set dev_prop.speed_range_set to SPEED_B\n"); > + } else { > + host->uhs2_dev_prop.speed_range_set = > + UHS2_DEV_CONFIG_PHY_SET_SPEED_A; > + host->flags &= ~MMC_UHS2_SPEED_B; > + DBG("set dev_prop.speed_range_set to SPEED_A\n"); > + } > + > + payload[0] = host->uhs2_dev_prop.speed_range_set << > + UHS2_DEV_CONFIG_PHY_SET_SPEED_POS; > + > + host->uhs2_dev_prop.n_lss_sync_set = > + (min(host->uhs2_dev_prop.n_lss_sync, > + host->uhs2_caps.n_lss_sync) >> 2) & > + UHS2_DEV_CONFIG_N_LSS_SYN_MASK; > + host->uhs2_caps.n_lss_sync_set = > + host->uhs2_dev_prop.n_lss_sync_set; > + > + if (nTry) { > + host->uhs2_dev_prop.n_lss_dir_set = > + (max(host->uhs2_dev_prop.n_lss_dir, > + host->uhs2_caps.n_lss_dir) >> 3) & > + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; > + host->uhs2_caps.n_lss_dir_set = > + host->uhs2_dev_prop.n_lss_dir_set; > + payload[1] = (host->uhs2_dev_prop.n_lss_dir_set << > + UHS2_DEV_CONFIG_N_LSS_DIR_POS) | > + host->uhs2_dev_prop.n_lss_sync_set; > + } else { > + host->uhs2_caps.n_lss_dir_set = > + (host->uhs2_dev_prop.n_lss_dir >> 3) & > + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; > + host->uhs2_dev_prop.n_lss_dir_set = > + ((host->uhs2_caps.n_lss_dir >> 3) + 1) & > + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; > + } > + > + if (!g_lss_dir) { > + host->uhs2_dev_prop.n_lss_dir_set = 0; > + } else { > + host->uhs2_dev_prop.n_lss_dir_set = > + max((u8)g_lss_dir, > + host->uhs2_dev_prop.n_lss_dir_set); > + } > + > + payload[1] = (host->uhs2_dev_prop.n_lss_dir_set << > + UHS2_DEV_CONFIG_N_LSS_DIR_POS) | > + host->uhs2_dev_prop.n_lss_sync_set; > + payload[0] = cpu_to_be32(payload[0]); > + payload[1] = cpu_to_be32(payload[1]); > + > + DBG("UHS2 SET PHY Settings %08x %08x\n", > + payload[0], payload[1]); > + DBG("host->flags=%08x dev_prop.speed_range_set=%x\n", > + host->flags, > + host->uhs2_dev_prop.speed_range_set); > + DBG("dev_prop.n_lss_sync_set=%x host_caps.n_lss_sync_set=%x\n", > + host->uhs2_dev_prop.n_lss_sync_set, > + host->uhs2_caps.n_lss_sync_set); > + DBG("dev_prop.n_lss_dir_set=%x host_caps.n_lss_dir_set=%x\n", > + host->uhs2_dev_prop.n_lss_dir_set, > + host->uhs2_caps.n_lss_dir_set); > + > + DBG("Begin SET_COMMON_CFG header=0x%x arg=0x%x\n", > + header, arg); > + DBG("\t\tpayload[0]=0x%x payload[1]=0x%x\n", > + payload[0], payload[1]); > + > + resp_len = 4; > + memset(resp, 0, sizeof(resp)); > + > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, > + payload, plen, resp, resp_len); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + if (!(resp[2] & 0x80)) > + break; > + > + DBG("%s: %s: UHS2 SET PHY Settings fail, res= 0x%x!\n", > + mmc_hostname(host), __func__, resp[2]); > + } > + > + DBG("SET_COMMON_CFG: LINK-TRAN Settings.\n"); > + arg = ((UHS2_DEV_CONFIG_LINK_TRAN_SET & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_8B | > + (UHS2_DEV_CONFIG_LINK_TRAN_SET >> 8); > + > + plen = 2; > + > + if (host->uhs2_dev_prop.app_type == UHS2_DEV_CONFIG_APP_SD_MEM) > + host->uhs2_dev_prop.maxblk_len_set = > + UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN; > + else > + host->uhs2_dev_prop.maxblk_len_set = > + min(host->uhs2_dev_prop.maxblk_len, > + host->uhs2_caps.maxblk_len); > + host->uhs2_caps.maxblk_len_set = host->uhs2_dev_prop.maxblk_len_set; > + > + host->uhs2_dev_prop.n_fcu_set = > + min(host->uhs2_dev_prop.n_fcu, > + host->uhs2_caps.n_fcu); > + host->uhs2_caps.n_fcu_set = host->uhs2_dev_prop.n_fcu_set; > + > + host->uhs2_dev_prop.n_data_gap_set = > + max(nMinDataGap, host->uhs2_dev_prop.n_data_gap); > + > + host->uhs2_caps.n_data_gap_set = host->uhs2_dev_prop.n_data_gap_set; > + > + host->uhs2_caps.max_retry_set = 3; > + host->uhs2_dev_prop.max_retry_set = host->uhs2_caps.max_retry_set; > + > + payload[0] = (host->uhs2_dev_prop.maxblk_len_set << > + UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) | > + (host->uhs2_dev_prop.max_retry_set << > + UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS) | > + (host->uhs2_dev_prop.n_fcu_set << > + UHS2_DEV_CONFIG_N_FCU_POS); > + payload[1] = host->uhs2_dev_prop.n_data_gap_set; > + payload[0] = cpu_to_be32(payload[0]); > + payload[1] = cpu_to_be32(payload[1]); > + > + DBG("Begin SET_COMMON_CFG header=0x%x arg=0x%x\n", header, arg); > + DBG("\t\tpayload[0]=0x%x payload[1]=0x%x\n", payload[0], payload[1]); > + > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + DBG("SET_COMMON_CFG: Set Config Completion.\n"); > + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_8B | > + (UHS2_DEV_CONFIG_GEN_SET >> 8); > + > + plen = 2; > + payload[0] = 0; > + payload[1] = UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE; > + payload[0] = cpu_to_be32(payload[0]); > + payload[1] = cpu_to_be32(payload[1]); > + > + DBG("Begin SET_COMMON_CFG, header=0x%x, arg=0x%x, payload[0] = 0x%x.\n", > + header, arg, payload[0]); > + resp_len = 5; > + memset(resp, 0, sizeof(resp)); > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, > + resp, resp_len); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + /* Set host Config Setting registers */ > + if (host->ops->uhs2_set_reg(host, SET_CONFIG)) { > + pr_err("%s: %s: UHS2 SET_CONFIG fail!\n", > + mmc_hostname(host), __func__); > + return -EIO; > + } > + > + return 0; > +} > + > +static int uhs2_go_dormant(struct mmc_host *host, bool hibernate) > +{ > + struct mmc_command cmd = {0}; > + struct uhs2_command uhs2_cmd = {}; > + u16 header = 0, arg = 0; > + u32 payload[1]; > + u8 plen = 1; > + int err; > + > + /* Disable Normal INT */ > + if (host->ops->uhs2_set_reg(host, DISABLE_INT)) { > + pr_err("%s: %s: UHS2 DISABLE_INT fail!\n", > + mmc_hostname(host), __func__); > + return -EIO; > + } > + > + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | > + host->uhs2_dev_prop.node_id; > + > + arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) | > + UHS2_NATIVE_CMD_WRITE | > + UHS2_NATIVE_CMD_PLEN_4B | > + (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8); > + > + if (hibernate) > + payload[0] = UHS2_DEV_CMD_DORMANT_HIBER; > + > + DBG("Begin GO_DORMANT_STATE, header=0x%x, arg=0x%x, payload=0x%x.\n", > + header, arg, payload[0]); > + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0); > + > + err = mmc_wait_for_cmd(host, &cmd, 0); > + if (err) { > + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + /* Check Dormant State in Present */ > + if (host->ops->uhs2_set_reg(host, CHECK_DORMANT)) { > + pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n", > + mmc_hostname(host), __func__); > + return -EIO; > + } > + > + host->ops->uhs2_disable_clk(host); > + > + return 0; > +} > + > +static int uhs2_change_speed(struct mmc_host *host) > +{ > + int err; > + > + /* Change Speed Range */ > + if (host->ops->uhs2_set_reg(host, SET_SPEED_B)) { > + pr_err("%s: %s: UHS2 SET_SPEED fail!\n", > + mmc_hostname(host), __func__); > + return -EIO; > + } > + > + err = uhs2_go_dormant(host, false); > + if (err) { > + pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n", > + mmc_hostname(host), __func__, err); > + return -EIO; > + } > + > + /* restore sd clock */ > + mdelay(5); > + host->ops->uhs2_enable_clk(host); > + > + /* Enable Normal INT */ > + if (host->ops->uhs2_set_reg(host, ENABLE_INT)) { > + pr_err("%s: %s: UHS2 ENABLE_INT fail!\n", > + mmc_hostname(host), __func__); > + return -EIO; > + } > + > + if (host->ops->uhs2_detect_init(host)) { > + pr_err("%s: %s: uhs2_detect_init() fail!\n", > + mmc_hostname(host), __func__); > + return -EIO; > + } > + > + return 0; > +} > + > +/** > + * mmc_uhs2_rescan_try_freq - select UHS2 interface > + * @host: MMC host > + * @freq: Bus speed > + * > + * 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_uhs2_rescan_try_freq(struct mmc_host *host, unsigned int freq) > +{ > + int err = -EIO; > + > + host->flags |= MMC_UHS2_SUPPORT; > + host->f_init = freq; > + > + pr_debug("%s: %s: trying to init card at %u Hz\n", > + mmc_hostname(host), __func__, host->f_init); > + > + mmc_power_up(host, 0); > + if (host->ops->uhs2_detect_init(host)) { > + pr_err("%s: fail to detect UHS2!\n", mmc_hostname(host)); > + err = UHS2_PHY_INIT_ERR; > + goto init_fail; > + } > + > + if (uhs2_dev_init(host)) { > + pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host)); > + goto init_fail; > + } > + > + if (uhs2_enum(host)) { > + pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host)); > + goto init_fail; > + } > + > + if (uhs2_config_read(host)) { > + pr_err("%s: UHS2 INQUIRY_CONFIG fail!\n", mmc_hostname(host)); > + goto init_fail; > + } > + > + if (uhs2_config_write(host)) { > + pr_err("%s: UHS2 SET_COMMON_CONFIG fail!\n", > + mmc_hostname(host)); > + goto init_fail; > + } > + > + mmc_delay(10); > + > + /* Change to Speed Range B if it is supported */ > + if (host->flags & MMC_UHS2_SPEED_B) > + if (uhs2_change_speed(host)) { > + pr_err("%s: UHS2 uhs2_change_speed() fail!\n", > + mmc_hostname(host)); > + goto init_fail; > + } > + > + host->flags |= MMC_UHS2_INITIALIZED; > + > + mmc_send_if_cond(host, host->ocr_avail); > + > + /* On market, only can some SD cards support UHS-II so only call SD > + * attach process here. > + */ Not sure I understand this comment, can you please elaborate? > + if (!(host->caps2 & MMC_CAP2_NO_SD)) { > + err = mmc_attach_sd(host); While looking at this, perhaps we should simply add a new mmc_bus_ops for UHS-II cards, rather than re-using the mmc_sd_ops. Have you considered that? There are a couple of reasons why I think it could make sense. *) The UHS-II code becomes more standalone, which seems like a good idea since there really is lots of difference compared with the legacy SD interface. **) It would enable more flexibility and thus allow for easier UHS-II optimizations. For example, it would make power management potentially easier to support, as UHS-II re-initialization doesn't seem to have much in common with legacy SD. For those parts that are still common, sharing legacy SD functions via common header files can be done, along the lines of what we do between MMC and SD already. > + if (!err) > + return 0; > + } else { > + err = -EIO; > + } > + > +init_fail: > + mmc_power_off(host); > + if (host->flags & MMC_UHS2_INITIALIZED) > + host->flags &= ~MMC_UHS2_INITIALIZED; > + host->flags &= ~MMC_UHS2_SUPPORT; > + > + return err; > +} > +EXPORT_SYMBOL_GPL(mmc_uhs2_rescan_try_freq); > diff --git a/drivers/mmc/core/uhs2.h b/drivers/mmc/core/uhs2.h > new file mode 100644 > index 000000000000..e3389d4dda3b > --- /dev/null > +++ b/drivers/mmc/core/uhs2.h > @@ -0,0 +1,20 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * driver/mmc/core/uhs2.h - UHS-II driver > + * > + * Header file for UHS-II packets, Host Controller registers and I/O > + * accessors. > + * > + * Copyright (C) 2014 Intel Corp, All Rights Reserved. > + */ > +#ifndef MMC_UHS2_H > +#define MMC_UHS2_H > + > +#include > +#include > + > +#define UHS2_PHY_INIT_ERR 1 > + > +int mmc_uhs2_rescan_try_freq(struct mmc_host *host, unsigned int freq); > + > +#endif /* MMC_UHS2_H */ > -- > 2.27.0 > Kind regards Uffe