diff mbox series

[4/7] mmc: renesas_sdhi: Add support for RZ/G3E SoC

Message ID 20250126134616.37334-5-biju.das.jz@bp.renesas.com (mailing list archive)
State New
Headers show
Series Add RZ/G3E SDHI support | expand

Commit Message

Biju Das Jan. 26, 2025, 1:46 p.m. UTC
The SDHI/eMMC IPs in the RZ/G3E SoC are similar to those in R-Car Gen3.
However, the RZ/G3E SD0 channel has Voltage level control and PWEN pin
support via SD_STATUS register.

internal regulator support is added to control the voltage levels of
the SD pins via sd_iovs/sd_pwen bits in SD_STATUS register by populating
vqmmc-regulator child node.

SD1 and SD2 channels have gpio regulator support and internal regulator
support. Selection of the regulator is based on the regulator phandle.

Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 drivers/mmc/host/renesas_sdhi.h      |   1 +
 drivers/mmc/host/renesas_sdhi_core.c | 129 +++++++++++++++++++++++++++
 drivers/mmc/host/tmio_mmc.h          |   5 ++
 3 files changed, 135 insertions(+)

Comments

Tommaso Merciai Jan. 26, 2025, 7 p.m. UTC | #1
On Sun, Jan 26, 2025 at 01:46:06PM +0000, Biju Das wrote:
> The SDHI/eMMC IPs in the RZ/G3E SoC are similar to those in R-Car Gen3.
> However, the RZ/G3E SD0 channel has Voltage level control and PWEN pin
> support via SD_STATUS register.
> 
> internal regulator support is added to control the voltage levels of
> the SD pins via sd_iovs/sd_pwen bits in SD_STATUS register by populating
> vqmmc-regulator child node.
> 
> SD1 and SD2 channels have gpio regulator support and internal regulator
> support. Selection of the regulator is based on the regulator phandle.
> 
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
>  drivers/mmc/host/renesas_sdhi.h      |   1 +
>  drivers/mmc/host/renesas_sdhi_core.c | 129 +++++++++++++++++++++++++++
>  drivers/mmc/host/tmio_mmc.h          |   5 ++
>  3 files changed, 135 insertions(+)
> 
> diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
> index f12a87442338..291ddb4ad9be 100644
> --- a/drivers/mmc/host/renesas_sdhi.h
> +++ b/drivers/mmc/host/renesas_sdhi.h
> @@ -95,6 +95,7 @@ struct renesas_sdhi {
>  
>  	struct reset_control *rstc;
>  	struct tmio_mmc_host *host;
> +	struct regulator_dev *rdev;
>  };
>  
>  #define host_to_priv(host) \
> diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
> index 6ea651409774..bbfa68ca682b 100644
> --- a/drivers/mmc/host/renesas_sdhi_core.c
> +++ b/drivers/mmc/host/renesas_sdhi_core.c
> @@ -32,6 +32,8 @@
>  #include <linux/platform_device.h>
>  #include <linux/pm_domain.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/regulator/driver.h>
> +#include <linux/regulator/of_regulator.h>
>  #include <linux/reset.h>
>  #include <linux/sh_dma.h>
>  #include <linux/slab.h>
> @@ -581,12 +583,24 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
>  
>  	if (!preserve) {
>  		if (priv->rstc) {
> +			u32 sd_status;
> +			/*
> +			 * HW reset might have toggled the regulator state in
> +			 * HW which regulator core might be unaware of so save
> +			 * and restore the regulator state during HW reset.
> +			 */
> +			if (priv->rdev)
> +				sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
> +
>  			reset_control_reset(priv->rstc);
>  			/* Unknown why but without polling reset status, it will hang */
>  			read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
>  					  false, priv->rstc);
>  			/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
>  			sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
> +			if (priv->rdev)
> +				sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
> +
>  			priv->needs_adjust_hs400 = false;
>  			renesas_sdhi_set_clock(host, host->clk_cache);
>  
> @@ -904,15 +918,113 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
>  	renesas_sdhi_sdbuf_width(host, enable ? width : 16);
>  }
>  
> +static const unsigned int renesas_sdhi_vqmmc_voltages[] = {
> +	3300000, 1800000
> +};
> +
> +static int renesas_sdhi_regulator_disable(struct regulator_dev *rdev)
> +{
> +	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
> +	u32 sd_status;
> +
> +	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
> +	sd_status &= ~SD_STATUS_PWEN;
> +	sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
> +
> +	return 0;
> +}
> +
> +static int renesas_sdhi_regulator_enable(struct regulator_dev *rdev)
> +{
> +	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
> +	u32 sd_status;
> +
> +	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
> +	sd_status |= SD_STATUS_PWEN;
> +	sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
> +
> +	return 0;
> +}
> +
> +static int renesas_sdhi_regulator_is_enabled(struct regulator_dev *rdev)
> +{
> +	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
> +	u32 sd_status;
> +
> +	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
> +
> +	return (sd_status & SD_STATUS_PWEN) ? 1 : 0;
> +}
> +
> +static int renesas_sdhi_regulator_get_voltage(struct regulator_dev *rdev)
> +{
> +	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
> +	u32 sd_status;
> +
> +	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
> +
> +	return (sd_status & SD_STATUS_IOVS) ? 1800000 : 3300000;
> +}
> +
> +static int renesas_sdhi_regulator_set_voltage(struct regulator_dev *rdev,
> +					      int min_uV, int max_uV,
> +					      unsigned int *selector)
> +{
> +	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
> +	u32 sd_status;
> +
> +	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
> +	if (min_uV >= 1700000 && max_uV <= 1950000) {
> +		sd_status |= SD_STATUS_IOVS;
> +		*selector = 1;
> +	} else {
> +		sd_status &= ~SD_STATUS_IOVS;
> +		*selector = 0;
> +	}
> +	sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
> +
> +	return 0;
> +}
> +
> +static int renesas_sdhi_regulator_list_voltage(struct regulator_dev *rdev,
> +					       unsigned int selector)
> +{
> +	if (selector >= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages))
> +		return -EINVAL;
> +
> +	return renesas_sdhi_vqmmc_voltages[selector];
> +}
> +
> +static const struct regulator_ops renesas_sdhi_regulator_voltage_ops = {
> +	.enable = renesas_sdhi_regulator_enable,
> +	.disable = renesas_sdhi_regulator_disable,
> +	.is_enabled = renesas_sdhi_regulator_is_enabled,
> +	.list_voltage = renesas_sdhi_regulator_list_voltage,
> +	.get_voltage = renesas_sdhi_regulator_get_voltage,
> +	.set_voltage = renesas_sdhi_regulator_set_voltage,
> +};
> +
> +static struct regulator_desc renesas_sdhi_vqmmc_regulator = {
> +	.of_match	= of_match_ptr("vqmmc-regulator"),
> +	.owner		= THIS_MODULE,
> +	.type		= REGULATOR_VOLTAGE,
> +	.ops		= &renesas_sdhi_regulator_voltage_ops,
> +	.volt_table	= renesas_sdhi_vqmmc_voltages,
> +	.n_voltages	= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages),
> +};
> +
>  int renesas_sdhi_probe(struct platform_device *pdev,
>  		       const struct tmio_mmc_dma_ops *dma_ops,
>  		       const struct renesas_sdhi_of_data *of_data,
>  		       const struct renesas_sdhi_quirks *quirks)
>  {
> +	struct regulator_config rcfg = { .dev = &pdev->dev, };
>  	struct tmio_mmc_data *mmd = pdev->dev.platform_data;
>  	struct renesas_sdhi_dma *dma_priv;
> +	struct device *dev = &pdev->dev;
>  	struct tmio_mmc_data *mmc_data;
>  	struct tmio_mmc_host *host;
> +	struct regulator_dev *rdev;
>  	struct renesas_sdhi *priv;
>  	int num_irqs, irq, ret, i;
>  	struct resource *res;
> @@ -1053,6 +1165,23 @@ int renesas_sdhi_probe(struct platform_device *pdev,
>  	if (ret)
>  		goto efree;
>  
> +	rcfg.of_node = of_get_child_by_name(dev->of_node, "vqmmc-regulator");
> +	if (rcfg.of_node) {
> +		rcfg.driver_data = priv->host;
> +
> +		renesas_sdhi_vqmmc_regulator.name = "sdhi-vqmmc-regulator";
> +		renesas_sdhi_vqmmc_regulator.of_match = of_match_ptr("vqmmc-regulator");
> +		renesas_sdhi_vqmmc_regulator.type = REGULATOR_VOLTAGE;
> +		renesas_sdhi_vqmmc_regulator.owner = THIS_MODULE;
> +		rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg);
> +		of_node_put(rcfg.of_node);
> +		if (IS_ERR(rdev)) {
> +			dev_err(dev, "regulator register failed err=%ld", PTR_ERR(rdev));
> +			goto efree;
> +		}
> +		priv->rdev = rdev;
> +	}
> +
>  	ver = sd_ctrl_read16(host, CTL_VERSION);
>  	/* GEN2_SDR104 is first known SDHI to use 32bit block count */
>  	if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
> diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
> index a75755f31d31..5970ca598850 100644
> --- a/drivers/mmc/host/tmio_mmc.h
> +++ b/drivers/mmc/host/tmio_mmc.h
> @@ -44,6 +44,7 @@
>  #define CTL_RESET_SD 0xe0
>  #define CTL_VERSION 0xe2
>  #define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */
> +#define CTL_SD_STATUS 0xf2 /* only known on RZ/{G2L,G3E,V2H} */
>  
>  /* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */
>  #define TMIO_STOP_STP		BIT(0)
> @@ -103,6 +104,10 @@
>  /* Definitions for values the CTL_SDIF_MODE register can take */
>  #define SDIF_MODE_HS400		BIT(0) /* only known on R-Car 2+ */
>  
> +/* Definitions for values the CTL_SD_STATUS register can take */
> +#define SD_STATUS_PWEN		BIT(0) /* only known on RZ/{G3E,V2H} */
> +#define SD_STATUS_IOVS		BIT(16) /* only known on RZ/{G3E,V2H} */
> +
>  /* Define some IRQ masks */
>  /* This is the mask used at reset by the chip */
>  #define TMIO_MASK_ALL           0x837f031d
> -- 
> 2.43.0
>

Reviewed-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Geert Uytterhoeven Jan. 28, 2025, 1:32 p.m. UTC | #2
Hi Biju,

On Sun, 26 Jan 2025 at 14:46, Biju Das <biju.das.jz@bp.renesas.com> wrote:
> The SDHI/eMMC IPs in the RZ/G3E SoC are similar to those in R-Car Gen3.
> However, the RZ/G3E SD0 channel has Voltage level control and PWEN pin
> support via SD_STATUS register.
>
> internal regulator support is added to control the voltage levels of
> the SD pins via sd_iovs/sd_pwen bits in SD_STATUS register by populating
> vqmmc-regulator child node.
>
> SD1 and SD2 channels have gpio regulator support and internal regulator
> support. Selection of the regulator is based on the regulator phandle.
>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>

Thanks for your patch!

> --- a/drivers/mmc/host/renesas_sdhi_core.c
> +++ b/drivers/mmc/host/renesas_sdhi_core.c

> @@ -1053,6 +1165,23 @@ int renesas_sdhi_probe(struct platform_device *pdev,
>         if (ret)
>                 goto efree;
>
> +       rcfg.of_node = of_get_child_by_name(dev->of_node, "vqmmc-regulator");

If this node becomes required on RZ/V2H and RZ/G3E, and controlled
through status, you also need:

    if (!of_device_is_available(rcfg.of_node)) {
            of_node_put(rcfg.of_node);
            rcfg.of_node = NULL;
    }

Or introduce of_get_available_child_by_name()...

> +       if (rcfg.of_node) {
> +               rcfg.driver_data = priv->host;
> +
> +               renesas_sdhi_vqmmc_regulator.name = "sdhi-vqmmc-regulator";

Name conflict in case of multiple instances?

> +               renesas_sdhi_vqmmc_regulator.of_match = of_match_ptr("vqmmc-regulator");
> +               renesas_sdhi_vqmmc_regulator.type = REGULATOR_VOLTAGE;
> +               renesas_sdhi_vqmmc_regulator.owner = THIS_MODULE;
> +               rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg);
> +               of_node_put(rcfg.of_node);
> +               if (IS_ERR(rdev)) {
> +                       dev_err(dev, "regulator register failed err=%ld", PTR_ERR(rdev));
> +                       goto efree;
> +               }
> +               priv->rdev = rdev;
> +       }
> +
>         ver = sd_ctrl_read16(host, CTL_VERSION);
>         /* GEN2_SDR104 is first known SDHI to use 32bit block count */
>         if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)

Gr{oetje,eeting}s,

                        Geert
Biju Das Jan. 28, 2025, 1:43 p.m. UTC | #3
Hi Geert,

Thanks for the feedback.

> -----Original Message-----
> From: Geert Uytterhoeven <geert@linux-m68k.org>
> Sent: 28 January 2025 13:32
> Subject: Re: [PATCH 4/7] mmc: renesas_sdhi: Add support for RZ/G3E SoC
> 
> Hi Biju,
> 
> On Sun, 26 Jan 2025 at 14:46, Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > The SDHI/eMMC IPs in the RZ/G3E SoC are similar to those in R-Car Gen3.
> > However, the RZ/G3E SD0 channel has Voltage level control and PWEN pin
> > support via SD_STATUS register.
> >
> > internal regulator support is added to control the voltage levels of
> > the SD pins via sd_iovs/sd_pwen bits in SD_STATUS register by
> > populating vqmmc-regulator child node.
> >
> > SD1 and SD2 channels have gpio regulator support and internal
> > regulator support. Selection of the regulator is based on the regulator phandle.
> >
> > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> 
> Thanks for your patch!
> 
> > --- a/drivers/mmc/host/renesas_sdhi_core.c
> > +++ b/drivers/mmc/host/renesas_sdhi_core.c
> 
> > @@ -1053,6 +1165,23 @@ int renesas_sdhi_probe(struct platform_device *pdev,
> >         if (ret)
> >                 goto efree;
> >
> > +       rcfg.of_node = of_get_child_by_name(dev->of_node,
> > + "vqmmc-regulator");
> 
> If this node becomes required on RZ/V2H and RZ/G3E, and controlled through status, you also need:
> 
>     if (!of_device_is_available(rcfg.of_node)) {
>             of_node_put(rcfg.of_node);
>             rcfg.of_node = NULL;
>     }

OK.

> 
> Or introduce of_get_available_child_by_name()...

OK, will send a separate patch for optimizing the above 2 calls.

> 
> > +       if (rcfg.of_node) {
> > +               rcfg.driver_data = priv->host;
> > +
> > +               renesas_sdhi_vqmmc_regulator.name =
> > + "sdhi-vqmmc-regulator";
> 
> Name conflict in case of multiple instances?

This regulator name is Overriden by of_regulator() and it will pick the name from there.
See below. Am I missing anything?

root@smarc-rzg3e:/cip-test-scripts# cat /sys/class/regulator/regulator.*/name
regulator-dummy
fixed-3.3V
fixed-3.3V
SDHI1 VccQ
SDHI0-VQMMC
SDHI2-VQMMC
SDHI1-VQMMC

Cheers,
Biju
Biju Das Jan. 29, 2025, 2:45 p.m. UTC | #4
Hi Geert,

 
> > -----Original Message-----
> > From: Geert Uytterhoeven <geert@linux-m68k.org>
> > Sent: 28 January 2025 13:32
> > Subject: Re: [PATCH 4/7] mmc: renesas_sdhi: Add support for RZ/G3E SoC
> >
> > Hi Biju,
> >
> > On Sun, 26 Jan 2025 at 14:46, Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > > The SDHI/eMMC IPs in the RZ/G3E SoC are similar to those in R-Car Gen3.
> > > However, the RZ/G3E SD0 channel has Voltage level control and PWEN
> > > pin support via SD_STATUS register.
> > >
> > > internal regulator support is added to control the voltage levels of
> > > the SD pins via sd_iovs/sd_pwen bits in SD_STATUS register by
> > > populating vqmmc-regulator child node.
> > >
> > > SD1 and SD2 channels have gpio regulator support and internal
> > > regulator support. Selection of the regulator is based on the regulator phandle.
> > >
> > > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> >
> > Thanks for your patch!
> >
> > > --- a/drivers/mmc/host/renesas_sdhi_core.c
> > > +++ b/drivers/mmc/host/renesas_sdhi_core.c
> >
> > > @@ -1053,6 +1165,23 @@ int renesas_sdhi_probe(struct platform_device *pdev,
> > >         if (ret)
> > >                 goto efree;
> > >
> >
> > > +       if (rcfg.of_node) {
> > > +               rcfg.driver_data = priv->host;
> > > +
> > > +               renesas_sdhi_vqmmc_regulator.name =
> > > + "sdhi-vqmmc-regulator";
> >
> > Name conflict in case of multiple instances?
> 
> This regulator name is Overriden by of_regulator() and it will pick the name from there.
> See below. Am I missing anything?

Just to add, without the name it returns error see [1],

Regulator name from DT node is picked by [2] and [3]. So there won't be conflict for
Multiple instances.

[1] https://elixir.bootlin.com/linux/v6.13-rc7/source/drivers/regulator/core.c#L5597

[2] https://elixir.bootlin.com/linux/v6.13-rc7/source/drivers/regulator/core.c#L5659
[3] https://elixir.bootlin.com/linux/v6.13-rc7/source/drivers/regulator/of_regulator.c#L97

Cheers,
Biju

> 
> root@smarc-rzg3e:/cip-test-scripts# cat /sys/class/regulator/regulator.*/name
> regulator-dummy
> fixed-3.3V
> fixed-3.3V
> SDHI1 VccQ
> SDHI0-VQMMC
> SDHI2-VQMMC
> SDHI1-VQMMC
> 
> Cheers,
> Biju
Geert Uytterhoeven Jan. 29, 2025, 2:47 p.m. UTC | #5
Hi Biju,

On Wed, 29 Jan 2025 at 15:45, Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > > -----Original Message-----
> > > From: Geert Uytterhoeven <geert@linux-m68k.org>
> > > Sent: 28 January 2025 13:32
> > > Subject: Re: [PATCH 4/7] mmc: renesas_sdhi: Add support for RZ/G3E SoC
> > >
> > > On Sun, 26 Jan 2025 at 14:46, Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > > > The SDHI/eMMC IPs in the RZ/G3E SoC are similar to those in R-Car Gen3.
> > > > However, the RZ/G3E SD0 channel has Voltage level control and PWEN
> > > > pin support via SD_STATUS register.
> > > >
> > > > internal regulator support is added to control the voltage levels of
> > > > the SD pins via sd_iovs/sd_pwen bits in SD_STATUS register by
> > > > populating vqmmc-regulator child node.
> > > >
> > > > SD1 and SD2 channels have gpio regulator support and internal
> > > > regulator support. Selection of the regulator is based on the regulator phandle.
> > > >
> > > > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> > >
> > > > --- a/drivers/mmc/host/renesas_sdhi_core.c
> > > > +++ b/drivers/mmc/host/renesas_sdhi_core.c
> > >
> > > > @@ -1053,6 +1165,23 @@ int renesas_sdhi_probe(struct platform_device *pdev,
> > > >         if (ret)
> > > >                 goto efree;
> > > >
> > >
> > > > +       if (rcfg.of_node) {
> > > > +               rcfg.driver_data = priv->host;
> > > > +
> > > > +               renesas_sdhi_vqmmc_regulator.name =
> > > > + "sdhi-vqmmc-regulator";
> > >
> > > Name conflict in case of multiple instances?
> >
> > This regulator name is Overriden by of_regulator() and it will pick the name from there.
> > See below. Am I missing anything?
>
> Just to add, without the name it returns error see [1],

Thanks, I had arrived at the same conclusion.

Gr{oetje,eeting}s,

                        Geert
diff mbox series

Patch

diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index f12a87442338..291ddb4ad9be 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -95,6 +95,7 @@  struct renesas_sdhi {
 
 	struct reset_control *rstc;
 	struct tmio_mmc_host *host;
+	struct regulator_dev *rdev;
 };
 
 #define host_to_priv(host) \
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 6ea651409774..bbfa68ca682b 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -32,6 +32,8 @@ 
 #include <linux/platform_device.h>
 #include <linux/pm_domain.h>
 #include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/reset.h>
 #include <linux/sh_dma.h>
 #include <linux/slab.h>
@@ -581,12 +583,24 @@  static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
 
 	if (!preserve) {
 		if (priv->rstc) {
+			u32 sd_status;
+			/*
+			 * HW reset might have toggled the regulator state in
+			 * HW which regulator core might be unaware of so save
+			 * and restore the regulator state during HW reset.
+			 */
+			if (priv->rdev)
+				sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
+
 			reset_control_reset(priv->rstc);
 			/* Unknown why but without polling reset status, it will hang */
 			read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
 					  false, priv->rstc);
 			/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
 			sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
+			if (priv->rdev)
+				sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
 			priv->needs_adjust_hs400 = false;
 			renesas_sdhi_set_clock(host, host->clk_cache);
 
@@ -904,15 +918,113 @@  static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
 	renesas_sdhi_sdbuf_width(host, enable ? width : 16);
 }
 
+static const unsigned int renesas_sdhi_vqmmc_voltages[] = {
+	3300000, 1800000
+};
+
+static int renesas_sdhi_regulator_disable(struct regulator_dev *rdev)
+{
+	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+	u32 sd_status;
+
+	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
+	sd_status &= ~SD_STATUS_PWEN;
+	sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+	return 0;
+}
+
+static int renesas_sdhi_regulator_enable(struct regulator_dev *rdev)
+{
+	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+	u32 sd_status;
+
+	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
+	sd_status |= SD_STATUS_PWEN;
+	sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+	return 0;
+}
+
+static int renesas_sdhi_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+	u32 sd_status;
+
+	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
+
+	return (sd_status & SD_STATUS_PWEN) ? 1 : 0;
+}
+
+static int renesas_sdhi_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+	u32 sd_status;
+
+	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
+
+	return (sd_status & SD_STATUS_IOVS) ? 1800000 : 3300000;
+}
+
+static int renesas_sdhi_regulator_set_voltage(struct regulator_dev *rdev,
+					      int min_uV, int max_uV,
+					      unsigned int *selector)
+{
+	struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+	u32 sd_status;
+
+	sd_ctrl_read32_rep(host, CTL_SD_STATUS, &sd_status, 1);
+	if (min_uV >= 1700000 && max_uV <= 1950000) {
+		sd_status |= SD_STATUS_IOVS;
+		*selector = 1;
+	} else {
+		sd_status &= ~SD_STATUS_IOVS;
+		*selector = 0;
+	}
+	sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+	return 0;
+}
+
+static int renesas_sdhi_regulator_list_voltage(struct regulator_dev *rdev,
+					       unsigned int selector)
+{
+	if (selector >= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages))
+		return -EINVAL;
+
+	return renesas_sdhi_vqmmc_voltages[selector];
+}
+
+static const struct regulator_ops renesas_sdhi_regulator_voltage_ops = {
+	.enable = renesas_sdhi_regulator_enable,
+	.disable = renesas_sdhi_regulator_disable,
+	.is_enabled = renesas_sdhi_regulator_is_enabled,
+	.list_voltage = renesas_sdhi_regulator_list_voltage,
+	.get_voltage = renesas_sdhi_regulator_get_voltage,
+	.set_voltage = renesas_sdhi_regulator_set_voltage,
+};
+
+static struct regulator_desc renesas_sdhi_vqmmc_regulator = {
+	.of_match	= of_match_ptr("vqmmc-regulator"),
+	.owner		= THIS_MODULE,
+	.type		= REGULATOR_VOLTAGE,
+	.ops		= &renesas_sdhi_regulator_voltage_ops,
+	.volt_table	= renesas_sdhi_vqmmc_voltages,
+	.n_voltages	= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages),
+};
+
 int renesas_sdhi_probe(struct platform_device *pdev,
 		       const struct tmio_mmc_dma_ops *dma_ops,
 		       const struct renesas_sdhi_of_data *of_data,
 		       const struct renesas_sdhi_quirks *quirks)
 {
+	struct regulator_config rcfg = { .dev = &pdev->dev, };
 	struct tmio_mmc_data *mmd = pdev->dev.platform_data;
 	struct renesas_sdhi_dma *dma_priv;
+	struct device *dev = &pdev->dev;
 	struct tmio_mmc_data *mmc_data;
 	struct tmio_mmc_host *host;
+	struct regulator_dev *rdev;
 	struct renesas_sdhi *priv;
 	int num_irqs, irq, ret, i;
 	struct resource *res;
@@ -1053,6 +1165,23 @@  int renesas_sdhi_probe(struct platform_device *pdev,
 	if (ret)
 		goto efree;
 
+	rcfg.of_node = of_get_child_by_name(dev->of_node, "vqmmc-regulator");
+	if (rcfg.of_node) {
+		rcfg.driver_data = priv->host;
+
+		renesas_sdhi_vqmmc_regulator.name = "sdhi-vqmmc-regulator";
+		renesas_sdhi_vqmmc_regulator.of_match = of_match_ptr("vqmmc-regulator");
+		renesas_sdhi_vqmmc_regulator.type = REGULATOR_VOLTAGE;
+		renesas_sdhi_vqmmc_regulator.owner = THIS_MODULE;
+		rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg);
+		of_node_put(rcfg.of_node);
+		if (IS_ERR(rdev)) {
+			dev_err(dev, "regulator register failed err=%ld", PTR_ERR(rdev));
+			goto efree;
+		}
+		priv->rdev = rdev;
+	}
+
 	ver = sd_ctrl_read16(host, CTL_VERSION);
 	/* GEN2_SDR104 is first known SDHI to use 32bit block count */
 	if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index a75755f31d31..5970ca598850 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -44,6 +44,7 @@ 
 #define CTL_RESET_SD 0xe0
 #define CTL_VERSION 0xe2
 #define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */
+#define CTL_SD_STATUS 0xf2 /* only known on RZ/{G2L,G3E,V2H} */
 
 /* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */
 #define TMIO_STOP_STP		BIT(0)
@@ -103,6 +104,10 @@ 
 /* Definitions for values the CTL_SDIF_MODE register can take */
 #define SDIF_MODE_HS400		BIT(0) /* only known on R-Car 2+ */
 
+/* Definitions for values the CTL_SD_STATUS register can take */
+#define SD_STATUS_PWEN		BIT(0) /* only known on RZ/{G3E,V2H} */
+#define SD_STATUS_IOVS		BIT(16) /* only known on RZ/{G3E,V2H} */
+
 /* Define some IRQ masks */
 /* This is the mask used at reset by the chip */
 #define TMIO_MASK_ALL           0x837f031d