diff mbox series

[RFC,v3.1,17/27] mmc: sdhci-uhs2: add detect_init() to detect the interface

Message ID 20201106022726.19831-18-takahiro.akashi@linaro.org
State New
Headers show
Series Add support UHS-II for GL9755 | expand

Commit Message

AKASHI Takahiro Nov. 6, 2020, 2:27 a.m. UTC
Sdhci_uhs2_do_detect_init() is a sdhci version of mmc's uhs2_detect_init
operation. After detected, 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>
---
 drivers/mmc/host/sdhci-uhs2.c | 160 ++++++++++++++++++++++++++++++++++
 1 file changed, 160 insertions(+)

Comments

Adrian Hunter Nov. 26, 2020, 8:17 a.m. UTC | #1
On 6/11/20 4:27 am, AKASHI Takahiro wrote:
> Sdhci_uhs2_do_detect_init() is a sdhci version of mmc's uhs2_detect_init

> operation. After detected, 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>

> ---

>  drivers/mmc/host/sdhci-uhs2.c | 160 ++++++++++++++++++++++++++++++++++

>  1 file changed, 160 insertions(+)

> 

> diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c

> index 637464748cc4..994dff967e85 100644

> --- a/drivers/mmc/host/sdhci-uhs2.c

> +++ b/drivers/mmc/host/sdhci-uhs2.c

> @@ -391,12 +391,172 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

>   *                                                                           *

>  \*****************************************************************************/

>  

> +static int sdhci_uhs2_interface_detect(struct sdhci_host *host)

> +{

> +	int timeout = 100;


Please comment on where timeouts / delays come from. e.g. as per spec

> +

> +	udelay(200); /* wait for 200us before check */

> +

> +	while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &

> +		SDHCI_UHS2_IF_DETECT)) {



Can be read_poll_timeout(sdhci_readl,...,host, SDHCI_PRESENT_STATE)

> +		if (timeout == 0) {

> +			pr_warn("%s: not detect UHS2 interface in 200us.\n",

> +				mmc_hostname(host->mmc));

> +			sdhci_dumpregs(host);

> +			return -EIO;

> +		}

> +		timeout--;

> +		mdelay(1);

> +	}

> +

> +	/* Enable UHS2 error interrupts */

> +	sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,

> +				  SDHCI_UHS2_ERR_INT_STATUS_MASK);

> +

> +	timeout = 150;

> +	while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &

> +		SDHCI_UHS2_LANE_SYNC)) {


Ditto

> +		if (timeout == 0) {

> +			pr_warn("%s: UHS2 Lane sync fail in 150ms.\n",

> +				mmc_hostname(host->mmc));

> +			sdhci_dumpregs(host);

> +			return -EIO;

> +		}

> +		timeout--;

> +		mdelay(1);

> +	}

> +

> +	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;

> +

> +	/*

> +	 * TODO: may add corresponding members in sdhci_host to

> +	 * keep these caps.

> +	 */

> +	caps_ptr = sdhci_readw(host, SDHCI_UHS2_HOST_CAPS_PTR);

> +	if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {

> +		pr_err("%s: SDHCI_UHS2_HOST_CAPS_PTR(%d) is wrong.\n",

> +		       mmc_hostname(mmc), caps_ptr);

> +		return -ENODEV;

> +	}

> +	caps_gen = sdhci_readl(host,

> +			       caps_ptr + SDHCI_UHS2_HOST_CAPS_GEN_OFFSET);

> +	caps_phy = sdhci_readl(host,

> +			       caps_ptr + SDHCI_UHS2_HOST_CAPS_PHY_OFFSET);

> +	caps_tran[0] = sdhci_readl(host,

> +				   caps_ptr + SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET);

> +	caps_tran[1] = sdhci_readl(host,

> +				   caps_ptr

> +					+ SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET);

> +

> +	/* General Caps */

> +	mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK;

> +	mmc->uhs2_caps.gap = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK) >>

> +			     SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT;

> +	mmc->uhs2_caps.n_lanes = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK)

> +			>> SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT;

> +	mmc->uhs2_caps.addr64 =

> +		(caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64) ? 1 : 0;

> +	mmc->uhs2_caps.card_type =

> +		(caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK) >>

> +		SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT;

> +

> +	/* PHY Caps */

> +	mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK;

> +	mmc->uhs2_caps.speed_range =

> +		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK)

> +		>> SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT;

> +	mmc->uhs2_caps.n_lss_sync =

> +		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK)

> +		>> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT;

> +	mmc->uhs2_caps.n_lss_dir =

> +		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK)

> +		>> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT;

> +	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_HOST_CAPS_TRAN_LINK_REV_MASK;

> +	mmc->uhs2_caps.n_fcu =

> +		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK)

> +		>> SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT;

> +	if (mmc->uhs2_caps.n_fcu == 0)

> +		mmc->uhs2_caps.n_fcu = 256;

> +	mmc->uhs2_caps.host_type =

> +		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK)

> +		>> SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT;

> +	mmc->uhs2_caps.maxblk_len =

> +		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK)

> +		>> SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT;

> +	mmc->uhs2_caps.n_data_gap =

> +		caps_tran[1] & SDHCI_UHS2_HOST_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);

> +	unsigned long flags;

> +	int ret = -EIO;

> +

> +	DBG("%s: begin UHS2 init.\n", __func__);

> +	spin_lock_irqsave(&host->lock, flags);

> +

> +	if (sdhci_uhs2_interface_detect(host)) {

> +		pr_warn("%s: cannot detect UHS2 interface.\n",

> +			mmc_hostname(host->mmc));

> +		goto out;

> +	}

> +

> +	if (sdhci_uhs2_init(host)) {

> +		pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));

> +		goto out;

> +	}

> +

> +	/* 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_ERR_INT_STATUS_MASK);

> +	/*

> +	 * !!! 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);

> +

> +	ret = 0;

> +out:

> +	spin_unlock_irqrestore(&host->lock, flags);

> +	return ret;

> +}

> +

>  static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)

>  {

>  	host->mmc_host_ops.start_signal_voltage_switch =

>  		sdhci_uhs2_start_signal_voltage_switch;

>  	host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;

>  

> +	if (!host->mmc_host_ops.uhs2_detect_init)

> +		host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;

> +

>  	return 0;

>  }

>  

>
AKASHI Takahiro Dec. 1, 2020, 2:25 a.m. UTC | #2
On Thu, Nov 26, 2020 at 10:17:38AM +0200, Adrian Hunter wrote:
> On 6/11/20 4:27 am, AKASHI Takahiro wrote:

> > Sdhci_uhs2_do_detect_init() is a sdhci version of mmc's uhs2_detect_init

> > operation. After detected, 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>

> > ---

> >  drivers/mmc/host/sdhci-uhs2.c | 160 ++++++++++++++++++++++++++++++++++

> >  1 file changed, 160 insertions(+)

> > 

> > diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c

> > index 637464748cc4..994dff967e85 100644

> > --- a/drivers/mmc/host/sdhci-uhs2.c

> > +++ b/drivers/mmc/host/sdhci-uhs2.c

> > @@ -391,12 +391,172 @@ void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

> >   *                                                                           *

> >  \*****************************************************************************/

> >  

> > +static int sdhci_uhs2_interface_detect(struct sdhci_host *host)

> > +{

> > +	int timeout = 100;

> 

> Please comment on where timeouts / delays come from. e.g. as per spec


It does exist in the original code, but I will try to find the source
of information.

> > +

> > +	udelay(200); /* wait for 200us before check */

> > +

> > +	while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &

> > +		SDHCI_UHS2_IF_DETECT)) {

> 

> 

> Can be read_poll_timeout(sdhci_readl,...,host, SDHCI_PRESENT_STATE)


Okay.

> > +		if (timeout == 0) {

> > +			pr_warn("%s: not detect UHS2 interface in 200us.\n",

> > +				mmc_hostname(host->mmc));

> > +			sdhci_dumpregs(host);

> > +			return -EIO;

> > +		}

> > +		timeout--;

> > +		mdelay(1);

> > +	}

> > +

> > +	/* Enable UHS2 error interrupts */

> > +	sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,

> > +				  SDHCI_UHS2_ERR_INT_STATUS_MASK);

> > +

> > +	timeout = 150;

> > +	while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &

> > +		SDHCI_UHS2_LANE_SYNC)) {

> 

> Ditto


Okay.

-Takahiro Akashi

> 

> > +		if (timeout == 0) {

> > +			pr_warn("%s: UHS2 Lane sync fail in 150ms.\n",

> > +				mmc_hostname(host->mmc));

> > +			sdhci_dumpregs(host);

> > +			return -EIO;

> > +		}

> > +		timeout--;

> > +		mdelay(1);

> > +	}

> > +

> > +	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;

> > +

> > +	/*

> > +	 * TODO: may add corresponding members in sdhci_host to

> > +	 * keep these caps.

> > +	 */

> > +	caps_ptr = sdhci_readw(host, SDHCI_UHS2_HOST_CAPS_PTR);

> > +	if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {

> > +		pr_err("%s: SDHCI_UHS2_HOST_CAPS_PTR(%d) is wrong.\n",

> > +		       mmc_hostname(mmc), caps_ptr);

> > +		return -ENODEV;

> > +	}

> > +	caps_gen = sdhci_readl(host,

> > +			       caps_ptr + SDHCI_UHS2_HOST_CAPS_GEN_OFFSET);

> > +	caps_phy = sdhci_readl(host,

> > +			       caps_ptr + SDHCI_UHS2_HOST_CAPS_PHY_OFFSET);

> > +	caps_tran[0] = sdhci_readl(host,

> > +				   caps_ptr + SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET);

> > +	caps_tran[1] = sdhci_readl(host,

> > +				   caps_ptr

> > +					+ SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET);

> > +

> > +	/* General Caps */

> > +	mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK;

> > +	mmc->uhs2_caps.gap = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK) >>

> > +			     SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT;

> > +	mmc->uhs2_caps.n_lanes = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK)

> > +			>> SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT;

> > +	mmc->uhs2_caps.addr64 =

> > +		(caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64) ? 1 : 0;

> > +	mmc->uhs2_caps.card_type =

> > +		(caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK) >>

> > +		SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT;

> > +

> > +	/* PHY Caps */

> > +	mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK;

> > +	mmc->uhs2_caps.speed_range =

> > +		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK)

> > +		>> SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT;

> > +	mmc->uhs2_caps.n_lss_sync =

> > +		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK)

> > +		>> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT;

> > +	mmc->uhs2_caps.n_lss_dir =

> > +		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK)

> > +		>> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT;

> > +	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_HOST_CAPS_TRAN_LINK_REV_MASK;

> > +	mmc->uhs2_caps.n_fcu =

> > +		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK)

> > +		>> SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT;

> > +	if (mmc->uhs2_caps.n_fcu == 0)

> > +		mmc->uhs2_caps.n_fcu = 256;

> > +	mmc->uhs2_caps.host_type =

> > +		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK)

> > +		>> SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT;

> > +	mmc->uhs2_caps.maxblk_len =

> > +		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK)

> > +		>> SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT;

> > +	mmc->uhs2_caps.n_data_gap =

> > +		caps_tran[1] & SDHCI_UHS2_HOST_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);

> > +	unsigned long flags;

> > +	int ret = -EIO;

> > +

> > +	DBG("%s: begin UHS2 init.\n", __func__);

> > +	spin_lock_irqsave(&host->lock, flags);

> > +

> > +	if (sdhci_uhs2_interface_detect(host)) {

> > +		pr_warn("%s: cannot detect UHS2 interface.\n",

> > +			mmc_hostname(host->mmc));

> > +		goto out;

> > +	}

> > +

> > +	if (sdhci_uhs2_init(host)) {

> > +		pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));

> > +		goto out;

> > +	}

> > +

> > +	/* 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_ERR_INT_STATUS_MASK);

> > +	/*

> > +	 * !!! 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);

> > +

> > +	ret = 0;

> > +out:

> > +	spin_unlock_irqrestore(&host->lock, flags);

> > +	return ret;

> > +}

> > +

> >  static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)

> >  {

> >  	host->mmc_host_ops.start_signal_voltage_switch =

> >  		sdhci_uhs2_start_signal_voltage_switch;

> >  	host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;

> >  

> > +	if (!host->mmc_host_ops.uhs2_detect_init)

> > +		host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;

> > +

> >  	return 0;

> >  }

> >  

> > 

>
diff mbox series

Patch

diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 637464748cc4..994dff967e85 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -391,12 +391,172 @@  void sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
  *                                                                           *
 \*****************************************************************************/
 
+static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
+{
+	int timeout = 100;
+
+	udelay(200); /* wait for 200us before check */
+
+	while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+		SDHCI_UHS2_IF_DETECT)) {
+		if (timeout == 0) {
+			pr_warn("%s: not detect UHS2 interface in 200us.\n",
+				mmc_hostname(host->mmc));
+			sdhci_dumpregs(host);
+			return -EIO;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	/* Enable UHS2 error interrupts */
+	sdhci_uhs2_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+				  SDHCI_UHS2_ERR_INT_STATUS_MASK);
+
+	timeout = 150;
+	while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+		SDHCI_UHS2_LANE_SYNC)) {
+		if (timeout == 0) {
+			pr_warn("%s: UHS2 Lane sync fail in 150ms.\n",
+				mmc_hostname(host->mmc));
+			sdhci_dumpregs(host);
+			return -EIO;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	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;
+
+	/*
+	 * TODO: may add corresponding members in sdhci_host to
+	 * keep these caps.
+	 */
+	caps_ptr = sdhci_readw(host, SDHCI_UHS2_HOST_CAPS_PTR);
+	if (caps_ptr < 0x100 || caps_ptr > 0x1FF) {
+		pr_err("%s: SDHCI_UHS2_HOST_CAPS_PTR(%d) is wrong.\n",
+		       mmc_hostname(mmc), caps_ptr);
+		return -ENODEV;
+	}
+	caps_gen = sdhci_readl(host,
+			       caps_ptr + SDHCI_UHS2_HOST_CAPS_GEN_OFFSET);
+	caps_phy = sdhci_readl(host,
+			       caps_ptr + SDHCI_UHS2_HOST_CAPS_PHY_OFFSET);
+	caps_tran[0] = sdhci_readl(host,
+				   caps_ptr + SDHCI_UHS2_HOST_CAPS_TRAN_OFFSET);
+	caps_tran[1] = sdhci_readl(host,
+				   caps_ptr
+					+ SDHCI_UHS2_HOST_CAPS_TRAN_1_OFFSET);
+
+	/* General Caps */
+	mmc->uhs2_caps.dap = caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DAP_MASK;
+	mmc->uhs2_caps.gap = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_GAP_MASK) >>
+			     SDHCI_UHS2_HOST_CAPS_GEN_GAP_SHIFT;
+	mmc->uhs2_caps.n_lanes = (caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_LANE_MASK)
+			>> SDHCI_UHS2_HOST_CAPS_GEN_LANE_SHIFT;
+	mmc->uhs2_caps.addr64 =
+		(caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_ADDR_64) ? 1 : 0;
+	mmc->uhs2_caps.card_type =
+		(caps_gen & SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_MASK) >>
+		SDHCI_UHS2_HOST_CAPS_GEN_DEV_TYPE_SHIFT;
+
+	/* PHY Caps */
+	mmc->uhs2_caps.phy_rev = caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_REV_MASK;
+	mmc->uhs2_caps.speed_range =
+		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_RANGE_MASK)
+		>> SDHCI_UHS2_HOST_CAPS_PHY_RANGE_SHIFT;
+	mmc->uhs2_caps.n_lss_sync =
+		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_MASK)
+		>> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_SYN_SHIFT;
+	mmc->uhs2_caps.n_lss_dir =
+		(caps_phy & SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_MASK)
+		>> SDHCI_UHS2_HOST_CAPS_PHY_N_LSS_DIR_SHIFT;
+	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_HOST_CAPS_TRAN_LINK_REV_MASK;
+	mmc->uhs2_caps.n_fcu =
+		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_MASK)
+		>> SDHCI_UHS2_HOST_CAPS_TRAN_N_FCU_SHIFT;
+	if (mmc->uhs2_caps.n_fcu == 0)
+		mmc->uhs2_caps.n_fcu = 256;
+	mmc->uhs2_caps.host_type =
+		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_MASK)
+		>> SDHCI_UHS2_HOST_CAPS_TRAN_HOST_TYPE_SHIFT;
+	mmc->uhs2_caps.maxblk_len =
+		(caps_tran[0] & SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_MASK)
+		>> SDHCI_UHS2_HOST_CAPS_TRAN_BLK_LEN_SHIFT;
+	mmc->uhs2_caps.n_data_gap =
+		caps_tran[1] & SDHCI_UHS2_HOST_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);
+	unsigned long flags;
+	int ret = -EIO;
+
+	DBG("%s: begin UHS2 init.\n", __func__);
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (sdhci_uhs2_interface_detect(host)) {
+		pr_warn("%s: cannot detect UHS2 interface.\n",
+			mmc_hostname(host->mmc));
+		goto out;
+	}
+
+	if (sdhci_uhs2_init(host)) {
+		pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
+		goto out;
+	}
+
+	/* 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_ERR_INT_STATUS_MASK);
+	/*
+	 * !!! 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);
+
+	ret = 0;
+out:
+	spin_unlock_irqrestore(&host->lock, flags);
+	return ret;
+}
+
 static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
 {
 	host->mmc_host_ops.start_signal_voltage_switch =
 		sdhci_uhs2_start_signal_voltage_switch;
 	host->mmc_host_ops.set_ios = sdhci_uhs2_set_ios;
 
+	if (!host->mmc_host_ops.uhs2_detect_init)
+		host->mmc_host_ops.uhs2_detect_init = sdhci_uhs2_do_detect_init;
+
 	return 0;
 }