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 |
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>
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
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
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
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 --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
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(+)