From: Lad Prabhakar <[email protected]>
Hi All,
This patch series aims to add SD/MMC support for Renesas RZ/V2H(P) SoC.
v1->v2
- Dropped regulator core API changes
- Updated DT binding
- Now controlling PWEN bit via regultor api
v1: https://patchwork.kernel.org/project/linux-renesas-soc/cover/[email protected]/
Cheers,
Prabhakar
Lad Prabhakar (3):
dt-bindings: mmc: renesas,sdhi: Document RZ/V2H(P) support
mmc: tmio: Use MMC core APIs to control the vqmmc regulator
mmc: renesas_sdhi: Add support for RZ/V2H(P) SoC
.../devicetree/bindings/mmc/renesas,sdhi.yaml | 20 ++-
drivers/mmc/host/renesas_sdhi.h | 8 ++
drivers/mmc/host/renesas_sdhi_core.c | 43 +++++++
drivers/mmc/host/renesas_sdhi_internal_dmac.c | 120 ++++++++++++++++++
drivers/mmc/host/tmio_mmc.h | 5 +
drivers/mmc/host/tmio_mmc_core.c | 7 +-
6 files changed, 198 insertions(+), 5 deletions(-)
--
2.34.1
From: Lad Prabhakar <[email protected]>
The SD/MMC block on the RZ/V2H(P) ("R9A09G057") SoC is similar to that
of the R-Car Gen3, but it has some differences:
- HS400 is not supported.
- It supports the SD_IOVS bit to control the IO voltage level.
- It supports fixed address mode.
To accommodate these differences, a SoC-specific 'renesas,sdhi-r9a09g057'
compatible string is added.
A "vqmmc-r9a09g057-regulator" regulator object is added to handle the
pwen and voltage level switch of the SD/MMC.
Signed-off-by: Lad Prabhakar <[email protected]>
---
v1->v2
- Moved vqmmc object in the if block
- Updated commit message
---
.../devicetree/bindings/mmc/renesas,sdhi.yaml | 20 ++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
index 3d0e61e59856..154f5767cf03 100644
--- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
+++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
@@ -18,6 +18,7 @@ properties:
- renesas,sdhi-r7s9210 # SH-Mobile AG5
- renesas,sdhi-r8a73a4 # R-Mobile APE6
- renesas,sdhi-r8a7740 # R-Mobile A1
+ - renesas,sdhi-r9a09g057 # RZ/V2H(P)
- renesas,sdhi-sh73a0 # R-Mobile APE6
- items:
- enum:
@@ -118,7 +119,9 @@ allOf:
properties:
compatible:
contains:
- const: renesas,rzg2l-sdhi
+ enum:
+ - renesas,sdhi-r9a09g057
+ - renesas,rzg2l-sdhi
then:
properties:
clocks:
@@ -204,6 +207,21 @@ allOf:
sectioned off to be run by a separate second clock source to allow
the main core clock to be turned off to save power.
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: renesas,sdhi-r9a09g057
+ then:
+ properties:
+ vqmmc-r9a09g057-regulator:
+ type: object
+ description: VQMMC SD regulator
+ $ref: /schemas/regulator/regulator.yaml#
+ unevaluatedProperties: false
+ required:
+ - vqmmc-r9a09g057-regulator
+
required:
- compatible
- reg
--
2.34.1
From: Lad Prabhakar <[email protected]>
Use the mmc_regulator_enable_vqmmc() and mmc_regulator_disable_vqmmc() APIs
to enable/disable the vqmmc regulator.
Signed-off-by: Lad Prabhakar <[email protected]>
---
v1->v2
- New patch
---
drivers/mmc/host/tmio_mmc_core.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 93e912afd3ae..2ec1a74c85bc 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -897,8 +897,8 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
* It seems, VccQ should be switched on after Vcc, this is also what the
* omap_hsmmc.c driver does.
*/
- if (!IS_ERR(mmc->supply.vqmmc) && !ret) {
- ret = regulator_enable(mmc->supply.vqmmc);
+ if (!ret) {
+ ret = mmc_regulator_enable_vqmmc(mmc);
usleep_range(200, 300);
}
@@ -911,8 +911,7 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
{
struct mmc_host *mmc = host->mmc;
- if (!IS_ERR(mmc->supply.vqmmc))
- regulator_disable(mmc->supply.vqmmc);
+ mmc_regulator_disable_vqmmc(mmc);
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
--
2.34.1
From: Lad Prabhakar <[email protected]>
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.
regulator support is added to control the volatage levels of SD pins
via sd_iovs/sd_pwen bits in SD_STATUS register.
Signed-off-by: Lad Prabhakar <[email protected]>
---
Hi Wolfram,
- Ive modelled the regulator now to control the PWEN aswell.
- I have still kept regulator bits in quirks I was wondering if I should
move this to renesas_sdhi_of_data instead?
- I still need to add checks if the internal regulator used and
only then call regulator_enable/regulator_set_voltage. ATM I am still
unclear on differentiating if internal/external regulator is used.
Please let me know your thoughts.
Cheers, Prabhakar
v1->v2
- Now controlling PWEN bit get/set_voltage
---
drivers/mmc/host/renesas_sdhi.h | 8 ++
drivers/mmc/host/renesas_sdhi_core.c | 43 +++++++
drivers/mmc/host/renesas_sdhi_internal_dmac.c | 120 ++++++++++++++++++
drivers/mmc/host/tmio_mmc.h | 5 +
4 files changed, 176 insertions(+)
diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index 586f94d4dbfd..91e42e680dbb 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -11,6 +11,9 @@
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
#include "tmio_mmc.h"
struct renesas_sdhi_scc {
@@ -49,6 +52,9 @@ struct renesas_sdhi_quirks {
bool manual_tap_correction;
bool old_info1_layout;
u32 hs400_bad_taps;
+ bool internal_regulator;
+ struct regulator_desc *rdesc;
+ struct regulator_init_data *reg_init_data;
const u8 (*hs400_calib_table)[SDHI_CALIB_TABLE_MAX];
};
@@ -93,6 +99,8 @@ struct renesas_sdhi {
unsigned int tap_set;
struct reset_control *rstc;
+
+ struct regulator_dev *sd_status;
};
#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..47e99ce0b752 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -587,6 +587,20 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
false, priv->rstc);
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
+ if (sdhi_has_quirk(priv, internal_regulator)) {
+ unsigned int voltage;
+
+ if (!regulator_is_enabled(host->mmc->supply.vqmmc))
+ if (regulator_enable(host->mmc->supply.vqmmc))
+ dev_err(&host->pdev->dev, "Failed to enable internal regulator\n");
+
+ /* restore back voltage on vqmmc supply */
+ voltage = host->mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330 ?
+ 3300000 : 1800000;
+ regulator_set_voltage(host->mmc->supply.vqmmc,
+ voltage, voltage);
+ }
+
priv->needs_adjust_hs400 = false;
renesas_sdhi_set_clock(host, host->clk_cache);
@@ -904,6 +918,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_quirks *quirks)
+{
+ 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 = quirks->reg_init_data,
+ };
+ const char *devname;
+
+ devname = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-vqmmc-regulator",
+ dev_name(&pdev->dev));
+ if (!devname)
+ return -ENOMEM;
+
+ quirks->rdesc->name = devname;
+ priv->sd_status = devm_regulator_register(&pdev->dev, quirks->rdesc, &rcfg);
+
+ return PTR_ERR_OR_ZERO(priv->sd_status);
+}
+
int renesas_sdhi_probe(struct platform_device *pdev,
const struct tmio_mmc_dma_ops *dma_ops,
const struct renesas_sdhi_of_data *of_data,
@@ -1051,6 +1088,12 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ret)
goto efree;
+ if (sdhi_has_quirk(priv, internal_regulator)) {
+ ret = renesas_sdhi_internal_dmac_register_regulator(pdev, quirks);
+ 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..ee0aafae9661 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -215,6 +215,120 @@ static const struct renesas_sdhi_quirks sdhi_quirks_rzg2l = {
.hs400_disabled = true,
};
+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;
+ 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;
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+ return 0;
+}
+
+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;
+}
+
+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_list_voltage(struct regulator_dev *rdev,
+ unsigned 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,
+ },
+};
+
+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_quirks sdhi_quirks_r9a09g057 = {
+ .fixed_addr_mode = true,
+ .hs400_disabled = true,
+ .internal_regulator = true,
+ .rdesc = &r9a09g057_vqmmc_regulator,
+ .reg_init_data = &r9a09g057_regulator_init_data,
+};
+
/*
* Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
* So, we want to treat them equally and only have a match for ES1.2 to enforce
@@ -260,6 +374,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_rcar_gen3,
+ .quirks = &sdhi_quirks_r9a09g057,
+};
+
static const struct renesas_sdhi_of_data_with_quirks of_rcar_gen3_compatible = {
.of_data = &of_data_rcar_gen3,
};
@@ -284,6 +403,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
--
2.34.1