On 24.06.2024 18:32, Prabhakar wrote: > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx> > > The SDHI/eMMC IPs found in the RZ/V2H(P) (a.k.a. r9a09g057) are very > similar to those found in R-Car Gen3. However, they are not identical, > necessitating an SoC-specific compatible string for fine-tuning driver > support. > > Key features of the RZ/V2H(P) SDHI/eMMC IPs include: > - Voltage level control via the IOVS bit. > - PWEN pin support via SD_STATUS register. > - Lack of HS400 support. > - Fixed address mode operation. > > internal regulator support is added to control the voltage levels of SD > pins via sd_iovs/sd_pwen bits in SD_STATUS register. > > As the users can use GPIO regulator instead of SD_IOVS/PWEN pins > 'renesas,sdhi-use-internal-regulator' DT property is used for > special handling of internal regulator. > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx> Tested-by: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx> # on RZ/G3S Some minor comments above. Feel free to address them or not. Thank you, Claudiu Beznea > --- > v2->v3 > - Moved regulator info to renesas_sdhi_of_data instead of quirks > - Added support to configure the init state of regulator > - Added function pointers to configure regulator > - Added REGULATOR_CHANGE_VOLTAGE mask > > v1->v2 > - Now controlling PWEN bit get/set_voltage > --- > drivers/mmc/host/renesas_sdhi.h | 13 ++ > drivers/mmc/host/renesas_sdhi_core.c | 93 +++++++++++ > drivers/mmc/host/renesas_sdhi_internal_dmac.c | 150 ++++++++++++++++++ > drivers/mmc/host/tmio_mmc.h | 5 + > 4 files changed, 261 insertions(+) > > diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h > index 586f94d4dbfd..377804bbc37e 100644 > --- a/drivers/mmc/host/renesas_sdhi.h > +++ b/drivers/mmc/host/renesas_sdhi.h > @@ -11,6 +11,8 @@ > > #include <linux/dmaengine.h> > #include <linux/platform_device.h> > +#include <linux/regulator/driver.h> > +#include <linux/regulator/machine.h> > #include "tmio_mmc.h" > > struct renesas_sdhi_scc { > @@ -35,6 +37,12 @@ struct renesas_sdhi_of_data { > unsigned int max_blk_count; > unsigned short max_segs; > unsigned long sdhi_flags; > + struct regulator_desc *rdesc; > + struct regulator_init_data *reg_init_data; > + bool regulator_init_state; > + unsigned int regulator_init_voltage; > + int (*regulator_force_endis)(struct regulator_dev *rdev, bool enable); > + int (*regulator_force_voltage)(struct regulator_dev *rdev, unsigned int voltage); > }; > > #define SDHI_CALIB_TABLE_MAX 32 > @@ -93,6 +101,11 @@ struct renesas_sdhi { > unsigned int tap_set; > > struct reset_control *rstc; > + > + bool use_internal_regulator; > + struct regulator_dev *internal_regulator; > + int (*regulator_force_endis)(struct regulator_dev *rdev, bool enable); > + int (*regulator_force_voltage)(struct regulator_dev *rdev, unsigned int voltage); > }; > > #define host_to_priv(host) \ > diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c > index 12f4faaaf4ee..8cf912512f07 100644 > --- a/drivers/mmc/host/renesas_sdhi_core.c > +++ b/drivers/mmc/host/renesas_sdhi_core.c > @@ -581,12 +581,50 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve) > > if (!preserve) { > if (priv->rstc) { > + bool regulator_enabled; > + > + /* to restore back the internal regulator after reset make a copy of it */ > + if (priv->use_internal_regulator) > + regulator_enabled = regulator_is_enabled(host->mmc->supply.vqmmc); > + > 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->use_internal_regulator) { > + int voltage; > + > + /* > + * HW reset might have toggled the regulator state in HW > + * which regulator core might be unaware of so restore > + * back the regulator state in HW bypassing the regulator > + * core. > + */ > + if (regulator_enabled != > + regulator_is_enabled(host->mmc->supply.vqmmc)) { > + ret = priv->regulator_force_endis(priv->internal_regulator, > + regulator_enabled); > + if (ret) > + dev_err(&host->pdev->dev, > + "Failed to %s internal regulator\n", > + regulator_enabled ? "enable" : "disable"); > + } > + > + /* restore back voltage on vqmmc supply */ > + voltage = regulator_get_voltage(host->mmc->supply.vqmmc); > + if (voltage != host->mmc->ios.signal_voltage) { > + voltage = host->mmc->ios.signal_voltage == > + MMC_SIGNAL_VOLTAGE_330 ? 3300000 : 1800000; > + ret = regulator_set_voltage(host->mmc->supply.vqmmc, > + voltage, voltage); > + if (ret) > + dev_err(&host->pdev->dev, > + "Failed to set voltage on internal regulator\n"); > + } > + } > + > priv->needs_adjust_hs400 = false; > renesas_sdhi_set_clock(host, host->clk_cache); > > @@ -904,6 +942,29 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) > renesas_sdhi_sdbuf_width(host, enable ? width : 16); > } > > +static int renesas_sdhi_internal_dmac_register_regulator(struct platform_device *pdev, > + const struct renesas_sdhi_of_data *of_data) > +{ > + struct tmio_mmc_host *host = platform_get_drvdata(pdev); > + struct renesas_sdhi *priv = host_to_priv(host); > + struct regulator_config rcfg = { > + .dev = &pdev->dev, > + .driver_data = host, > + .init_data = of_data->reg_init_data, > + }; > + const char *devname; > + > + devname = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-vqmmc-regulator", > + dev_name(&pdev->dev)); > + if (!devname) > + return -ENOMEM; > + > + of_data->rdesc->name = devname; > + priv->internal_regulator = devm_regulator_register(&pdev->dev, of_data->rdesc, &rcfg); > + > + return PTR_ERR_OR_ZERO(priv->internal_regulator); > +} > + > int renesas_sdhi_probe(struct platform_device *pdev, > const struct tmio_mmc_dma_ops *dma_ops, > const struct renesas_sdhi_of_data *of_data, > @@ -970,6 +1031,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, > if (IS_ERR(host)) > return PTR_ERR(host); > > + if (pdev->dev.of_node) > + priv->use_internal_regulator = of_property_read_bool(pdev->dev.of_node, > + "renesas,sdhi-use-internal-regulator"); > + > if (of_data) { > mmc_data->flags |= of_data->tmio_flags; > mmc_data->ocr_mask = of_data->tmio_ocr_mask; > @@ -980,6 +1045,18 @@ int renesas_sdhi_probe(struct platform_device *pdev, > mmc_data->max_segs = of_data->max_segs; > dma_priv->dma_buswidth = of_data->dma_buswidth; > host->bus_shift = of_data->bus_shift; > + if (priv->use_internal_regulator) { > + if (!of_data->regulator_force_endis) > + return dev_err_probe(&pdev->dev, -EINVAL, > + "missing function pointer to force regulator enable/disable"); > + priv->regulator_force_endis = > + of_data->regulator_force_endis; > + if (!of_data->regulator_force_voltage) > + return dev_err_probe(&pdev->dev, -EINVAL, > + "missing function pointer to force regulator voltage"); > + priv->regulator_force_voltage = > + of_data->regulator_force_voltage; > + } > /* Fallback for old DTs */ > if (!priv->clkh && of_data->sdhi_flags & SDHI_FLAG_NEED_CLKH_FALLBACK) > priv->clkh = clk_get_parent(clk_get_parent(priv->clk)); > @@ -1051,6 +1128,22 @@ int renesas_sdhi_probe(struct platform_device *pdev, > if (ret) > goto efree; > > + if (priv->use_internal_regulator && of_data) { > + ret = renesas_sdhi_internal_dmac_register_regulator(pdev, of_data); > + if (ret) > + goto efree; > + > + /* Set the default init state for regulator in the HW */ > + ret = priv->regulator_force_endis(priv->internal_regulator, > + of_data->regulator_init_state); > + if (ret) > + goto efree; > + ret = priv->regulator_force_voltage(priv->internal_regulator, > + of_data->regulator_init_voltage); > + if (ret) > + goto efree; > + } > + > 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/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c > index 422fa63a2e99..a70a1ae6919c 100644 > --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c > +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c > @@ -89,6 +89,150 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { > }, > }; > > +static const unsigned int r9a09g057_vqmmc_voltages[] = { > + 1800000, 3300000, > +}; > + > +static int r9a09g057_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; There is an extra space after = > + sd_ctrl_write32(host, CTL_SD_STATUS, sd_status); > + > + return 0; > +} > + > +static int r9a09g057_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; Same here. > + sd_ctrl_write32(host, CTL_SD_STATUS, sd_status); > + > + return 0; > +} > + > +static int r9a09g057_regulator_force_endis(struct regulator_dev *rdev, bool enable) > +{ > + if (enable) > + return r9a09g057_regulator_enable(rdev); > + > + return r9a09g057_regulator_disable(rdev); > +} > + > +static int r9a09g057_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); > + if (sd_status & SD_STATUS_PWEN) > + return 1; > + > + return 0; Could be re-written as: return !!(sd_status & SD_STATUS_PWEN) > +} > + > +static int r9a09g057_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); > + if (sd_status & SD_STATUS_IOVS) > + return 1800000; > + > + return 3300000; > +} > + > +static int r9a09g057_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 = 0; > + } else { > + sd_status &= ~SD_STATUS_IOVS; > + *selector = 1; > + } > + sd_ctrl_write32(host, CTL_SD_STATUS, sd_status); > + > + return 0; > +} > + > +static int r9a09g057_regulator_force_voltage(struct regulator_dev *rdev, > + unsigned int voltage) > +{ > + unsigned int selector = 0; > + > + return r9a09g057_regulator_set_voltage(rdev, voltage, voltage, &selector); > +} > + > +static int r9a09g057_regulator_list_voltage(struct regulator_dev *rdev, > + unsigned int selector) > +{ > + if (selector >= ARRAY_SIZE(r9a09g057_vqmmc_voltages)) > + return -EINVAL; > + > + return r9a09g057_vqmmc_voltages[selector]; > +} > + > +static struct regulator_init_data r9a09g057_regulator_init_data = { > + .constraints = { > + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE, > + }, > +}; > + > +static const struct regulator_ops r9a09g057_regulator_voltage_ops = { > + .enable = r9a09g057_regulator_enable, > + .disable = r9a09g057_regulator_disable, > + .is_enabled = r9a09g057_regulator_is_enabled, > + .list_voltage = r9a09g057_regulator_list_voltage, > + .get_voltage = r9a09g057_regulator_get_voltage, > + .set_voltage = r9a09g057_regulator_set_voltage, > + .map_voltage = regulator_map_voltage_ascend, > +}; > + > +static struct regulator_desc r9a09g057_vqmmc_regulator = { > + .of_match = of_match_ptr("vqmmc-r9a09g057-regulator"), > + .owner = THIS_MODULE, > + .type = REGULATOR_VOLTAGE, > + .ops = &r9a09g057_regulator_voltage_ops, > + .volt_table = r9a09g057_vqmmc_voltages, > + .n_voltages = ARRAY_SIZE(r9a09g057_vqmmc_voltages), > +}; > + > +static const struct renesas_sdhi_of_data of_data_r9a09g057 = { > + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | > + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, > + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | > + MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY, > + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE, > + .bus_shift = 2, > + .scc_offset = 0x1000, > + .taps = rcar_gen3_scc_taps, > + .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), > + /* DMAC can handle 32bit blk count but only 1 segment */ > + .max_blk_count = UINT_MAX / TMIO_MAX_BLK_SIZE, > + .max_segs = 1, > + .sdhi_flags = SDHI_FLAG_NEED_CLKH_FALLBACK, > + .rdesc = &r9a09g057_vqmmc_regulator, > + .reg_init_data = &r9a09g057_regulator_init_data, > + .regulator_init_state = false, > + .regulator_init_voltage = 3300000, > + .regulator_force_endis = r9a09g057_regulator_force_endis, > + .regulator_force_voltage = r9a09g057_regulator_force_voltage, > +}; > + > static const struct renesas_sdhi_of_data of_data_rza2 = { > .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | > TMIO_MMC_HAVE_CBSY, > @@ -260,6 +404,11 @@ static const struct renesas_sdhi_of_data_with_quirks of_rzg2l_compatible = { > .quirks = &sdhi_quirks_rzg2l, > }; > > +static const struct renesas_sdhi_of_data_with_quirks of_r9a09g057_compatible = { > + .of_data = &of_data_r9a09g057, > + .quirks = &sdhi_quirks_rzg2l, > +}; > + > static const struct renesas_sdhi_of_data_with_quirks of_rcar_gen3_compatible = { > .of_data = &of_data_rcar_gen3, > }; > @@ -284,6 +433,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { > { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, }, > { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, }, > { .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, }, > + { .compatible = "renesas,sdhi-r9a09g057", .data = &of_r9a09g057_compatible, }, > { .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, }, > { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, > { .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, }, > diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h > index de56e6534aea..1a3172786662 100644 > --- a/drivers/mmc/host/tmio_mmc.h > +++ b/drivers/mmc/host/tmio_mmc.h > @@ -43,6 +43,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 and RZ/V2H(P) */ > > /* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */ > #define TMIO_STOP_STP BIT(0) > @@ -102,6 +103,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/V2H(P) */ > +#define SD_STATUS_IOVS BIT(16) /* only known on RZ/V2H(P) */ > + > /* Define some IRQ masks */ > /* This is the mask used at reset by the chip */ > #define TMIO_MASK_ALL 0x837f031d