diff mbox

[v3,3/4] mmc: sh_mobile_sdhi: Add tuning support

Message ID 1464057797-29951-4-git-send-email-horms+renesas@verge.net.au (mailing list archive)
State New, archived
Headers show

Commit Message

Simon Horman May 24, 2016, 2:43 a.m. UTC
From: Ai Kyuse <ai.kyuse.uw@renesas.com>

Add tuning support for use with SDR104 mode
This includes adding support for the sampling clock controller (SCC).

Signed-off-by: Ai Kyuse <ai.kyuse.uw@renesas.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
v3 [Simon Horman]
* As suggested by Kuninori Morimoto:
  - Do not add unused retuning callback to struct tmio_mmc_host
  - Change return type of prepare_tuning callback to void
  - Add tap_size parameter to select_tuning callback

v2 [Simon Horman]
* As suggested by Kuninori Morimoto
  - Use host->mmc->caps & MMC_CAP_UHS_SDR104 instead of
    pdata->flags & TMIO_MMC_HAS_UHS_SCC to avoid needing the
    MMC_CAP_UHS_SDR104 flag at all.
    N.B: Morimoto-san suggested using but this flag is not actually
    set there in by current probe come.
  - Simplify logic in sh_mobile_sdhi_inquiry_tuning
* As suggested by Wolfram Sang
  - Use clk_rate instead of clk for field in struct sh_mobile_sdhi_scc
  - Remove inquiry_tuning callback which is now unnecessary as calling
    of tuning is handled by the core
  - Removed unused sh_mobile_sdhi_set_clk_div callback
  - Save sci_base address rather than calculating it on each read and write
* Update selection logic in sh_mobile_sdhi_select_tuning to match spec
* Use bool instead of long for taps parameter of
  sh_mobile_sdhi_select_tuning()
* Return 0 from sh_mobile_sdhi_init_tuning() if the
  SDR104 capability is not set and thus tuning should not
  be performed because it is not supported by the hardware

v1 [Simon Horman]
* Rebase
* Always use value of 0x8 for TAPNUM field of DTCNTL register
  rather than reading value from DT property. There does not
  seem to be a need to expose this in DT at this point.
* Do not include tmio_mmc_start_signal_voltage_switch changes which
  are already in mainline in a different form
* Do not add renesas,clk-rate property as the max-frequency property, which
  is now present in mainline, seems to provide the needed rate
* Omit Gen3 specific changes
* Do not provide renesas,mmc-scc-tappos DT property.
  Instead, always use taps provided in driver.
* Do not parse sd-uhs-sdr50 and sd-uhs-sdr104 properties.
  This is handled by the core.

v0 [Ai Kyuse]
---
 drivers/mmc/host/sh_mobile_sdhi.c | 203 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 202 insertions(+), 1 deletion(-)

Comments

Wolfram Sang May 27, 2016, 11:59 a.m. UTC | #1
On Tue, May 24, 2016 at 11:43:16AM +0900, Simon Horman wrote:
> From: Ai Kyuse <ai.kyuse.uw@renesas.com>

I wonder if you shouldn't take over ownership of this and the previous
patch? You changed quite a lot.

> +static inline u32 sd_scc_read32(struct tmio_mmc_host *host, int addr)
> +{
> +	return readl(host_to_priv(host)->scc_ctl + (addr << host->bus_shift));
> +}

What about passing 'priv' to these functions? Then we can save the
host_to_priv for each access.

> +
> +static inline void sd_scc_write32(struct tmio_mmc_host *host, int addr, u32 val)
> +{
> +	writel(val, host_to_priv(host)->scc_ctl + (addr << host->bus_shift));
> +}

Ditto.

> +
> +static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host)
> +{
> +	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
> +		return 0;

Will the core call us if MMC_CAP_UHS_SDR104 was not set?


> +
> +	/* set sampling clock selection range */
> +	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DTCNTL,
> +			0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
> +
> +	/* Initialize SCC */
> +	sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x00000000);

..., CTL_STATUS, 0);

?

> +
> +	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DTCNTL,
> +		SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
> +		sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL));
> +
> +	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
> +		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));

'CLK_CTL_SCLKEN' instead of 0x100?

> +
> +	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_CKSEL,
> +		SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
> +		sd_scc_read32(host, SH_MOBILE_SDHI_SCC_CKSEL));
> +
> +	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
> +		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
> +
> +	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
> +		~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
> +		sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
> +
> +	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DT2FF, host->scc_tappos);
> +
> +	/* Read TAPNUM */
> +	return (sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL) >>
> +		SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) &
> +		SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK;
> +}
> +
> +static void sh_mobile_sdhi_prepare_tuning(struct tmio_mmc_host *host,
> +					 unsigned long tap)
> +{
> +	/* Set sampling clock position */
> +	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_TAPSET, tap);
> +}
> +
> +#define SH_MOBILE_SDHI_MAX_TAP	3

unused

> +
> +static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host,
> +					bool *tap, int tap_size)
> +{
> +	unsigned long tap_num, i;
> +	int ok_count;
> +
> +	/* Clear SCC_RVSREQ */
> +	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
> +
> +	/* Select SCC */
> +	tap_num = (sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL) >>
> +		   SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) &
> +		SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK;
> +
> +	if (tap_num * 2 != tap_size)
> +		return -EINVAL;
> +
> +	/*
> +	 * Select clock where three consecutive bock reads succeeded.
> +	 *
> +	 * There may be multiple occurrences of three successive reads
> +	 * and selecting any of them is correct. Here the first one is
> +	 * selected.
> +	 */
> +	ok_count = 0;
> +	for (i = 0; i < tap_size; i++) {
> +		if (tap[i])
> +			ok_count++;
> +		else
> +			ok_count = 0;

ok_count = tap[i] ? ok_count + 1 : 0;

? Yes, I do like the ternary operator :D

...

> +	if (host->mmc->caps & MMC_CAP_UHS_SDR104) {
> +		/* Reset SCC */
> +		sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
> +			sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));

'CLK_CTL_SCLKEN' instead of 0x100?

> +
> +		sd_scc_write32(host, SH_MOBILE_SDHI_SCC_CKSEL,
> +			~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL &
> +			sd_scc_read32(host, SH_MOBILE_SDHI_SCC_CKSEL));
> +
> +		sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
> +			sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));

Ditto.

...

> +		if (!hit)
> +			dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104 and HS200\n");

HS200 will come later, I think (although the path should be easy now).

Thanks, I think we are quite close. Maybe Ulf does have some high level
comments?
Geert Uytterhoeven May 31, 2016, 10:39 a.m. UTC | #2
Hi Simon,

On Tue, May 24, 2016 at 4:43 AM, Simon Horman
<horms+renesas@verge.net.au> wrote:
> --- a/drivers/mmc/host/sh_mobile_sdhi.c
> +++ b/drivers/mmc/host/sh_mobile_sdhi.c

> @@ -403,6 +580,30 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
>         if (ret < 0)
>                 goto efree;
>
> +       if (host->mmc->caps & MMC_CAP_UHS_SDR104)
> +               host->mmc->caps |= MMC_CAP_HW_RESET;
> +
> +       if (of_id && of_id->data) {
> +               const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
> +               const struct sh_mobile_sdhi_scc *taps = of_data->taps;
> +               bool hit = false;
> +
> +               for (i = 0; i < of_data->taps_num; i++) {
> +                       if (taps[i].clk_rate == 0 ||
> +                           taps[i].clk_rate == host->mmc->f_max) {
> +                               host->scc_tappos = taps->tap;
> +                               hit = true;
> +                               break;
> +                       }
> +               }
> +
> +               if (!hit)
> +                       dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104 and HS200\n");

This warning triggers on sh7a0/kzm9g, r8a73a4/ape6evm, and
r8a7740/armadillo.

Perhaps the tap code should check if MMC_CAP_UHS_SDR104 is
enabled?

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
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Simon Horman June 6, 2016, 1:02 a.m. UTC | #3
On Tue, May 31, 2016 at 12:39:58PM +0200, Geert Uytterhoeven wrote:
> Hi Simon,
> 
> On Tue, May 24, 2016 at 4:43 AM, Simon Horman
> <horms+renesas@verge.net.au> wrote:
> > --- a/drivers/mmc/host/sh_mobile_sdhi.c
> > +++ b/drivers/mmc/host/sh_mobile_sdhi.c
> 
> > @@ -403,6 +580,30 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
> >         if (ret < 0)
> >                 goto efree;
> >
> > +       if (host->mmc->caps & MMC_CAP_UHS_SDR104)
> > +               host->mmc->caps |= MMC_CAP_HW_RESET;
> > +
> > +       if (of_id && of_id->data) {
> > +               const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
> > +               const struct sh_mobile_sdhi_scc *taps = of_data->taps;
> > +               bool hit = false;
> > +
> > +               for (i = 0; i < of_data->taps_num; i++) {
> > +                       if (taps[i].clk_rate == 0 ||
> > +                           taps[i].clk_rate == host->mmc->f_max) {
> > +                               host->scc_tappos = taps->tap;
> > +                               hit = true;
> > +                               break;
> > +                       }
> > +               }
> > +
> > +               if (!hit)
> > +                       dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104 and HS200\n");
> 
> This warning triggers on sh7a0/kzm9g, r8a73a4/ape6evm, and
> r8a7740/armadillo.
> 
> Perhaps the tap code should check if MMC_CAP_UHS_SDR104 is
> enabled?

Thanks. Yes, I think that would be sensible.
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Simon Horman July 20, 2016, 6:24 a.m. UTC | #4
On Fri, May 27, 2016 at 01:59:34PM +0200, Wolfram Sang wrote:
> On Tue, May 24, 2016 at 11:43:16AM +0900, Simon Horman wrote:
> > From: Ai Kyuse <ai.kyuse.uw@renesas.com>
> 
> I wonder if you shouldn't take over ownership of this and the previous
> patch? You changed quite a lot.

Sure, will do.

[snip]

> > +
> > +static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host)
> > +{
> > +	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
> > +		return 0;
> 
> Will the core call us if MMC_CAP_UHS_SDR104 was not set?

Empirically, yes, that appears to be the case.


For all your other suggestions: thanks, I will update things as you suggest.

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 5309c73be1f0..3ad52de45b89 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -41,6 +41,11 @@ 
 
 #define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
 
+struct sh_mobile_sdhi_scc {
+	unsigned long clk_rate;	/* clock rate for SDR104 */
+	u32 tap;		/* sampling clock position for SDR104 */
+};
+
 struct sh_mobile_sdhi_of_data {
 	unsigned long tmio_flags;
 	unsigned long capabilities;
@@ -48,6 +53,9 @@  struct sh_mobile_sdhi_of_data {
 	enum dma_slave_buswidth dma_buswidth;
 	dma_addr_t dma_rx_offset;
 	unsigned bus_shift;
+	int scc_offset;
+	struct sh_mobile_sdhi_scc *taps;
+	int taps_num;
 };
 
 static const struct sh_mobile_sdhi_of_data of_default_cfg = {
@@ -60,12 +68,27 @@  static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
 	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
 };
 
+/* Definitions for sampling clocks */
+static struct sh_mobile_sdhi_scc rcar_gen2_scc_taps[] = {
+	{
+		.clk_rate = 156000000,
+		.tap = 0x00000703,
+	},
+	{
+		.clk_rate = 0,
+		.tap = 0x00000300,
+	},
+};
+
 static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
 	.tmio_flags	= TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
 			  TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2,
 	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
 	.dma_buswidth	= DMA_SLAVE_BUSWIDTH_4_BYTES,
 	.dma_rx_offset	= 0x2000,
+	.scc_offset	= 0x0300,
+	.taps		= rcar_gen2_scc_taps,
+	.taps_num	= ARRAY_SIZE(rcar_gen2_scc_taps),
 };
 
 static const struct sh_mobile_sdhi_of_data of_rcar_gen3_compatible = {
@@ -98,6 +121,7 @@  struct sh_mobile_sdhi {
 	struct tmio_mmc_dma dma_priv;
 	struct pinctrl *pinctrl;
 	struct pinctrl_state *pins_default, *pins_uhs;
+	void __iomem *scc_ctl;
 };
 
 static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
@@ -241,6 +265,155 @@  static int sh_mobile_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
 	return pinctrl_select_state(priv->pinctrl, pin_state);
 }
 
+/* SCC registers */
+#define SH_MOBILE_SDHI_SCC_DTCNTL	0x000
+#define SH_MOBILE_SDHI_SCC_TAPSET	0x002
+#define SH_MOBILE_SDHI_SCC_DT2FF	0x004
+#define SH_MOBILE_SDHI_SCC_CKSEL	0x006
+#define SH_MOBILE_SDHI_SCC_RVSCNTL	0x008
+#define SH_MOBILE_SDHI_SCC_RVSREQ	0x00A
+
+/* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */
+#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN		BIT(0)
+#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT	16
+#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK	0xff
+
+/* Definitions for values the SH_MOBILE_SDHI_SCC_CKSEL register */
+#define SH_MOBILE_SDHI_SCC_CKSEL_DTSEL		BIT(0)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSCNTL register */
+#define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN	BIT(1)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */
+#define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR	BIT(2)
+
+static inline u32 sd_scc_read32(struct tmio_mmc_host *host, int addr)
+{
+	return readl(host_to_priv(host)->scc_ctl + (addr << host->bus_shift));
+}
+
+static inline void sd_scc_write32(struct tmio_mmc_host *host, int addr, u32 val)
+{
+	writel(val, host_to_priv(host)->scc_ctl + (addr << host->bus_shift));
+}
+
+static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host)
+{
+	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
+		return 0;
+
+	/* set sampling clock selection range */
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DTCNTL,
+			0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
+
+	/* Initialize SCC */
+	sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x00000000);
+
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DTCNTL,
+		SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
+		sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL));
+
+	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_CKSEL,
+		SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
+		sd_scc_read32(host, SH_MOBILE_SDHI_SCC_CKSEL));
+
+	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+		~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+		sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DT2FF, host->scc_tappos);
+
+	/* Read TAPNUM */
+	return (sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL) >>
+		SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) &
+		SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK;
+}
+
+static void sh_mobile_sdhi_prepare_tuning(struct tmio_mmc_host *host,
+					 unsigned long tap)
+{
+	/* Set sampling clock position */
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_TAPSET, tap);
+}
+
+#define SH_MOBILE_SDHI_MAX_TAP	3
+
+static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host,
+					bool *tap, int tap_size)
+{
+	unsigned long tap_num, i;
+	int ok_count;
+
+	/* Clear SCC_RVSREQ */
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
+
+	/* Select SCC */
+	tap_num = (sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL) >>
+		   SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) &
+		SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK;
+
+	if (tap_num * 2 != tap_size)
+		return -EINVAL;
+
+	/*
+	 * Select clock where three consecutive bock reads succeeded.
+	 *
+	 * There may be multiple occurrences of three successive reads
+	 * and selecting any of them is correct. Here the first one is
+	 * selected.
+	 */
+	ok_count = 0;
+	for (i = 0; i < tap_size; i++) {
+		if (tap[i])
+			ok_count++;
+		else
+			ok_count = 0;
+		if (ok_count == 3)
+			break;
+	}
+
+	if (ok_count != 3)
+		return -EIO;
+
+	/* Set SCC */
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_TAPSET, i);
+
+	/* Enable auto re-tuning */
+	sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+		SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN |
+		sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+	return 0;
+}
+
+static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host)
+{
+	if (host->mmc->caps & MMC_CAP_UHS_SDR104) {
+		/* Reset SCC */
+		sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+			sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+		sd_scc_write32(host, SH_MOBILE_SDHI_SCC_CKSEL,
+			~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL &
+			sd_scc_read32(host, SH_MOBILE_SDHI_SCC_CKSEL));
+
+		sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+			sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+		sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+			~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+			sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+		sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+			~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+			sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+	}
+}
+
 static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
 {
 	int timeout = 1000;
@@ -311,7 +484,7 @@  static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 	struct tmio_mmc_data *mmd = pdev->dev.platform_data;
 	struct tmio_mmc_host *host;
 	struct resource *res;
-	int irq, ret, i = 0;
+	int irq, ret, i;
 	struct tmio_mmc_dma *dma_priv;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -364,6 +537,10 @@  static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 	host->clk_disable	= sh_mobile_sdhi_clk_disable;
 	host->multi_io_quirk	= sh_mobile_sdhi_multi_io_quirk;
 	host->start_signal_voltage_switch = sh_mobile_sdhi_start_signal_voltage_switch;
+	host->init_tuning	= sh_mobile_sdhi_init_tuning;
+	host->prepare_tuning	= sh_mobile_sdhi_prepare_tuning;
+	host->select_tuning	= sh_mobile_sdhi_select_tuning;
+	host->hw_reset		= sh_mobile_sdhi_hw_reset;
 
 	/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
 	if (!host->bus_shift && resource_size(res) > 0x100) /* old way to determine the shift */
@@ -403,6 +580,30 @@  static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto efree;
 
+	if (host->mmc->caps & MMC_CAP_UHS_SDR104)
+		host->mmc->caps |= MMC_CAP_HW_RESET;
+
+	if (of_id && of_id->data) {
+		const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
+		const struct sh_mobile_sdhi_scc *taps = of_data->taps;
+		bool hit = false;
+
+		for (i = 0; i < of_data->taps_num; i++) {
+			if (taps[i].clk_rate == 0 ||
+			    taps[i].clk_rate == host->mmc->f_max) {
+				host->scc_tappos = taps->tap;
+				hit = true;
+				break;
+			}
+		}
+
+		if (!hit)
+			dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104 and HS200\n");
+
+		priv->scc_ctl = host->ctl + of_data->scc_offset;
+	}
+
+	i = 0;
 	while (1) {
 		irq = platform_get_irq(pdev, i);
 		if (irq < 0)