All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support
@ 2023-02-27 18:31 Piyush Malgujar
  2023-02-27 18:31 ` [PATCH v3 1/6] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
                   ` (5 more replies)
  0 siblings, 6 replies; 15+ messages in thread
From: Piyush Malgujar @ 2023-02-27 18:31 UTC (permalink / raw)
  To: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Piyush Malgujar

Added changes to support SD6 controller
- Restructure and reformat the code.
- Add SD6 related operations.
- Support added for MMC_SDHCI_IO_ACCESSORS.
- Related changes done in dt bindings.
- Support for debug option.

Changes since V2:
- Added separate patches for renaming of functions and 
  restructuring, adding new structures to support SD4/SD6 operations.
- Added proper suffixes to properties in dt binding.
- Removed unreachable code.
- Handled sdhci_cdns_uniphier_pltfm_data similar to sdhci_cdns_sd4_of_data
  as per the added structured design.
- Used dev_dbg instead of DEBUG_DRV in debug patch.

Changes since V1:
- Added separate patch for reformat/rename changes.
- Enabled MMC_SDHCI_IO_ACCESSORS in config MMC_SDHCI_CADENCE.
- Used proper properties in dt binding.
- Removed patch of config option to change default for sdhci timeout.
- Resolved issues reported by:
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <error27@gmail.com>

Dhananjay Kangude (3):
  mmc: sdhci-cadence: Rename functions to SD4 specific
  mmc: sdhci-cadence: Restructure the code
  mmc: sdhci-cadence: SD6 controller support

Jayanthi Annadurai (3):
  mmc: sdhci-cadence: enable MMC_SDHCI_IO_ACCESSORS
  dt-bindings: mmc: sdhci-cadence: SD6 support
  mmc: sdhci-cadence: Add debug option for sdhci-cadence driver

 .../devicetree/bindings/mmc/cdns,sdhci.yaml   |   24 +-
 drivers/mmc/host/Kconfig                      |    1 +
 drivers/mmc/host/sdhci-cadence.c              | 1651 ++++++++++++++++-
 3 files changed, 1612 insertions(+), 64 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v3 1/6] mmc: sdhci-cadence: Rename functions to SD4 specific
  2023-02-27 18:31 [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
@ 2023-02-27 18:31 ` Piyush Malgujar
  2023-02-27 18:31 ` [PATCH v3 2/6] mmc: sdhci-cadence: Restructure the code Piyush Malgujar
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: Piyush Malgujar @ 2023-02-27 18:31 UTC (permalink / raw)
  To: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Dhananjay Kangude, Piyush Malgujar

From: Dhananjay Kangude <dkangude@cadence.com>

Renaming the functions and structures specific to SD4 so
that it can be separated from upcoming SD6 related
functionality.

Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
 drivers/mmc/host/sdhci-cadence.c | 96 ++++++++++++++++----------------
 1 file changed, 48 insertions(+), 48 deletions(-)

diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index 6f2de54a598773879bf339aae8450f63e1251509..5276cdbc652f7faac13bb0244af4926b63dc119a 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -15,16 +15,16 @@
 
 #include "sdhci-pltfm.h"
 
-/* HRS - Host Register Set (specific to Cadence) */
-#define SDHCI_CDNS_HRS04		0x10		/* PHY access port */
-#define   SDHCI_CDNS_HRS04_ACK			BIT(26)
-#define   SDHCI_CDNS_HRS04_RD			BIT(25)
-#define   SDHCI_CDNS_HRS04_WR			BIT(24)
-#define   SDHCI_CDNS_HRS04_RDATA		GENMASK(23, 16)
-#define   SDHCI_CDNS_HRS04_WDATA		GENMASK(15, 8)
-#define   SDHCI_CDNS_HRS04_ADDR			GENMASK(5, 0)
-
-#define SDHCI_CDNS_HRS06		0x18		/* eMMC control */
+/* SD 4.0 Controller HRS - Host Register Set (specific to Cadence) */
+#define SDHCI_CDNS_HRS04			0x10	/* PHY access port */
+#define SDHCI_CDNS_SD4_HRS04_ACK		BIT(26)
+#define SDHCI_CDNS_SD4_HRS04_RD			BIT(25)
+#define SDHCI_CDNS_SD4_HRS04_WR			BIT(24)
+#define SDHCI_CDNS_SD4_HRS04_RDATA		GENMASK(23, 16)
+#define SDHCI_CDNS_SD4_HRS04_WDATA		GENMASK(15, 8)
+#define SDHCI_CDNS_SD4_HRS04_ADDR		GENMASK(5, 0)
+
+#define SDHCI_CDNS_HRS06			0x18	/* eMMC control */
 #define   SDHCI_CDNS_HRS06_TUNE_UP		BIT(15)
 #define   SDHCI_CDNS_HRS06_TUNE			GENMASK(13, 8)
 #define   SDHCI_CDNS_HRS06_MODE			GENMASK(2, 0)
@@ -38,7 +38,7 @@
 /* SRS - Slot Register Set (SDHCI-compatible) */
 #define SDHCI_CDNS_SRS_BASE		0x200
 
-/* PHY */
+/* PHY registers for SD4 controller */
 #define SDHCI_CDNS_PHY_DLY_SD_HS	0x00
 #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT	0x01
 #define SDHCI_CDNS_PHY_DLY_UHS_SDR12	0x02
@@ -59,7 +59,7 @@
  */
 #define SDHCI_CDNS_MAX_TUNING_LOOP	40
 
-struct sdhci_cdns_phy_param {
+struct sdhci_cdns_sd4_phy_param {
 	u8 addr;
 	u8 data;
 };
@@ -68,15 +68,15 @@ struct sdhci_cdns_priv {
 	void __iomem *hrs_addr;
 	bool enhanced_strobe;
 	unsigned int nr_phy_params;
-	struct sdhci_cdns_phy_param phy_params[];
+	struct sdhci_cdns_sd4_phy_param phy_params[];
 };
 
-struct sdhci_cdns_phy_cfg {
+struct sdhci_cdns_sd4_phy_cfg {
 	const char *property;
 	u8 addr;
 };
 
-static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
+static const struct sdhci_cdns_sd4_phy_cfg sdhci_cdns_sd4_phy_cfgs[] = {
 	{ "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
 	{ "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
 	{ "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, },
@@ -90,76 +90,76 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
 	{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
 };
 
-static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
-				    u8 addr, u8 data)
+static int sdhci_cdns_sd4_write_phy_reg(struct sdhci_cdns_priv *priv,
+					u8 addr, u8 data)
 {
 	void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04;
 	u32 tmp;
 	int ret;
 
-	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
+	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_SD4_HRS04_ACK),
 				 0, 10);
 	if (ret)
 		return ret;
 
-	tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
-	      FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
+	tmp = FIELD_PREP(SDHCI_CDNS_SD4_HRS04_WDATA, data) |
+	      FIELD_PREP(SDHCI_CDNS_SD4_HRS04_ADDR, addr);
 	writel(tmp, reg);
 
-	tmp |= SDHCI_CDNS_HRS04_WR;
+	tmp |= SDHCI_CDNS_SD4_HRS04_WR;
 	writel(tmp, reg);
 
-	ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10);
+	ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_SD4_HRS04_ACK, 0, 10);
 	if (ret)
 		return ret;
 
-	tmp &= ~SDHCI_CDNS_HRS04_WR;
+	tmp &= ~SDHCI_CDNS_SD4_HRS04_WR;
 	writel(tmp, reg);
 
-	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
+	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_SD4_HRS04_ACK),
 				 0, 10);
 
 	return ret;
 }
 
-static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
+static unsigned int sdhci_cdns_sd4_phy_param_count(struct device_node *np)
 {
 	unsigned int count = 0;
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
-		if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
+	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_sd4_phy_cfgs); i++)
+		if (of_property_read_bool(np, sdhci_cdns_sd4_phy_cfgs[i].property))
 			count++;
 
 	return count;
 }
 
-static void sdhci_cdns_phy_param_parse(struct device_node *np,
-				       struct sdhci_cdns_priv *priv)
+static void sdhci_cdns_sd4_phy_param_parse(struct device_node *np,
+					   struct sdhci_cdns_priv *priv)
 {
-	struct sdhci_cdns_phy_param *p = priv->phy_params;
+	struct sdhci_cdns_sd4_phy_param *p = priv->phy_params;
 	u32 val;
 	int ret, i;
 
-	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
-		ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property,
+	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_sd4_phy_cfgs); i++) {
+		ret = of_property_read_u32(np, sdhci_cdns_sd4_phy_cfgs[i].property,
 					   &val);
 		if (ret)
 			continue;
 
-		p->addr = sdhci_cdns_phy_cfgs[i].addr;
+		p->addr = sdhci_cdns_sd4_phy_cfgs[i].addr;
 		p->data = val;
 		p++;
 	}
 }
 
-static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
+static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_priv *priv)
 {
 	int ret, i;
 
 	for (i = 0; i < priv->nr_phy_params; i++) {
-		ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr,
-					       priv->phy_params[i].data);
+		ret = sdhci_cdns_sd4_write_phy_reg(priv, priv->phy_params[i].addr,
+						   priv->phy_params[i].data);
 		if (ret)
 			return ret;
 	}
@@ -202,7 +202,7 @@ static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
 	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
 }
 
-static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
+static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val)
 {
 	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
 	void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
@@ -255,7 +255,7 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
 		return 0;
 
 	for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
-		if (sdhci_cdns_set_tune_val(host, i) ||
+		if (sdhci_cdns_sd4_set_tune_val(host, i) ||
 		    mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
 			cur_streak = 0;
 		} else { /* good */
@@ -272,7 +272,7 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
 		return -EIO;
 	}
 
-	return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
+	return sdhci_cdns_sd4_set_tune_val(host, end_of_streak - max_streak / 2);
 }
 
 static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
@@ -309,7 +309,7 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
 		sdhci_set_uhs_signaling(host, timing);
 }
 
-static const struct sdhci_ops sdhci_cdns_ops = {
+static const struct sdhci_ops sdhci_cdns_sd4_ops = {
 	.set_clock = sdhci_set_clock,
 	.get_timeout_clock = sdhci_cdns_get_timeout_clock,
 	.set_bus_width = sdhci_set_bus_width,
@@ -319,12 +319,12 @@ static const struct sdhci_ops sdhci_cdns_ops = {
 };
 
 static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
-	.ops = &sdhci_cdns_ops,
+	.ops = &sdhci_cdns_sd4_ops,
 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
 };
 
-static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
-	.ops = &sdhci_cdns_ops,
+static const struct sdhci_pltfm_data sdhci_cdns_sd4_pltfm_data = {
+	.ops = &sdhci_cdns_sd4_ops,
 };
 
 static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
@@ -369,9 +369,9 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 
 	data = of_device_get_match_data(dev);
 	if (!data)
-		data = &sdhci_cdns_pltfm_data;
+		data = &sdhci_cdns_sd4_pltfm_data;
 
-	nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
+	nr_phy_params = sdhci_cdns_sd4_phy_param_count(dev->of_node);
 	host = sdhci_pltfm_init(pdev, data,
 				struct_size(priv, phy_params, nr_phy_params));
 	if (IS_ERR(host)) {
@@ -398,9 +398,9 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 	if (ret)
 		goto free;
 
-	sdhci_cdns_phy_param_parse(dev->of_node, priv);
+	sdhci_cdns_sd4_phy_param_parse(dev->of_node, priv);
 
-	ret = sdhci_cdns_phy_init(priv);
+	ret = sdhci_cdns_sd4_phy_init(priv);
 	if (ret)
 		goto free;
 
@@ -429,7 +429,7 @@ static int sdhci_cdns_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	ret = sdhci_cdns_phy_init(priv);
+	ret = sdhci_cdns_sd4_phy_init(priv);
 	if (ret)
 		goto disable_clk;
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 2/6] mmc: sdhci-cadence: Restructure the code
  2023-02-27 18:31 [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
  2023-02-27 18:31 ` [PATCH v3 1/6] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
@ 2023-02-27 18:31 ` Piyush Malgujar
  2023-03-03  8:36   ` Adrian Hunter
  2023-02-27 18:31 ` [PATCH v3 3/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Piyush Malgujar @ 2023-02-27 18:31 UTC (permalink / raw)
  To: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Dhananjay Kangude, Piyush Malgujar

From: Dhananjay Kangude <dkangude@cadence.com>

Restructured the code, added new structures and functions for
SD4 operations. Also this adds some abstraction to the code
which will make it modular and adaptable for further SD6 operations.

Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
 drivers/mmc/host/sdhci-cadence.c | 100 ++++++++++++++++++++++++-------
 1 file changed, 80 insertions(+), 20 deletions(-)

diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index 5276cdbc652f7faac13bb0244af4926b63dc119a..4f7e63c90e3d68da338b8964f08b7c65ebaf1ffd 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -59,16 +59,28 @@
  */
 #define SDHCI_CDNS_MAX_TUNING_LOOP	40
 
+struct sdhci_cdns_priv;
+
 struct sdhci_cdns_sd4_phy_param {
 	u8 addr;
 	u8 data;
 };
 
+struct sdhci_cdns_data {
+	int (*phy_init)(struct sdhci_cdns_priv *priv);
+	int (*set_tune_val)(struct sdhci_host *host, unsigned int val);
+};
+
+struct sdhci_cdns_sd4_phy {
+	unsigned int nr_phy_params;
+	struct sdhci_cdns_sd4_phy_param phy_params[];
+};
+
 struct sdhci_cdns_priv {
 	void __iomem *hrs_addr;
 	bool enhanced_strobe;
-	unsigned int nr_phy_params;
-	struct sdhci_cdns_sd4_phy_param phy_params[];
+	const struct sdhci_cdns_data *cdns_data;
+	void *phy;
 };
 
 struct sdhci_cdns_sd4_phy_cfg {
@@ -76,6 +88,13 @@ struct sdhci_cdns_sd4_phy_cfg {
 	u8 addr;
 };
 
+struct sdhci_cdns_of_data {
+	const struct sdhci_pltfm_data *pltfm_data;
+	const struct sdhci_cdns_data *cdns_data;
+	int (*phy_probe)(struct platform_device *pdev,
+			 struct sdhci_cdns_priv *priv);
+};
+
 static const struct sdhci_cdns_sd4_phy_cfg sdhci_cdns_sd4_phy_cfgs[] = {
 	{ "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
 	{ "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
@@ -135,9 +154,9 @@ static unsigned int sdhci_cdns_sd4_phy_param_count(struct device_node *np)
 }
 
 static void sdhci_cdns_sd4_phy_param_parse(struct device_node *np,
-					   struct sdhci_cdns_priv *priv)
+					   struct sdhci_cdns_sd4_phy *phy)
 {
-	struct sdhci_cdns_sd4_phy_param *p = priv->phy_params;
+	struct sdhci_cdns_sd4_phy_param *p = phy->phy_params;
 	u32 val;
 	int ret, i;
 
@@ -156,10 +175,11 @@ static void sdhci_cdns_sd4_phy_param_parse(struct device_node *np,
 static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_priv *priv)
 {
 	int ret, i;
+	struct sdhci_cdns_sd4_phy *phy = priv->phy;
 
-	for (i = 0; i < priv->nr_phy_params; i++) {
-		ret = sdhci_cdns_sd4_write_phy_reg(priv, priv->phy_params[i].addr,
-						   priv->phy_params[i].data);
+	for (i = 0; i < phy->nr_phy_params; i++) {
+		ret = sdhci_cdns_sd4_write_phy_reg(priv, phy->phy_params[i].addr,
+						   phy->phy_params[i].data);
 		if (ret)
 			return ret;
 	}
@@ -202,6 +222,27 @@ static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
 	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
 }
 
+static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
+				    struct sdhci_cdns_priv *priv)
+{
+	unsigned int nr_phy_params;
+	struct sdhci_cdns_sd4_phy *phy;
+	struct device *dev = &pdev->dev;
+
+	nr_phy_params = sdhci_cdns_sd4_phy_param_count(dev->of_node);
+	phy = devm_kzalloc(dev, struct_size(phy, phy_params, nr_phy_params),
+			   GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	phy->nr_phy_params = nr_phy_params;
+
+	sdhci_cdns_sd4_phy_param_parse(dev->of_node, phy);
+	priv->phy = phy;
+
+	return 0;
+}
+
 static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val)
 {
 	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
@@ -323,10 +364,25 @@ static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
 };
 
+static const struct sdhci_cdns_of_data sdhci_cdns_uniphier_of_data = {
+	.pltfm_data = &sdhci_cdns_uniphier_pltfm_data,
+};
+
 static const struct sdhci_pltfm_data sdhci_cdns_sd4_pltfm_data = {
 	.ops = &sdhci_cdns_sd4_ops,
 };
 
+static const struct sdhci_cdns_data sdhci_cdns_sd4_data = {
+	.phy_init = sdhci_cdns_sd4_phy_init,
+	.set_tune_val = sdhci_cdns_sd4_set_tune_val,
+};
+
+static const struct sdhci_cdns_of_data sdhci_cdns_sd4_of_data = {
+	.pltfm_data = &sdhci_cdns_sd4_pltfm_data,
+	.cdns_data = &sdhci_cdns_sd4_data,
+	.phy_probe = sdhci_cdns_sd4_phy_probe,
+};
+
 static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
 					     struct mmc_ios *ios)
 {
@@ -350,11 +406,10 @@ static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
 static int sdhci_cdns_probe(struct platform_device *pdev)
 {
 	struct sdhci_host *host;
-	const struct sdhci_pltfm_data *data;
+	const struct sdhci_cdns_of_data *data;
 	struct sdhci_pltfm_host *pltfm_host;
 	struct sdhci_cdns_priv *priv;
 	struct clk *clk;
-	unsigned int nr_phy_params;
 	int ret;
 	struct device *dev = &pdev->dev;
 	static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
@@ -368,12 +423,12 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 		return ret;
 
 	data = of_device_get_match_data(dev);
-	if (!data)
-		data = &sdhci_cdns_sd4_pltfm_data;
+	if (!data) {
+		ret = -EINVAL;
+		goto disable_clk;
+	}
 
-	nr_phy_params = sdhci_cdns_sd4_phy_param_count(dev->of_node);
-	host = sdhci_pltfm_init(pdev, data,
-				struct_size(priv, phy_params, nr_phy_params));
+	host = sdhci_pltfm_init(pdev, data->pltfm_data, sizeof(*priv));
 	if (IS_ERR(host)) {
 		ret = PTR_ERR(host);
 		goto disable_clk;
@@ -383,9 +438,9 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 	pltfm_host->clk = clk;
 
 	priv = sdhci_pltfm_priv(pltfm_host);
-	priv->nr_phy_params = nr_phy_params;
 	priv->hrs_addr = host->ioaddr;
 	priv->enhanced_strobe = false;
+	priv->cdns_data = data->cdns_data;
 	host->ioaddr += SDHCI_CDNS_SRS_BASE;
 	host->mmc_host_ops.hs400_enhanced_strobe =
 				sdhci_cdns_hs400_enhanced_strobe;
@@ -398,9 +453,11 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 	if (ret)
 		goto free;
 
-	sdhci_cdns_sd4_phy_param_parse(dev->of_node, priv);
+	ret = data->phy_probe(pdev, priv);
+	if (ret)
+		goto free;
 
-	ret = sdhci_cdns_sd4_phy_init(priv);
+	ret = priv->cdns_data->phy_init(priv);
 	if (ret)
 		goto free;
 
@@ -429,7 +486,7 @@ static int sdhci_cdns_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	ret = sdhci_cdns_sd4_phy_init(priv);
+	ret = priv->cdns_data->phy_init(priv);
 	if (ret)
 		goto disable_clk;
 
@@ -453,9 +510,12 @@ static const struct dev_pm_ops sdhci_cdns_pm_ops = {
 static const struct of_device_id sdhci_cdns_match[] = {
 	{
 		.compatible = "socionext,uniphier-sd4hc",
-		.data = &sdhci_cdns_uniphier_pltfm_data,
+		.data = &sdhci_cdns_uniphier_of_data,
+	},
+	{
+		.compatible = "cdns,sd4hc",
+		.data = &sdhci_cdns_sd4_of_data,
 	},
-	{ .compatible = "cdns,sd4hc" },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 3/6] mmc: sdhci-cadence: SD6 controller support
  2023-02-27 18:31 [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
  2023-02-27 18:31 ` [PATCH v3 1/6] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
  2023-02-27 18:31 ` [PATCH v3 2/6] mmc: sdhci-cadence: Restructure the code Piyush Malgujar
@ 2023-02-27 18:31 ` Piyush Malgujar
  2023-03-03  8:48   ` Adrian Hunter
  2023-02-27 18:31 ` [PATCH v3 4/6] mmc: sdhci-cadence: enable MMC_SDHCI_IO_ACCESSORS Piyush Malgujar
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Piyush Malgujar @ 2023-02-27 18:31 UTC (permalink / raw)
  To: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Dhananjay Kangude, Piyush Malgujar

From: Dhananjay Kangude <dkangude@cadence.com>

Add support for SD6 controller and related ops along with
support for HS400 and HS400ES emmc modes.
Updated HS200 tuning values and support to read tune configuration
from FDT and support to configure and read host side drive strength,
slew from device tree.

Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
v2 ---> v3
- Removed value stored to 'clk_wr_delay' which was never read
- Removed some whitespace changes
v1 ---> v2
- Separated out the reformating and renaming of code as
  preparation patch to add SD6 support
- Used proper property names as per dtschema
- Fixed issues related to PTR_ERR and unreachable code
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <error27@gmail.com>
---
 drivers/mmc/host/sdhci-cadence.c | 1260 +++++++++++++++++++++++++++++-
 1 file changed, 1255 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index 4f7e63c90e3d68da338b8964f08b7c65ebaf1ffd..4b32757dd3750af6428e0d4177b419b3a9e8696d 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -15,8 +15,20 @@
 
 #include "sdhci-pltfm.h"
 
+#define SDMCLK_MAX_FREQ		200000000
+
+#define DEFAULT_CMD_DELAY		16
+#define SDHCI_CDNS_TUNE_START		16
+#define SDHCI_CDNS_TUNE_STEP		6
+#define SDHCI_CDNS_TUNE_ITERATIONS	40
+
+#define SDHCI_CDNS_HRS00			0x00
+#define SDHCI_CDNS_HRS00_SWR			BIT(0)
+
+#define SDHCI_CDNS_HRS02			0x08		/* PHY access port */
+#define SDHCI_CDNS_HRS04			0x10		/* PHY access port */
+
 /* SD 4.0 Controller HRS - Host Register Set (specific to Cadence) */
-#define SDHCI_CDNS_HRS04			0x10	/* PHY access port */
 #define SDHCI_CDNS_SD4_HRS04_ACK		BIT(26)
 #define SDHCI_CDNS_SD4_HRS04_RD			BIT(25)
 #define SDHCI_CDNS_SD4_HRS04_WR			BIT(24)
@@ -29,12 +41,89 @@
 #define   SDHCI_CDNS_HRS06_TUNE			GENMASK(13, 8)
 #define   SDHCI_CDNS_HRS06_MODE			GENMASK(2, 0)
 #define   SDHCI_CDNS_HRS06_MODE_SD		0x0
+#define SDHCI_CDNS_HRS06_MODE_LEGACY		0x1
 #define   SDHCI_CDNS_HRS06_MODE_MMC_SDR		0x2
 #define   SDHCI_CDNS_HRS06_MODE_MMC_DDR		0x3
 #define   SDHCI_CDNS_HRS06_MODE_MMC_HS200	0x4
 #define   SDHCI_CDNS_HRS06_MODE_MMC_HS400	0x5
 #define   SDHCI_CDNS_HRS06_MODE_MMC_HS400ES	0x6
 
+/* SD 6.0 Controller HRS - Host Register Set (Specific to Cadence) */
+#define SDHCI_CDNS_SD6_HRS04_ADDR		GENMASK(15, 0)
+
+#define SDHCI_CDNS_HRS05			0x14
+
+#define SDHCI_CDNS_HRS07			0x1C
+#define	SDHCI_CDNS_HRS07_RW_COMPENSATE		GENMASK(20, 16)
+#define	SDHCI_CDNS_HRS07_IDELAY_VAL		GENMASK(4, 0)
+
+#define SDHCI_CDNS_HRS09			0x24
+#define	SDHCI_CDNS_HRS09_RDDATA_EN		BIT(16)
+#define	SDHCI_CDNS_HRS09_RDCMD_EN		BIT(15)
+#define	SDHCI_CDNS_HRS09_EXTENDED_WR_MODE	BIT(3)
+#define	SDHCI_CDNS_HRS09_EXTENDED_RD_MODE	BIT(2)
+#define	SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE	BIT(1)
+#define	SDHCI_CDNS_HRS09_PHY_SW_RESET		BIT(0)
+
+#define SDHCI_CDNS_HRS10			0x28
+#define	SDHCI_CDNS_HRS10_HCSDCLKADJ		GENMASK(19, 16)
+
+#define SDHCI_CDNS_HRS11			0x2c
+/*Reset related*/
+#define SDHCI_CDNS_SRS11_SW_RESET_ALL		BIT(24)
+#define SDHCI_CDNS_SRS11_SW_RESET_CMD		BIT(25)
+#define SDHCI_CDNS_SRS11_SW_RESET_DAT		BIT(26)
+
+#define SDHCI_CDNS_HRS16			0x40
+#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY	GENMASK(31, 28)
+#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY	GENMASK(27, 24)
+#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY	GENMASK(23, 20)
+#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY	GENMASK(19, 16)
+#define SDHCI_CDNS_HRS16_WRDATA1_DLY		GENMASK(15, 12)
+#define SDHCI_CDNS_HRS16_WRDATA0_DLY		GENMASK(11, 8)
+#define SDHCI_CDNS_HRS16_WRCMD1_DLY		GENMASK(7, 4)
+#define SDHCI_CDNS_HRS16_WRCMD0_DLY		GENMASK(3, 0)
+
+/* PHY registers for SD6 controller */
+#define SDHCI_CDNS_SD6_PHY_DQ_TIMING				0x2000
+#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON		BIT(31)
+#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END		GENMASK(29, 27)
+#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START		GENMASK(26, 24)
+#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END		GENMASK(2, 0)
+
+#define SDHCI_CDNS_SD6_PHY_DQS_TIMING				0x2004
+#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS		BIT(22)
+#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS		BIT(21)
+#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS		BIT(20)
+#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD		BIT(19)
+
+#define SDHCI_CDNS_SD6_PHY_GATE_LPBK				0x2008
+#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD		BIT(31)
+#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT	BIT(28)
+#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL			GENMASK(24, 19)
+#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON		BIT(6)
+
+#define SDHCI_CDNS_SD6_PHY_DLL_MASTER				0x200C
+#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_BYPASS_MODE		BIT(23)
+#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_PHASE_DETECT_SEL		GENMASK(22, 20)
+#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_LOCK_NUM		GENMASK(18, 16)
+#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_START_POINT		GENMASK(7, 0)
+
+#define SDHCI_CDNS_SD6_PHY_DLL_SLAVE				0x2010
+#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_CMD_DELAY		GENMASK(31, 24)
+#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WRDQS_DELAY		GENMASK(23, 16)
+#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY		GENMASK(15, 8)
+#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY		GENMASK(7, 0)
+
+#define SDHCI_CDNS_SD6_PHY_CTRL					0x2080
+#define	SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING		GENMASK(9, 4)
+
+#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0				0x2088
+#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV			GENMASK(6, 5)
+#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV_OVR_EN		BIT(4)
+#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW			GENMASK(2, 1)
+#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW_OVR_EN		BIT(0)
+
 /* SRS - Slot Register Set (SDHCI-compatible) */
 #define SDHCI_CDNS_SRS_BASE		0x200
 
@@ -59,6 +148,10 @@
  */
 #define SDHCI_CDNS_MAX_TUNING_LOOP	40
 
+static int tune_val_start = SDHCI_CDNS_TUNE_START;
+static int tune_val_step = SDHCI_CDNS_TUNE_STEP;
+static int max_tune_iter = SDHCI_CDNS_TUNE_ITERATIONS;
+
 struct sdhci_cdns_priv;
 
 struct sdhci_cdns_sd4_phy_param {
@@ -109,6 +202,558 @@ static const struct sdhci_cdns_sd4_phy_cfg sdhci_cdns_sd4_phy_cfgs[] = {
 	{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
 };
 
+enum sdhci_cdns_sd6_phy_lock_mode {
+	SDHCI_CDNS_SD6_PHY_LOCK_MODE_FULL_CLK = 0,
+	SDHCI_CDNS_SD6_PHY_LOCK_MODE_HALF_CLK = 2,
+	SDHCI_CDNS_SD6_PHY_LOCK_MODE_SATURATION = 3,
+};
+
+struct sdhci_cdns_sd6_phy_timings {
+	u32 t_cmd_output_min;
+	u32 t_cmd_output_max;
+	u32 t_dat_output_min;
+	u32 t_dat_output_max;
+	u32 t_cmd_input_min;
+	u32 t_cmd_input_max;
+	u32 t_dat_input_min;
+	u32 t_dat_input_max;
+	u32 t_sdclk_min;
+	u32 t_sdclk_max;
+};
+
+struct sdhci_cdns_sd6_phy_delays {
+	u32 phy_sdclk_delay;
+	u32 phy_cmd_o_delay;
+	u32 phy_dat_o_delay;
+	u32 iocell_input_delay;
+	u32 iocell_output_delay;
+	u32 delay_element_org;
+	u32 delay_element;
+};
+
+struct sdhci_cdns_sd6_phy_settings {
+	/* SDHCI_CDNS_SD6_PHY_DLL_SLAVE */
+	u32 cp_read_dqs_cmd_delay;
+	u32 cp_read_dqs_delay;
+	u32 cp_clk_wr_delay;
+	u32 cp_clk_wrdqs_delay;
+
+	/* SDHCI_CDNS_SD6_PHY_DLL_MASTER */
+	u32 cp_dll_bypass_mode;
+	u32 cp_dll_start_point;
+
+	/* SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0 */
+	u32 cp_dll_locked_mode;
+
+	/* SDHCI_CDNS_SD6_PHY_GATE_LPBK */
+	u32 cp_gate_cfg_always_on;
+	u32 cp_sync_method;
+	u32 cp_rd_del_sel;
+	u32 cp_sw_half_cycle_shift;
+	u32 cp_underrun_suppress;
+
+	/* SDHCI_CDNS_SD6_PHY_DQ_TIMING */
+	u32 cp_io_mask_always_on;
+	u32 cp_io_mask_end;
+	u32 cp_io_mask_start;
+	u32 cp_data_select_oe_end;
+
+	/* SDHCI_CDNS_SD6_PHY_DQS_TIMING */
+	u32 cp_use_ext_lpbk_dqs;
+	u32 cp_use_lpbk_dqs;
+	u8 cp_use_phony_dqs;
+	u8 cp_use_phony_dqs_cmd;
+
+	/* HRS 09 */
+	u8 sdhc_extended_rd_mode;
+	u8 sdhc_extended_wr_mode;
+	u32 sdhc_rdcmd_en;
+	u32 sdhc_rddata_en;
+
+	/* HRS10 */
+	u32 sdhc_hcsdclkadj;
+
+	/* HRS 07 */
+	u32 sdhc_idelay_val;
+	u32 sdhc_rw_compensate;
+
+	/* SRS 11 */
+	u32 sdhc_sdcfsh;
+	u32 sdhc_sdcfsl;
+
+	/* HRS 16 */
+	u32 sdhc_wrcmd0_dly;
+	u32 sdhc_wrcmd0_sdclk_dly;
+	u32 sdhc_wrcmd1_dly;
+	u32 sdhc_wrcmd1_sdclk_dly;
+	u32 sdhc_wrdata0_dly;
+	u32 sdhc_wrdata0_sdclk_dly;
+	u32 sdhc_wrdata1_dly;
+	u32 sdhc_wrdata1_sdclk_dly;
+
+	u32 hs200_tune_val;
+	u32 drive;
+	u32 slew;
+};
+
+struct sdhci_cdns_sd6_phy_intermediate_results {
+	u32 t_sdmclk_calc;
+	u32 dll_max_value;
+};
+
+struct sdhci_cdns_sd6_phy {
+	struct sdhci_cdns_sd6_phy_timings t;
+	struct sdhci_cdns_sd6_phy_delays d;
+	u32 t_sdmclk;
+	struct sdhci_cdns_sd6_phy_settings settings;
+	struct sdhci_cdns_sd6_phy_intermediate_results vars;
+	bool ddr;
+	bool tune_cmd;
+	bool tune_dat;
+	bool strobe_cmd;
+	bool strobe_dat;
+	int mode;
+	int t_sdclk;
+};
+
+static void init_hs(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 2000, .t_cmd_output_max = t_sdclk - 6000,
+		.t_dat_output_min = 2000, .t_dat_output_max = t_sdclk - 6000,
+		.t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 2500,
+		.t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 2500,
+		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_uhs_sdr12(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
+		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
+		.t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 1500,
+		.t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 1500,
+		.t_sdclk_min = 1000000 / 25, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_uhs_sdr25(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
+		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
+		.t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 1500,
+		.t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 1500,
+		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_uhs_sdr50(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
+		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
+		.t_cmd_input_min = 7500, .t_cmd_input_max = t_sdclk + 1500,
+		.t_dat_input_min = 7500, .t_dat_input_max = t_sdclk + 1500,
+		.t_sdclk_min = 1000000 / 100, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_uhs_sdr104(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400,
+		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 1400,
+		.t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000,
+		.t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000,
+		.t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100
+	};
+}
+
+static void init_uhs_ddr50(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
+		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
+		.t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 1500,
+		.t_dat_input_min = 7000, .t_dat_input_max = t_sdclk + 1500,
+		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_emmc_legacy(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000,
+		.t_dat_output_min = 3000, .t_dat_output_max = t_sdclk - 3000,
+		.t_cmd_input_min = 11700, .t_cmd_input_max = t_sdclk + 8300,
+		.t_dat_input_min = 11700, .t_dat_input_max = t_sdclk + 8300,
+		.t_sdclk_min = 1000000 / 25, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_emmc_sdr(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000,
+		.t_dat_output_min = 3000, .t_dat_output_max = t_sdclk - 3000,
+		.t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 2500,
+		.t_dat_input_min = 13700, .t_dat_input_max = t_sdclk + 2500,
+		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_emmc_ddr(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000,
+		.t_dat_output_min = 2500, .t_dat_output_max = t_sdclk - 2500,
+		.t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 2500,
+		.t_dat_input_min = 7000, .t_dat_input_max = t_sdclk + 1500,
+		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
+	};
+}
+
+static void init_emmc_hs200(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400,
+		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 1400,
+		.t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000,
+		.t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000,
+		.t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100
+	};
+}
+
+/* HS400 and HS400ES */
+static void init_emmc_hs400(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
+{
+	*t = (struct sdhci_cdns_sd6_phy_timings){
+		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400,
+		.t_dat_output_min = 400, .t_dat_output_max = t_sdclk - 400,
+		.t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000,
+		.t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000,
+		.t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100
+	};
+}
+
+static void (*init_timings[])(struct sdhci_cdns_sd6_phy_timings*, int) = {
+	&init_hs, &init_emmc_legacy, &init_emmc_sdr,
+	&init_emmc_ddr, &init_emmc_hs200, &init_emmc_hs400,
+	&init_uhs_sdr12, &init_uhs_sdr25, &init_uhs_sdr50,
+	&init_uhs_sdr104, &init_uhs_ddr50
+};
+
+static u32 read_dqs_cmd_delay, clk_wrdqs_delay, clk_wr_delay, read_dqs_delay;
+
+static u32 sdhci_cdns_sd6_get_mode(struct sdhci_host *host, unsigned int timing);
+
+static int sdhci_cdns_sd6_phy_lock_dll(struct sdhci_cdns_sd6_phy *phy)
+{
+	u32 delay_element = phy->d.delay_element_org;
+	u32 delay_elements_in_sdmclk;
+	enum sdhci_cdns_sd6_phy_lock_mode mode;
+
+	delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk, delay_element);
+	if (delay_elements_in_sdmclk > 256) {
+		delay_element *= 2;
+		delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk,
+							delay_element);
+
+		if (delay_elements_in_sdmclk > 256)
+			return -1;
+
+		mode = SDHCI_CDNS_SD6_PHY_LOCK_MODE_HALF_CLK;
+		phy->vars.dll_max_value = 127;
+	} else {
+		mode = SDHCI_CDNS_SD6_PHY_LOCK_MODE_FULL_CLK;
+		phy->vars.dll_max_value = 255;
+	}
+
+	phy->vars.t_sdmclk_calc = delay_element * delay_elements_in_sdmclk;
+	phy->d.delay_element = delay_element;
+	phy->settings.cp_dll_locked_mode = mode;
+	phy->settings.cp_dll_bypass_mode = 0;
+
+	return 0;
+}
+
+static void sdhci_cdns_sd6_phy_dll_bypass(struct sdhci_cdns_sd6_phy *phy)
+{
+	phy->vars.dll_max_value = 256;
+	phy->settings.cp_dll_bypass_mode = 1;
+	phy->settings.cp_dll_locked_mode =
+		SDHCI_CDNS_SD6_PHY_LOCK_MODE_SATURATION;
+}
+
+static void sdhci_cdns_sd6_phy_configure_dll(struct sdhci_cdns_sd6_phy *phy)
+{
+	if (phy->settings.sdhc_extended_wr_mode == 0) {
+		if (sdhci_cdns_sd6_phy_lock_dll(phy) == 0)
+			return;
+	}
+	sdhci_cdns_sd6_phy_dll_bypass(phy);
+}
+
+static void sdhci_cdns_sd6_phy_calc_out(struct sdhci_cdns_sd6_phy *phy,
+					bool cmd_not_dat)
+{
+	u32 wr0_dly = 0, wr1_dly = 0, output_min, output_max, phy_o_delay,
+	    clk_wr_delay = 0, wr0_sdclk_dly = 0, wr1_sdclk_dly = 0;
+	bool data_ddr = phy->ddr && !cmd_not_dat;
+	int t;
+
+	if (cmd_not_dat) {
+		output_min = phy->t.t_cmd_output_min;
+		output_max = phy->t.t_cmd_output_max;
+		phy_o_delay = phy->d.phy_cmd_o_delay;
+	} else {
+		output_min = phy->t.t_dat_output_min;
+		output_max = phy->t.t_dat_output_max;
+		phy_o_delay = phy->d.phy_dat_o_delay;
+	}
+
+	if (data_ddr) {
+		wr0_sdclk_dly = 1;
+		wr1_sdclk_dly = 1;
+	}
+
+	t = phy_o_delay - phy->d.phy_sdclk_delay - output_min;
+	if (t < 0 && phy->settings.sdhc_extended_wr_mode == 1) {
+		u32 n_half_cycle = DIV_ROUND_UP(-t * 2, phy->t_sdmclk);
+
+		wr0_dly = (n_half_cycle + 1) / 2;
+		if (data_ddr)
+			wr1_dly = (n_half_cycle + 1) / 2;
+		else
+			wr1_dly = (n_half_cycle + 1) % 2 + wr0_dly - 1;
+	}
+
+	if (phy->settings.sdhc_extended_wr_mode == 0) {
+		u32 out_hold, out_setup, out_hold_margin;
+		u32 n;
+
+		if (!data_ddr)
+			wr0_dly = 1;
+
+		out_setup = output_max;
+		out_hold = output_min;
+		out_hold_margin = DIV_ROUND_UP(out_setup - out_hold, 4);
+		out_hold += out_hold_margin;
+
+		if (phy->settings.cp_dll_bypass_mode == 0)
+			n = DIV_ROUND_UP(256 * out_hold, phy->vars.t_sdmclk_calc);
+		else
+			n = DIV_ROUND_UP(out_hold, phy->d.delay_element) - 1;
+
+		if (n <= phy->vars.dll_max_value)
+			clk_wr_delay = n;
+		else
+			clk_wr_delay = 255;
+	} else {
+		/*  sdhc_extended_wr_mode = 1 - PHY IO cell work in SDR mode */
+		clk_wr_delay = 0;
+	}
+
+	if (cmd_not_dat) {
+		phy->settings.sdhc_wrcmd0_dly = wr0_dly;
+		phy->settings.sdhc_wrcmd1_dly = wr1_dly;
+		phy->settings.cp_clk_wrdqs_delay = clk_wr_delay;
+		phy->settings.sdhc_wrcmd0_sdclk_dly = wr0_sdclk_dly;
+		phy->settings.sdhc_wrcmd1_sdclk_dly = wr1_sdclk_dly;
+	} else {
+		phy->settings.sdhc_wrdata0_dly = wr0_dly;
+		phy->settings.sdhc_wrdata1_dly = wr1_dly;
+		phy->settings.cp_clk_wr_delay = clk_wr_delay;
+		phy->settings.sdhc_wrdata0_sdclk_dly = wr0_sdclk_dly;
+		phy->settings.sdhc_wrdata1_sdclk_dly = wr1_sdclk_dly;
+	}
+}
+
+static void sdhci_cdns_sd6_phy_calc_cmd_out(struct sdhci_cdns_sd6_phy *phy)
+{
+	sdhci_cdns_sd6_phy_calc_out(phy, true);
+}
+
+static void sdhci_cdns_sd6_phy_calc_cmd_in(struct sdhci_cdns_sd6_phy *phy)
+{
+	phy->settings.cp_io_mask_end =
+		((phy->d.iocell_output_delay + phy->d.iocell_input_delay) * 2)
+		/ phy->t_sdmclk;
+
+	if (phy->settings.cp_io_mask_end >= 8)
+		phy->settings.cp_io_mask_end = 7;
+
+	if (phy->strobe_cmd && phy->settings.cp_io_mask_end > 0)
+		phy->settings.cp_io_mask_end--;
+
+	if (phy->strobe_cmd) {
+		phy->settings.cp_use_phony_dqs_cmd = 0;
+		phy->settings.cp_read_dqs_cmd_delay = 64;
+	} else {
+		phy->settings.cp_use_phony_dqs_cmd = 1;
+		phy->settings.cp_read_dqs_cmd_delay = 0;
+	}
+
+	if ((phy->mode == MMC_TIMING_MMC_HS400 && !phy->strobe_cmd) ||
+	    phy->mode == MMC_TIMING_MMC_HS200)
+		phy->settings.cp_read_dqs_cmd_delay =
+			phy->settings.hs200_tune_val;
+}
+
+static void sdhci_cdns_sd6_phy_calc_dat_in(struct sdhci_cdns_sd6_phy *phy)
+{
+	u32 hcsdclkadj = 0;
+
+	if (phy->strobe_dat) {
+		phy->settings.cp_use_phony_dqs = 0;
+		phy->settings.cp_read_dqs_delay = 64;
+	} else {
+		phy->settings.cp_use_phony_dqs = 1;
+		phy->settings.cp_read_dqs_delay = 0;
+	}
+
+	if (phy->mode == MMC_TIMING_MMC_HS200)
+		phy->settings.cp_read_dqs_delay =
+			phy->settings.hs200_tune_val;
+
+	if (phy->strobe_dat) {
+		/* dqs loopback input via IO cell */
+		hcsdclkadj += phy->d.iocell_input_delay;
+		/* dfi_dqs_in: mem_dqs -> clean_dqs_mod; delay of hic_dll_dqs_nand2 */
+		hcsdclkadj += phy->d.delay_element / 2;
+		/* delay line */
+		hcsdclkadj += phy->t_sdclk / 2;
+		/* PHY FIFO write pointer */
+		hcsdclkadj += phy->t_sdclk / 2 + phy->d.delay_element;
+		/* 1st synchronizer */
+		hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk)
+			* phy->t_sdmclk - hcsdclkadj;
+		/*
+		 * 2nd synchronizer + PHY FIFO read pointer + PHY rddata
+		 * + PHY rddata registered, + FIFO 1st ciu_en
+		 */
+		hcsdclkadj += 5 * phy->t_sdmclk;
+		/* FIFO 2st ciu_en */
+		hcsdclkadj += phy->t_sdclk;
+
+		hcsdclkadj /= phy->t_sdclk;
+	} else {
+		u32 n;
+
+		/* rebar PHY delay */
+		hcsdclkadj += 2 * phy->t_sdmclk;
+		/* rebar output via IO cell */
+		hcsdclkadj += phy->d.iocell_output_delay;
+		/* dqs loopback input via IO cell */
+		hcsdclkadj += phy->d.iocell_input_delay;
+		/* dfi_dqs_in: mem_dqs -> clean_dqs_mod delay of hic_dll_dqs_nand2 */
+		hcsdclkadj += phy->d.delay_element / 2;
+		/* dll: one delay element between SIGI_0 and SIGO_0 */
+		hcsdclkadj += phy->d.delay_element;
+		/* dfi_dqs_in: mem_dqs_delayed -> clk_dqs delay of hic_dll_dqs_nand2 */
+		hcsdclkadj += phy->d.delay_element / 2;
+		/* deskew DLL: clk_dqs -> clk_dqN: one delay element */
+		hcsdclkadj += phy->d.delay_element;
+
+		if (phy->t_sdclk == phy->t_sdmclk)
+			n = (hcsdclkadj - 2 * phy->t_sdmclk) / phy->t_sdclk;
+		else
+			n = hcsdclkadj / phy->t_sdclk;
+
+		/* phase shift within one t_sdclk clock cycle caused by rebar - lbk dqs delay */
+		hcsdclkadj = hcsdclkadj % phy->t_sdclk;
+		/* PHY FIFO write pointer */
+		hcsdclkadj += phy->t_sdclk / 2;
+		/* 1st synchronizer */
+		hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk)
+			* phy->t_sdmclk - hcsdclkadj;
+		/*
+		 * 2nd synchronizer + PHY FIFO read pointer + PHY rddata
+		 * + PHY rddata registered
+		 */
+		hcsdclkadj += 4 * phy->t_sdmclk;
+
+		if ((phy->t_sdclk / phy->t_sdmclk) > 1) {
+			u32 tmp1, tmp2;
+
+			tmp1 = hcsdclkadj;
+			tmp2 = (hcsdclkadj / phy->t_sdclk) * phy->t_sdclk
+				+ phy->t_sdclk - phy->t_sdmclk;
+			if (tmp1 == tmp2)
+				tmp2 += phy->t_sdclk;
+
+			/* FIFO aligns to clock cycle before ciu_en */
+			hcsdclkadj += tmp2 - tmp1;
+		}
+
+		/* FIFO 1st ciu_en */
+		hcsdclkadj += phy->t_sdmclk;
+		/* FIFO 2nd ciu_en */
+		hcsdclkadj += phy->t_sdclk;
+
+		hcsdclkadj /= phy->t_sdclk;
+
+		hcsdclkadj += n;
+
+		if ((phy->t_sdclk / phy->t_sdmclk) >= 2) {
+			if (phy->mode == MMC_TIMING_UHS_DDR50 ||
+			    phy->mode == MMC_TIMING_MMC_DDR52)
+				hcsdclkadj -= 2;
+			else
+				hcsdclkadj -= 1;
+		} else if ((phy->t_sdclk / phy->t_sdmclk) == 1) {
+			hcsdclkadj += 2;
+		}
+
+		if (phy->tune_dat)
+			hcsdclkadj -= 1;
+	}
+
+	if (hcsdclkadj > 15)
+		hcsdclkadj = 15;
+
+	phy->settings.sdhc_hcsdclkadj = hcsdclkadj;
+}
+
+static void sdhci_cdns_sd6_phy_calc_dat_out(struct sdhci_cdns_sd6_phy *phy)
+{
+	sdhci_cdns_sd6_phy_calc_out(phy, false);
+}
+
+static void sdhci_cdns_sd6_phy_calc_io(struct sdhci_cdns_sd6_phy *phy)
+{
+	u32 rw_compensate;
+
+	rw_compensate = (phy->d.iocell_input_delay + phy->d.iocell_output_delay)
+		/ phy->t_sdmclk + phy->settings.sdhc_wrdata0_dly + 5 + 3;
+
+	phy->settings.sdhc_idelay_val = (2 * phy->d.iocell_input_delay)
+		/ phy->t_sdmclk;
+
+	phy->settings.cp_io_mask_start = 0;
+	if (phy->t_sdclk == phy->t_sdmclk && rw_compensate > 10)
+		phy->settings.cp_io_mask_start = 2 * (rw_compensate - 10);
+
+	if (phy->mode == MMC_TIMING_UHS_SDR104)
+		phy->settings.cp_io_mask_start++;
+
+	if (phy->t_sdclk == phy->t_sdmclk && phy->mode == MMC_TIMING_UHS_SDR50)
+		phy->settings.cp_io_mask_start++;
+
+	phy->settings.sdhc_rw_compensate = rw_compensate;
+}
+
+static void sdhci_cdns_sd6_phy_calc_settings(struct sdhci_cdns_sd6_phy *phy)
+{
+	sdhci_cdns_sd6_phy_calc_cmd_out(phy);
+	sdhci_cdns_sd6_phy_calc_cmd_in(phy);
+	sdhci_cdns_sd6_phy_calc_dat_out(phy);
+	sdhci_cdns_sd6_phy_calc_dat_in(phy);
+	sdhci_cdns_sd6_phy_calc_io(phy);
+}
+
 static int sdhci_cdns_sd4_write_phy_reg(struct sdhci_cdns_priv *priv,
 					u8 addr, u8 data)
 {
@@ -183,7 +828,276 @@ static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_priv *priv)
 		if (ret)
 			return ret;
 	}
+	return 0;
+}
+
+static u32 sdhci_cdns_sd6_read_phy_reg(struct sdhci_cdns_priv *priv,
+				       u32 addr)
+{
+	writel(FIELD_PREP(SDHCI_CDNS_SD6_HRS04_ADDR, addr),
+	       priv->hrs_addr + SDHCI_CDNS_HRS04);
+	return readl(priv->hrs_addr + SDHCI_CDNS_HRS05);
+}
+
+static void sdhci_cdns_sd6_write_phy_reg(struct sdhci_cdns_priv *priv,
+					 u32 addr, u32 data)
+{
+	writel(FIELD_PREP(SDHCI_CDNS_SD6_HRS04_ADDR, addr),
+	       priv->hrs_addr + SDHCI_CDNS_HRS04);
+	writel(data, priv->hrs_addr + SDHCI_CDNS_HRS05);
+}
+
+static int sdhci_cdns_sd6_dll_reset(struct sdhci_cdns_priv *priv, bool reset)
+{
+	u32 reg;
+	int ret = 0;
+
+	reg = readl(priv->hrs_addr + SDHCI_CDNS_HRS09);
+	if (reset)
+		reg &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
+	else
+		reg |= SDHCI_CDNS_HRS09_PHY_SW_RESET;
+
+	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS09);
+
+	if (!reset)
+		ret = readl_poll_timeout(priv->hrs_addr + SDHCI_CDNS_HRS09,
+					 reg,
+					 (reg &
+					  SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE),
+					 0, 0);
+
+	return ret;
+}
+
+static void sdhci_cdns_sd6_calc_phy(struct sdhci_cdns_sd6_phy *phy)
+{
+	if (phy->mode == MMC_TIMING_MMC_HS) {
+		phy->settings.cp_clk_wr_delay = 0;
+		phy->settings.cp_clk_wrdqs_delay = 0;
+		phy->settings.cp_data_select_oe_end = 1;
+		phy->settings.cp_dll_bypass_mode = 1;
+		phy->settings.cp_dll_locked_mode = 3;
+		phy->settings.cp_dll_start_point = 4;
+		phy->settings.cp_gate_cfg_always_on = 1;
+		phy->settings.cp_io_mask_always_on = 0;
+		phy->settings.cp_io_mask_end = 0;
+		phy->settings.cp_io_mask_start = 0;
+		phy->settings.cp_rd_del_sel = 52;
+		phy->settings.cp_read_dqs_cmd_delay = 0;
+		phy->settings.cp_read_dqs_delay = 0;
+		phy->settings.cp_sw_half_cycle_shift = 0;
+		phy->settings.cp_sync_method = 1;
+		phy->settings.cp_underrun_suppress = 1;
+		phy->settings.cp_use_ext_lpbk_dqs = 1;
+		phy->settings.cp_use_lpbk_dqs = 1;
+		phy->settings.cp_use_phony_dqs = 1;
+		phy->settings.cp_use_phony_dqs_cmd = 1;
+		phy->settings.sdhc_extended_rd_mode = 1;
+		phy->settings.sdhc_extended_wr_mode = 1;
+		phy->settings.sdhc_hcsdclkadj = 2;
+		phy->settings.sdhc_idelay_val = 0;
+		phy->settings.sdhc_rdcmd_en = 1;
+		phy->settings.sdhc_rddata_en = 1;
+		phy->settings.sdhc_rw_compensate = 9;
+		phy->settings.sdhc_sdcfsh = 0;
+		phy->settings.sdhc_sdcfsl = 4;
+		phy->settings.sdhc_wrcmd0_dly = 1;
+		phy->settings.sdhc_wrcmd0_sdclk_dly = 0;
+		phy->settings.sdhc_wrcmd1_dly = 0;
+		phy->settings.sdhc_wrcmd1_sdclk_dly = 0;
+		phy->settings.sdhc_wrdata0_dly = 1;
+		phy->settings.sdhc_wrdata0_sdclk_dly = 0;
+		phy->settings.sdhc_wrdata1_dly = 0;
+		phy->settings.sdhc_wrdata1_sdclk_dly = 0;
+	}
+}
+
+static
+int sdhci_cdns_sd6_get_delay_params(struct device *dev,
+				    struct sdhci_cdns_priv *priv)
+{
+	struct sdhci_cdns_sd6_phy *phy = priv->phy;
+	int ret;
+
+	of_property_read_u32(dev->of_node, "cdns,iocell-input-delay-ps",
+			     &phy->d.iocell_input_delay);
+	of_property_read_u32(dev->of_node, "cdns,iocell-output-delay-ps",
+			     &phy->d.iocell_output_delay);
+	of_property_read_u32(dev->of_node, "cdns,delay-element-ps",
+			     &phy->d.delay_element);
+	ret = of_property_read_u32(dev->of_node, "cdns,read-dqs-cmd-delay-ps",
+				   &phy->settings.cp_read_dqs_cmd_delay);
+	if (ret)
+		phy->settings.cp_read_dqs_cmd_delay = DEFAULT_CMD_DELAY;
+
+	ret = of_property_read_u32(dev->of_node, "cdns,tune-val-start-ps",
+				   &tune_val_start);
+	if (ret)
+		tune_val_start = SDHCI_CDNS_TUNE_START;
+
+	ret = of_property_read_u32(dev->of_node, "cdns,tune-val-step-ps",
+				   &tune_val_step);
+	if (ret)
+		tune_val_step = SDHCI_CDNS_TUNE_STEP;
+
+	read_dqs_cmd_delay = phy->settings.cp_read_dqs_cmd_delay;
+	clk_wrdqs_delay = phy->settings.cp_clk_wrdqs_delay;
+	clk_wr_delay = phy->settings.cp_clk_wr_delay;
+	read_dqs_delay = phy->settings.cp_read_dqs_delay;
+	return 0;
+}
+
+static int sdhci_cdns_sd6_phy_init(struct sdhci_cdns_priv *priv)
+{
+	int ret;
+	u32 reg;
+	struct sdhci_cdns_sd6_phy *phy = priv->phy;
+
+	sdhci_cdns_sd6_dll_reset(priv, true);
+
+	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQS_TIMING);
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS;
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS;
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS;
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD;
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS,
+			phy->settings.cp_use_ext_lpbk_dqs);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS,
+			phy->settings.cp_use_lpbk_dqs);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS,
+			  phy->settings.cp_use_phony_dqs);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD,
+			  phy->settings.cp_use_phony_dqs_cmd);
+	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQS_TIMING, reg);
+
+	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GATE_LPBK);
+	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD;
+	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT;
+	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL;
+	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON;
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD,
+			phy->settings.cp_sync_method);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT,
+			phy->settings.cp_sw_half_cycle_shift);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL,
+			phy->settings.cp_rd_del_sel);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON,
+			phy->settings.cp_gate_cfg_always_on);
+	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GATE_LPBK, reg);
+
+	reg = 0x0;
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_BYPASS_MODE,
+			 phy->settings.cp_dll_bypass_mode);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_PHASE_DETECT_SEL, 2);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_LOCK_NUM, 0);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_START_POINT,
+			phy->settings.cp_dll_start_point);
+	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DLL_MASTER, reg);
+
+	reg = 0x0;
+	reg = FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_CMD_DELAY,
+			 phy->settings.cp_read_dqs_cmd_delay);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WRDQS_DELAY,
+			  phy->settings.cp_clk_wrdqs_delay);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY,
+			  phy->settings.cp_clk_wr_delay);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY,
+			  phy->settings.cp_read_dqs_delay);
+	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DLL_SLAVE, reg);
+
+	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_CTRL);
+	reg &= ~SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING;
+	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_CTRL, reg);
+
+	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0);
+	reg &= ~0x77;
+	reg |= SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV_OVR_EN |
+		SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW_OVR_EN;
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV,
+		phy->settings.drive);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW,
+		phy->settings.slew);
+	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0, reg);
+
+	ret = sdhci_cdns_sd6_dll_reset(priv, false);
+	if (ret)
+		return ret;
+
+	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQ_TIMING);
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON;
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END;
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START;
+	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END;
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON,
+			phy->settings.cp_io_mask_always_on);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END,
+			  phy->settings.cp_io_mask_end);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START,
+			  phy->settings.cp_io_mask_start);
+	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END,
+			phy->settings.cp_data_select_oe_end);
+	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQ_TIMING, reg);
+
+	reg = readl(priv->hrs_addr + SDHCI_CDNS_HRS09);
+	if (phy->settings.sdhc_extended_wr_mode)
+		reg |= SDHCI_CDNS_HRS09_EXTENDED_WR_MODE;
+	else
+		reg &= ~SDHCI_CDNS_HRS09_EXTENDED_WR_MODE;
+
+	if (phy->settings.sdhc_extended_rd_mode)
+		reg |= SDHCI_CDNS_HRS09_EXTENDED_RD_MODE;
+	else
+		reg &= ~SDHCI_CDNS_HRS09_EXTENDED_RD_MODE;
+
+	if (phy->settings.sdhc_rddata_en)
+		reg |= SDHCI_CDNS_HRS09_RDDATA_EN;
+	else
+		reg &= ~SDHCI_CDNS_HRS09_RDDATA_EN;
+
+	if (phy->settings.sdhc_rdcmd_en)
+		reg |= SDHCI_CDNS_HRS09_RDCMD_EN;
+	else
+		reg &= ~SDHCI_CDNS_HRS09_RDCMD_EN;
+
+	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS09);
+
+	writel(0x30004, priv->hrs_addr + SDHCI_CDNS_HRS02);
+
+	reg = 0x0;
+	reg = FIELD_PREP(SDHCI_CDNS_HRS10_HCSDCLKADJ, phy->settings.sdhc_hcsdclkadj);
+	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS10);
+
+	if (phy->mode != MMC_TIMING_MMC_HS && phy->mode != MMC_TIMING_MMC_DDR52) {
+		reg = 0x0;
+		reg = FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY,
+				 phy->settings.sdhc_wrdata1_sdclk_dly);
+		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY,
+				  phy->settings.sdhc_wrdata0_sdclk_dly);
+		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY,
+				  phy->settings.sdhc_wrcmd1_sdclk_dly);
+		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY,
+				  phy->settings.sdhc_wrcmd0_sdclk_dly);
+		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_DLY,
+				  phy->settings.sdhc_wrdata1_dly);
+		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_DLY,
+				  phy->settings.sdhc_wrdata0_dly);
+		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_DLY,
+				  phy->settings.sdhc_wrcmd1_dly);
+		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_DLY,
+				  phy->settings.sdhc_wrcmd0_dly);
+	} else {
+		reg = 0x202;
+	}
 
+	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS16);
+
+	reg = 0x0;
+	reg = FIELD_PREP(SDHCI_CDNS_HRS07_RW_COMPENSATE,
+			 phy->settings.sdhc_rw_compensate);
+	reg |= FIELD_PREP(SDHCI_CDNS_HRS07_IDELAY_VAL,
+			 phy->settings.sdhc_idelay_val);
+	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS07);
 	return 0;
 }
 
@@ -194,6 +1108,19 @@ static void *sdhci_cdns_priv(struct sdhci_host *host)
 	return sdhci_pltfm_priv(pltfm_host);
 }
 
+static int sdhci_cdns_sd6_set_tune_val(struct sdhci_host *host,
+				       unsigned int val)
+{
+	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+	struct sdhci_cdns_sd6_phy *phy = priv->phy;
+
+	phy->settings.hs200_tune_val = val;
+	phy->settings.cp_read_dqs_cmd_delay = val;
+	phy->settings.cp_read_dqs_delay = val;
+
+	return sdhci_cdns_sd6_phy_init(priv);
+}
+
 static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
 {
 	/*
@@ -203,6 +1130,11 @@ static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
 	return host->max_clk;
 }
 
+static unsigned int sdhci_cdns_get_max_clock(struct sdhci_host *host)
+{
+	return SDMCLK_MAX_FREQ;
+}
+
 static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
 {
 	u32 tmp;
@@ -222,6 +1154,118 @@ static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
 	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
 }
 
+static int sdhci_cdns_sd6_phy_update_timings(struct sdhci_host *host)
+{
+	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+	struct sdhci_cdns_sd6_phy *phy = priv->phy;
+	int t_sdmclk = phy->t_sdmclk;
+	int mode;
+
+	mode = sdhci_cdns_sd6_get_mode(host, host->mmc->ios.timing);
+	/* initialize input */
+	init_timings[mode](&phy->t, phy->t_sdclk);
+
+	phy->mode = host->mmc->ios.timing;
+	phy->strobe_dat = false;
+
+	switch (phy->mode) {
+	case MMC_TIMING_UHS_SDR104:
+		phy->tune_cmd = true;
+		phy->tune_dat = true;
+		break;
+	case MMC_TIMING_UHS_DDR50:
+		phy->ddr = true;
+		break;
+	case MMC_TIMING_MMC_DDR52:
+		phy->ddr = true;
+		break;
+	case MMC_TIMING_MMC_HS200:
+		phy->tune_dat = true;
+		phy->tune_cmd = true;
+		break;
+	case MMC_TIMING_MMC_HS400:
+		phy->tune_cmd = true;
+		phy->ddr = true;
+		phy->strobe_dat = true;
+		break;
+	}
+
+	if (priv->enhanced_strobe)
+		phy->strobe_cmd = true;
+
+	phy->d.phy_sdclk_delay = 2 * t_sdmclk;
+	phy->d.phy_cmd_o_delay = 2 * t_sdmclk + t_sdmclk / 2;
+	phy->d.phy_dat_o_delay = 2 * t_sdmclk + t_sdmclk / 2;
+
+	if (phy->t_sdclk == phy->t_sdmclk) {
+		phy->settings.sdhc_extended_wr_mode = 0;
+		phy->settings.sdhc_extended_rd_mode = 0;
+	} else {
+		phy->settings.sdhc_extended_wr_mode = 1;
+		phy->settings.sdhc_extended_rd_mode = 1;
+	}
+
+	phy->settings.cp_gate_cfg_always_on = 1;
+
+	sdhci_cdns_sd6_phy_configure_dll(phy);
+
+	sdhci_cdns_sd6_phy_calc_settings(phy);
+
+	return 0;
+}
+
+static u32 sdhci_cdns_sd6_get_mode(struct sdhci_host *host,
+				   unsigned int timing)
+{
+	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+	u32 mode;
+
+	switch (timing) {
+	case MMC_TIMING_MMC_HS:
+		mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
+		break;
+	case MMC_TIMING_MMC_DDR52:
+		mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
+		break;
+	case MMC_TIMING_MMC_HS200:
+		mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
+		break;
+	case MMC_TIMING_MMC_HS400:
+		if (priv->enhanced_strobe)
+			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES;
+		else
+			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
+		break;
+	case MMC_TIMING_SD_HS:
+		mode = SDHCI_CDNS_HRS06_MODE_SD;
+		break;
+	default:
+		mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
+		break;
+	}
+
+	return mode;
+}
+
+static void sdhci_cdns_sd6_set_clock(struct sdhci_host *host,
+				     unsigned int clock)
+{
+	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+	struct sdhci_cdns_sd6_phy *phy = priv->phy;
+
+	phy->t_sdclk = DIV_ROUND_DOWN_ULL(1e12, clock);
+
+	pr_debug("%s %d %d\n", __func__, phy->mode, clock);
+
+	if (sdhci_cdns_sd6_phy_update_timings(host))
+		pr_debug("%s: update timings failed\n", __func__);
+
+	if (sdhci_cdns_sd6_phy_init(priv))
+		pr_debug("%s: phy init failed\n", __func__);
+
+	sdhci_set_clock(host, clock);
+}
+
 static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
 				    struct sdhci_cdns_priv *priv)
 {
@@ -243,6 +1287,106 @@ static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
 	return 0;
 }
 
+static int sdhci_cdns_sd6_phy_probe(struct platform_device *pdev,
+				    struct sdhci_cdns_priv *priv)
+{
+	struct device *dev = &pdev->dev;
+	struct sdhci_cdns_sd6_phy *phy;
+	u32 val;
+	struct clk *clk;
+	int ret;
+	const char *mode_name;
+
+	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	clk = devm_clk_get(dev, "sdmclk");
+	if (IS_ERR(clk)) {
+		dev_err(dev, "sdmclk get error\n");
+		return PTR_ERR(clk);
+	}
+
+	val = clk_get_rate(clk);
+	phy->t_sdmclk = DIV_ROUND_DOWN_ULL(1e12, val);
+
+	ret = of_property_read_u32(dev->of_node, "cdns,host_slew",
+				   &phy->settings.slew);
+	if (ret)
+		phy->settings.slew = 3;
+
+	ret = of_property_read_u32(dev->of_node, "cdns,host_drive",
+				   &phy->settings.drive);
+	if (ret)
+		phy->settings.drive = 2;
+
+	ret = of_property_read_u32(dev->of_node, "cdns,iocell-input-delay",
+				   &phy->d.iocell_input_delay);
+	if (ret)
+		phy->d.iocell_input_delay = 2500;
+
+	ret = of_property_read_u32(dev->of_node, "cdns,iocell-output-delay",
+				   &phy->d.iocell_output_delay);
+	if (ret)
+		phy->d.iocell_output_delay = 2500;
+
+	ret = of_property_read_u32(dev->of_node, "cdns,delay-element",
+				   &phy->d.delay_element);
+	if (ret)
+		phy->d.delay_element = 24;
+
+	ret = of_property_read_string_index(dev->of_node, "cdns,mode", 0,
+					    &mode_name);
+	if (!ret) {
+		if (!strcmp("emmc_sdr", mode_name))
+			phy->mode = MMC_TIMING_MMC_HS;
+		else if (!strcmp("emmc_ddr", mode_name))
+			phy->mode = MMC_TIMING_MMC_DDR52;
+		else if (!strcmp("emmc_hs200", mode_name))
+			phy->mode = MMC_TIMING_MMC_HS200;
+		else if (!strcmp("emmc_hs400", mode_name))
+			phy->mode = MMC_TIMING_MMC_HS400;
+		else if (!strcmp("sd_hs", mode_name))
+			phy->mode = MMC_TIMING_SD_HS;
+		else
+			phy->mode = MMC_TIMING_MMC_HS;
+	} else {
+		phy->mode = MMC_TIMING_MMC_HS;
+	}
+
+	phy->d.delay_element_org = phy->d.delay_element;
+	phy->d.iocell_input_delay = 650;
+	phy->d.iocell_output_delay = 1800;
+
+	switch (phy->mode) {
+	case MMC_TIMING_MMC_HS:
+		phy->t_sdclk =  10000;
+		break;
+	case MMC_TIMING_MMC_DDR52:
+		phy->t_sdclk = 10000;
+		break;
+	case MMC_TIMING_MMC_HS200:
+		phy->t_sdclk = 5000;
+		break;
+	case MMC_TIMING_MMC_HS400:
+		phy->t_sdclk = 5000;
+		break;
+	case MMC_TIMING_SD_HS:
+		phy->t_sdclk = 100000;
+		break;
+	default:
+		phy->t_sdclk = 10000;
+		break;
+	}
+
+	priv->phy = phy;
+
+	sdhci_cdns_sd6_get_delay_params(dev, priv);
+
+	sdhci_cdns_sd6_calc_phy(phy);
+	return 0;
+}
+
 static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val)
 {
 	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
@@ -276,6 +1420,58 @@ static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val
 	return 0;
 }
 
+/*
+ * In SD mode, software must not use the hardware tuning and instead perform
+ * an almost identical procedure to eMMC.
+ */
+static int sdhci_cdns_sd6_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+	int cur_streak = 0;
+	int max_streak = 0;
+	int end_of_streak = 0;
+	int i, midpoint, iter = 0;
+
+	/*
+	 * Do not execute tuning for UHS_SDR50 or UHS_DDR50.
+	 * The delay is set by probe, based on the DT properties.
+	 */
+	if (host->timing != MMC_TIMING_MMC_HS200 &&
+	    host->timing != MMC_TIMING_UHS_SDR104)
+		return 0;
+
+	for (i = tune_val_start; iter < max_tune_iter; iter++, i += tune_val_step) {
+		if (priv->cdns_data->set_tune_val(host, i) ||
+		    mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
+			cur_streak = 0;
+		} else { /* good */
+			cur_streak++;
+			if (cur_streak > max_streak) {
+				max_streak = cur_streak;
+				end_of_streak = i;
+				pr_debug("%s (%d-%d = %d)", __func__,
+					 end_of_streak - ((cur_streak - 1) * tune_val_step),
+					 end_of_streak, cur_streak);
+			} else {
+				pr_debug("%s (%d-%d)", __func__,
+					 i - ((cur_streak - 1) * tune_val_step), i);
+			}
+		}
+	}
+
+	if (!max_streak) {
+		dev_err(mmc_dev(host->mmc), "no tuning point found\n");
+		return -EIO;
+	}
+
+	pr_debug("max_streak: %d-%d", end_of_streak - ((max_streak - 1) * tune_val_step),
+		 end_of_streak);
+
+	midpoint = end_of_streak - (((max_streak - 1) * tune_val_step) / 2);
+
+	return priv->cdns_data->set_tune_val(host, midpoint);
+}
+
 /*
  * In SD mode, software must not use the hardware tuning and instead perform
  * an almost identical procedure to eMMC.
@@ -338,11 +1534,15 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
 		else
 			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
 		break;
-	default:
+	case MMC_TIMING_SD_HS:
 		mode = SDHCI_CDNS_HRS06_MODE_SD;
 		break;
+	default:
+		mode = SDHCI_CDNS_HRS06_MODE_LEGACY;
+		break;
 	}
 
+	pr_debug("%s mode %d timing %d\n", __func__, mode, timing);
 	sdhci_cdns_set_emmc_mode(priv, mode);
 
 	/* For SD, fall back to the default handler */
@@ -350,6 +1550,24 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
 		sdhci_set_uhs_signaling(host, timing);
 }
 
+static void sdhci_cdns_sd6_set_uhs_signaling(struct sdhci_host *host,
+					     unsigned int timing)
+{
+	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+	struct sdhci_cdns_sd6_phy *phy = priv->phy;
+
+	sdhci_cdns_set_uhs_signaling(host, timing);
+
+	if ((phy->mode == -1) || (phy->t_sdclk == -1))
+		return;
+
+	if (sdhci_cdns_sd6_phy_update_timings(host))
+		pr_debug("%s: update timings failed\n", __func__);
+
+	if (sdhci_cdns_sd6_phy_init(priv))
+		pr_debug("%s: phy init failed\n", __func__);
+}
+
 static const struct sdhci_ops sdhci_cdns_sd4_ops = {
 	.set_clock = sdhci_set_clock,
 	.get_timeout_clock = sdhci_cdns_get_timeout_clock,
@@ -359,6 +1577,16 @@ static const struct sdhci_ops sdhci_cdns_sd4_ops = {
 	.set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
 };
 
+static const struct sdhci_ops sdhci_cdns_sd6_ops = {
+	.get_max_clock = sdhci_cdns_get_max_clock,
+	.set_clock = sdhci_cdns_sd6_set_clock,
+	.get_timeout_clock = sdhci_cdns_get_timeout_clock,
+	.set_bus_width = sdhci_set_bus_width,
+	.reset = sdhci_reset,
+	.platform_execute_tuning = sdhci_cdns_sd6_execute_tuning,
+	.set_uhs_signaling = sdhci_cdns_sd6_set_uhs_signaling,
+};
+
 static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
 	.ops = &sdhci_cdns_sd4_ops,
 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
@@ -372,17 +1600,32 @@ static const struct sdhci_pltfm_data sdhci_cdns_sd4_pltfm_data = {
 	.ops = &sdhci_cdns_sd4_ops,
 };
 
+static const struct sdhci_pltfm_data sdhci_cdns_sd6_pltfm_data = {
+	.ops = &sdhci_cdns_sd6_ops,
+};
+
 static const struct sdhci_cdns_data sdhci_cdns_sd4_data = {
 	.phy_init = sdhci_cdns_sd4_phy_init,
 	.set_tune_val = sdhci_cdns_sd4_set_tune_val,
 };
 
+static const struct sdhci_cdns_data sdhci_cdns_sd6_data = {
+	.phy_init = sdhci_cdns_sd6_phy_init,
+	.set_tune_val = sdhci_cdns_sd6_set_tune_val,
+};
+
 static const struct sdhci_cdns_of_data sdhci_cdns_sd4_of_data = {
 	.pltfm_data = &sdhci_cdns_sd4_pltfm_data,
 	.cdns_data = &sdhci_cdns_sd4_data,
 	.phy_probe = sdhci_cdns_sd4_phy_probe,
 };
 
+static const struct sdhci_cdns_of_data sdhci_cdns_sd6_of_data = {
+	.pltfm_data = &sdhci_cdns_sd6_pltfm_data,
+	.cdns_data = &sdhci_cdns_sd6_data,
+	.phy_probe = sdhci_cdns_sd6_phy_probe,
+};
+
 static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
 					     struct mmc_ios *ios)
 {
@@ -412,7 +1655,6 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 	struct clk *clk;
 	int ret;
 	struct device *dev = &pdev->dev;
-	static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
 
 	clk = devm_clk_get(dev, NULL);
 	if (IS_ERR(clk))
@@ -437,6 +1679,10 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 	pltfm_host = sdhci_priv(host);
 	pltfm_host->clk = clk;
 
+	host->clk_mul = 0;
+	host->max_clk = SDMCLK_MAX_FREQ;
+	host->quirks |=  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
+	host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
 	priv = sdhci_pltfm_priv(pltfm_host);
 	priv->hrs_addr = host->ioaddr;
 	priv->enhanced_strobe = false;
@@ -444,8 +1690,6 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 	host->ioaddr += SDHCI_CDNS_SRS_BASE;
 	host->mmc_host_ops.hs400_enhanced_strobe =
 				sdhci_cdns_hs400_enhanced_strobe;
-	sdhci_enable_v4_mode(host);
-	__sdhci_read_caps(host, &version, NULL, NULL);
 
 	sdhci_get_of_property(pdev);
 
@@ -461,6 +1705,8 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
 	if (ret)
 		goto free;
 
+	sdhci_enable_v4_mode(host);
+	__sdhci_read_caps(host, NULL, NULL, NULL);
 	ret = sdhci_add_host(host);
 	if (ret)
 		goto free;
@@ -516,6 +1762,10 @@ static const struct of_device_id sdhci_cdns_match[] = {
 		.compatible = "cdns,sd4hc",
 		.data = &sdhci_cdns_sd4_of_data,
 	},
+	{
+		.compatible = "cdns,sd6hc",
+		.data = &sdhci_cdns_sd6_of_data,
+	},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 4/6] mmc: sdhci-cadence: enable MMC_SDHCI_IO_ACCESSORS
  2023-02-27 18:31 [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
                   ` (2 preceding siblings ...)
  2023-02-27 18:31 ` [PATCH v3 3/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
@ 2023-02-27 18:31 ` Piyush Malgujar
  2023-02-27 18:31 ` [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support Piyush Malgujar
  2023-02-27 18:31 ` [PATCH v3 6/6] mmc: sdhci-cadence: Add debug option for sdhci-cadence driver Piyush Malgujar
  5 siblings, 0 replies; 15+ messages in thread
From: Piyush Malgujar @ 2023-02-27 18:31 UTC (permalink / raw)
  To: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Piyush Malgujar

From: Jayanthi Annadurai <jannadurai@marvell.com>

Add support for CONFIG_MMC_SDHCI_IO_ACCESSORS for controller
specific register read and write APIs.

Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
 drivers/mmc/host/Kconfig         |  1 +
 drivers/mmc/host/sdhci-cadence.c | 63 ++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index e13b0b0b8ebb85a63b14a13d3fdbf009928b43fa..82dcfb323dda84171b501f0f26a88ce87f141453 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -255,6 +255,7 @@ config MMC_SDHCI_CADENCE
 	tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
 	depends on MMC_SDHCI_PLTFM
 	depends on OF
+	select MMC_SDHCI_IO_ACCESSORS
 	help
 	  This selects the Cadence SD/SDIO/eMMC driver.
 
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index 4b32757dd3750af6428e0d4177b419b3a9e8696d..badff2df70b904779c70775b02b40086780b118d 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -449,6 +449,61 @@ static u32 read_dqs_cmd_delay, clk_wrdqs_delay, clk_wr_delay, read_dqs_delay;
 
 static u32 sdhci_cdns_sd6_get_mode(struct sdhci_host *host, unsigned int timing);
 
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+static u32 sdhci_cdns_sd6_readl(struct sdhci_host *host, int reg)
+{
+	return readl(host->ioaddr + reg);
+}
+
+static void sdhci_cdns_sd6_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	writel(val, host->ioaddr + reg);
+}
+
+static u16 sdhci_cdns_sd6_readw(struct sdhci_host *host, int reg)
+{
+	u32 val, regoff;
+
+	regoff = reg & ~3;
+
+	val = readl(host->ioaddr + regoff);
+	if ((reg & 0x3) == 0)
+		return (val & 0xFFFF);
+	else
+		return ((val >> 16) & 0xFFFF);
+}
+
+static void sdhci_cdns_sd6_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	writew(val, host->ioaddr + reg);
+}
+
+static u8 sdhci_cdns_sd6_readb(struct sdhci_host *host, int reg)
+{
+	u32 val, regoff;
+
+	regoff = reg & ~3;
+
+	val = readl(host->ioaddr + regoff);
+	switch (reg & 3) {
+	case 0:
+		return (val & 0xFF);
+	case 1:
+		return ((val >> 8) & 0xFF);
+	case 2:
+		return ((val >> 16) & 0xFF);
+	case 3:
+		return ((val >> 24) & 0xFF);
+	}
+	return 0;
+}
+
+static void sdhci_cdns_sd6_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	writeb(val, host->ioaddr + reg);
+}
+#endif
+
 static int sdhci_cdns_sd6_phy_lock_dll(struct sdhci_cdns_sd6_phy *phy)
 {
 	u32 delay_element = phy->d.delay_element_org;
@@ -1578,6 +1633,14 @@ static const struct sdhci_ops sdhci_cdns_sd4_ops = {
 };
 
 static const struct sdhci_ops sdhci_cdns_sd6_ops = {
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+	.read_l = sdhci_cdns_sd6_readl,
+	.write_l = sdhci_cdns_sd6_writel,
+	.read_w = sdhci_cdns_sd6_readw,
+	.write_w = sdhci_cdns_sd6_writew,
+	.read_b = sdhci_cdns_sd6_readb,
+	.write_b = sdhci_cdns_sd6_writeb,
+#endif
 	.get_max_clock = sdhci_cdns_get_max_clock,
 	.set_clock = sdhci_cdns_sd6_set_clock,
 	.get_timeout_clock = sdhci_cdns_get_timeout_clock,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support
  2023-02-27 18:31 [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
                   ` (3 preceding siblings ...)
  2023-02-27 18:31 ` [PATCH v3 4/6] mmc: sdhci-cadence: enable MMC_SDHCI_IO_ACCESSORS Piyush Malgujar
@ 2023-02-27 18:31 ` Piyush Malgujar
  2023-02-28 10:53   ` Krzysztof Kozlowski
  2023-02-28 15:46   ` Rob Herring
  2023-02-27 18:31 ` [PATCH v3 6/6] mmc: sdhci-cadence: Add debug option for sdhci-cadence driver Piyush Malgujar
  5 siblings, 2 replies; 15+ messages in thread
From: Piyush Malgujar @ 2023-02-27 18:31 UTC (permalink / raw)
  To: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Piyush Malgujar

From: Jayanthi Annadurai <jannadurai@marvell.com>

Add support for SD6 controller support.

Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
 .../devicetree/bindings/mmc/cdns,sdhci.yaml   | 24 +++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
index 8b1a0fdcb5e3e2e8b87d8d7678e37f3dad447fc1..0dba17c4f17f82c8ae68e46225ed72418e8361ff 100644
--- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
+++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
@@ -4,7 +4,7 @@
 $id: http://devicetree.org/schemas/mmc/cdns,sdhci.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: Cadence SD/SDIO/eMMC Host Controller (SD4HC)
+title: Cadence SD/SDIO/eMMC Host Controller (SD4HC, SD6HC)
 
 maintainers:
   - Masahiro Yamada <yamada.masahiro@socionext.com>
@@ -18,7 +18,9 @@ properties:
       - enum:
           - microchip,mpfs-sd4hc
           - socionext,uniphier-sd4hc
-      - const: cdns,sd4hc
+      - enum:
+          - cdns,sd4hc
+          - cdns,sd6hc
 
   reg:
     maxItems: 1
@@ -111,6 +113,24 @@ properties:
     minimum: 0
     maximum: 0x7f
 
+  cdns,iocell-input-delay-ps:
+    description: Delay in ps across the input IO cells
+
+  cdns,iocell-output-delay-ps:
+    description: Delay in ps across the output IO cells
+
+  cdns,delay-element-ps:
+    description: Delay element in ps used for calculating phy timings
+
+  cdns,read-dqs-cmd-delay-ps:
+    description: Command delay used in HS200 tuning
+
+  cdns,tune-val-start-ps:
+    description: Staring value of data delay used in HS200 tuning
+
+  cdns,tune-val-step-ps:
+    description: Incremental value of data delay used in HS200 tuning
+
 required:
   - compatible
   - reg
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 6/6] mmc: sdhci-cadence: Add debug option for sdhci-cadence driver
  2023-02-27 18:31 [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
                   ` (4 preceding siblings ...)
  2023-02-27 18:31 ` [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support Piyush Malgujar
@ 2023-02-27 18:31 ` Piyush Malgujar
  2023-03-03  8:36   ` Adrian Hunter
  5 siblings, 1 reply; 15+ messages in thread
From: Piyush Malgujar @ 2023-02-27 18:31 UTC (permalink / raw)
  To: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Piyush Malgujar

From: Jayanthi Annadurai <jannadurai@marvell.com>

Use Kernel config CONFIG_MMC_DEBUG to support dumping PHY and host
controller register configuration for debug.

Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
 drivers/mmc/host/sdhci-cadence.c | 154 +++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index badff2df70b904779c70775b02b40086780b118d..c448a100f32c8c44a0da7c71c2aa5d25ac5a4b44 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -115,6 +115,10 @@
 #define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY		GENMASK(15, 8)
 #define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY		GENMASK(7, 0)
 
+#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0				0x201C
+#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG1				0x2020
+#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG2				0x2024
+
 #define SDHCI_CDNS_SD6_PHY_CTRL					0x2080
 #define	SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING		GENMASK(9, 4)
 
@@ -968,6 +972,154 @@ static void sdhci_cdns_sd6_calc_phy(struct sdhci_cdns_sd6_phy *phy)
 	}
 }
 
+#ifdef CONFIG_MMC_DEBUG
+
+static
+void sdhci_cdns_sd6_phy_dump(struct sdhci_cdns_sd6_phy *phy,
+			     struct sdhci_host *host)
+{
+	dev_dbg(mmc_dev(host->mmc), "PHY Timings\n");
+	dev_dbg(mmc_dev(host->mmc), "mode %d t_sdclk %d\n", phy->mode,
+		phy->t_sdclk);
+
+	dev_dbg(mmc_dev(host->mmc), "cp_clk_wr_delay %d\n",
+		phy->settings.cp_clk_wr_delay);
+	dev_dbg(mmc_dev(host->mmc), "cp_clk_wrdqs_delay %d\n",
+		phy->settings.cp_clk_wrdqs_delay);
+	dev_dbg(mmc_dev(host->mmc), "cp_data_select_oe_end %d\n",
+		phy->settings.cp_data_select_oe_end);
+	dev_dbg(mmc_dev(host->mmc), "cp_dll_bypass_mode %d\n",
+		phy->settings.cp_dll_bypass_mode);
+	dev_dbg(mmc_dev(host->mmc), "cp_dll_locked_mode %d\n",
+		phy->settings.cp_dll_locked_mode);
+	dev_dbg(mmc_dev(host->mmc), "cp_dll_start_point %d\n",
+		phy->settings.cp_dll_start_point);
+	dev_dbg(mmc_dev(host->mmc), "cp_io_mask_always_on %d\n",
+		phy->settings.cp_io_mask_always_on);
+	dev_dbg(mmc_dev(host->mmc), "cp_io_mask_end %d\n",
+		phy->settings.cp_io_mask_end);
+	dev_dbg(mmc_dev(host->mmc), "cp_io_mask_start %d\n",
+		phy->settings.cp_io_mask_start);
+	dev_dbg(mmc_dev(host->mmc), "cp_rd_del_sel %d\n",
+		phy->settings.cp_rd_del_sel);
+	dev_dbg(mmc_dev(host->mmc), "cp_read_dqs_cmd_delay %d\n",
+		phy->settings.cp_read_dqs_cmd_delay);
+	dev_dbg(mmc_dev(host->mmc), "cp_read_dqs_delay %d\n",
+		phy->settings.cp_read_dqs_delay);
+	dev_dbg(mmc_dev(host->mmc), "cp_sw_half_cycle_shift %d\n",
+		phy->settings.cp_sw_half_cycle_shift);
+	dev_dbg(mmc_dev(host->mmc), "cp_sync_method %d\n",
+		phy->settings.cp_sync_method);
+	dev_dbg(mmc_dev(host->mmc), "cp_use_ext_lpbk_dqs %d\n",
+		phy->settings.cp_use_ext_lpbk_dqs);
+	dev_dbg(mmc_dev(host->mmc), "cp_use_lpbk_dqs %d\n",
+		phy->settings.cp_use_lpbk_dqs);
+	dev_dbg(mmc_dev(host->mmc), "cp_use_phony_dqs %d\n",
+		phy->settings.cp_use_phony_dqs);
+	dev_dbg(mmc_dev(host->mmc), "cp_use_phony_dqs_cmd %d\n",
+		phy->settings.cp_use_phony_dqs_cmd);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_extended_rd_mode %d\n",
+		phy->settings.sdhc_extended_rd_mode);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_extended_wr_mode %d\n",
+		phy->settings.sdhc_extended_wr_mode);
+
+	dev_dbg(mmc_dev(host->mmc), "sdhc_hcsdclkadj %d\n",
+		phy->settings.sdhc_hcsdclkadj);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_idelay_val %d\n",
+		phy->settings.sdhc_idelay_val);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_rdcmd_en %d\n",
+		phy->settings.sdhc_rdcmd_en);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_rddata_en %d\n",
+		phy->settings.sdhc_rddata_en);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_rw_compensate %d\n",
+		phy->settings.sdhc_rw_compensate);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_sdcfsh %d\n",
+		phy->settings.sdhc_sdcfsh);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_sdcfsl %d\n",
+		phy->settings.sdhc_sdcfsl);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_wrcmd0_dly %d %d\n",
+		phy->settings.sdhc_wrcmd0_dly,
+		phy->settings.sdhc_wrcmd0_sdclk_dly);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_wrcmd1_dly %d %d\n",
+		phy->settings.sdhc_wrcmd1_dly,
+		phy->settings.sdhc_wrcmd1_sdclk_dly);
+	dev_dbg(mmc_dev(host->mmc), "sdhc_wrdata0_dly %d %d\n",
+		phy->settings.sdhc_wrdata0_dly,
+		phy->settings.sdhc_wrdata0_sdclk_dly);
+
+	dev_dbg(mmc_dev(host->mmc), "sdhc_wrdata1_dly %d %d\n",
+		phy->settings.sdhc_wrdata1_dly,
+		phy->settings.sdhc_wrdata1_sdclk_dly);
+	dev_dbg(mmc_dev(host->mmc), "hs200_tune_val %d\n",
+		phy->settings.hs200_tune_val);
+}
+
+static
+void sdhci_cdns_sd6_dump(struct sdhci_cdns_priv *priv, struct sdhci_host *host)
+{
+	struct sdhci_cdns_sd6_phy *phy = priv->phy;
+	int id;
+
+	sdhci_cdns_sd6_phy_dump(phy);
+
+	dev_dbg(mmc_dev(host->mmc), "Host controller Register Dump\n");
+	for (id = 0; id < 14; id++) {
+		dev_dbg(mmc_dev(host->mmc), "HRS%d 0x%x\n", id,
+			readl(priv->hrs_addr + (id * 4)));
+	}
+
+	id = 29;
+	dev_dbg(mmc_dev(host->mmc), "HRS%d 0x%x\n", id,
+		readl(priv->hrs_addr + (id * 4)));
+	id = 30;
+	dev_dbg(mmc_dev(host->mmc), "HRS%d 0x%x\n", id,
+		readl(priv->hrs_addr + (id * 4)));
+
+	for (id = 0; id < 27; id++) {
+		dev_dbg(mmc_dev(host->mmc), "SRS%d 0x%x\n", id,
+			readl(priv->hrs_addr + 0x200 + (id * 4)));
+	}
+
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DQS_TIMING 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_DQS_TIMING));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_GATE_LPBK 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_GATE_LPBK));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_MASTER 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_DLL_MASTER));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_SLAVE 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_DLL_SLAVE));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_CTRL 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_CTRL));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_GPIO_CTRL0 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_GPIO_CTRL0));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DQ_TIMING 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_DQ_TIMING));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_OBS_REG1 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_DLL_OBS_REG1));
+	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_OBS_REG2 0x%x\n",
+		sdhci_cdns_sd6_read_phy_reg(priv,
+					    SDHCI_CDNS_SD6_PHY_DLL_OBS_REG2));
+}
+
+#else
+
+static inline void sdhci_cdns_sd6_dump(struct sdhci_cdns_priv *priv,
+				       struct sdhci_host *host)
+{
+}
+
+#endif
+
 static
 int sdhci_cdns_sd6_get_delay_params(struct device *dev,
 				    struct sdhci_cdns_priv *priv)
@@ -1319,6 +1471,8 @@ static void sdhci_cdns_sd6_set_clock(struct sdhci_host *host,
 		pr_debug("%s: phy init failed\n", __func__);
 
 	sdhci_set_clock(host, clock);
+
+	sdhci_cdns_sd6_dump(priv, host);
 }
 
 static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support
  2023-02-27 18:31 ` [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support Piyush Malgujar
@ 2023-02-28 10:53   ` Krzysztof Kozlowski
  2023-03-24 15:02     ` Piyush Malgujar
  2023-02-28 15:46   ` Rob Herring
  1 sibling, 1 reply; 15+ messages in thread
From: Krzysztof Kozlowski @ 2023-02-28 10:53 UTC (permalink / raw)
  To: Piyush Malgujar, linux-mmc, linux-kernel, adrian.hunter,
	ulf.hansson, robh+dt, krzysztof.kozlowski+dt, yamada.masahiro,
	devicetree
  Cc: jannadurai, cchavva

On 27/02/2023 19:31, Piyush Malgujar wrote:
> From: Jayanthi Annadurai <jannadurai@marvell.com>
> 
> Add support for SD6 controller support.
> 
> Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> ---
>  .../devicetree/bindings/mmc/cdns,sdhci.yaml   | 24 +++++++++++++++++--
>  1 file changed, 22 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> index 8b1a0fdcb5e3e2e8b87d8d7678e37f3dad447fc1..0dba17c4f17f82c8ae68e46225ed72418e8361ff 100644
> --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> @@ -4,7 +4,7 @@
>  $id: http://devicetree.org/schemas/mmc/cdns,sdhci.yaml#
>  $schema: http://devicetree.org/meta-schemas/core.yaml#
>  
> -title: Cadence SD/SDIO/eMMC Host Controller (SD4HC)
> +title: Cadence SD/SDIO/eMMC Host Controller (SD4HC, SD6HC)
>  
>  maintainers:
>    - Masahiro Yamada <yamada.masahiro@socionext.com>
> @@ -18,7 +18,9 @@ properties:
>        - enum:
>            - microchip,mpfs-sd4hc
>            - socionext,uniphier-sd4hc
> -      - const: cdns,sd4hc
> +      - enum:
> +          - cdns,sd4hc
> +          - cdns,sd6hc

I see here rather random set of changes in each version of this patch.
This does not really make sense. You are saying that existing (!!!)
mpfs-sd4hc is compatible with sd6hc. I think you wanted oneOf here, but
not sure. Can you explain what is your intention? Your commit msg is
just one line saying the same as subject, so not really helpful.


Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support
  2023-02-27 18:31 ` [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support Piyush Malgujar
  2023-02-28 10:53   ` Krzysztof Kozlowski
@ 2023-02-28 15:46   ` Rob Herring
  2023-03-24 15:13     ` Piyush Malgujar
  1 sibling, 1 reply; 15+ messages in thread
From: Rob Herring @ 2023-02-28 15:46 UTC (permalink / raw)
  To: Piyush Malgujar
  Cc: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree, jannadurai,
	cchavva

On Mon, Feb 27, 2023 at 10:31:50AM -0800, Piyush Malgujar wrote:
> From: Jayanthi Annadurai <jannadurai@marvell.com>
> 
> Add support for SD6 controller support.

On what h/w?

> 
> Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> ---
>  .../devicetree/bindings/mmc/cdns,sdhci.yaml   | 24 +++++++++++++++++--
>  1 file changed, 22 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> index 8b1a0fdcb5e3e2e8b87d8d7678e37f3dad447fc1..0dba17c4f17f82c8ae68e46225ed72418e8361ff 100644
> --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> @@ -4,7 +4,7 @@
>  $id: http://devicetree.org/schemas/mmc/cdns,sdhci.yaml#
>  $schema: http://devicetree.org/meta-schemas/core.yaml#
>  
> -title: Cadence SD/SDIO/eMMC Host Controller (SD4HC)
> +title: Cadence SD/SDIO/eMMC Host Controller (SD4HC, SD6HC)
>  
>  maintainers:
>    - Masahiro Yamada <yamada.masahiro@socionext.com>
> @@ -18,7 +18,9 @@ properties:
>        - enum:
>            - microchip,mpfs-sd4hc
>            - socionext,uniphier-sd4hc
> -      - const: cdns,sd4hc
> +      - enum:
> +          - cdns,sd4hc
> +          - cdns,sd6hc

Other than FPGA implementations IP vendor compatible strings are pretty 
much useless. Define a compatible for your h/w.

>  
>    reg:
>      maxItems: 1
> @@ -111,6 +113,24 @@ properties:
>      minimum: 0
>      maximum: 0x7f
>  
> +  cdns,iocell-input-delay-ps:
> +    description: Delay in ps across the input IO cells
> +
> +  cdns,iocell-output-delay-ps:
> +    description: Delay in ps across the output IO cells
> +
> +  cdns,delay-element-ps:
> +    description: Delay element in ps used for calculating phy timings
> +
> +  cdns,read-dqs-cmd-delay-ps:
> +    description: Command delay used in HS200 tuning
> +
> +  cdns,tune-val-start-ps:
> +    description: Staring value of data delay used in HS200 tuning
> +
> +  cdns,tune-val-step-ps:
> +    description: Incremental value of data delay used in HS200 tuning

Wouldn't any controller implementation need these possibly? IIRC, we 
have some common properties for this. If not, survey what we do have and 
come up with something common. Or you can imply all this from the h/w 
specific compatible you are going to add.

Rob

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 2/6] mmc: sdhci-cadence: Restructure the code
  2023-02-27 18:31 ` [PATCH v3 2/6] mmc: sdhci-cadence: Restructure the code Piyush Malgujar
@ 2023-03-03  8:36   ` Adrian Hunter
  2023-03-24 15:19     ` Piyush Malgujar
  0 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2023-03-03  8:36 UTC (permalink / raw)
  To: Piyush Malgujar, linux-mmc, linux-kernel, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Dhananjay Kangude

On 27/02/23 20:31, Piyush Malgujar wrote:
> From: Dhananjay Kangude <dkangude@cadence.com>
> 
> Restructured the code, added new structures and functions for
> SD4 operations. Also this adds some abstraction to the code
> which will make it modular and adaptable for further SD6 operations.
> 
> Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
> Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> ---
>  drivers/mmc/host/sdhci-cadence.c | 100 ++++++++++++++++++++++++-------
>  1 file changed, 80 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
> index 5276cdbc652f7faac13bb0244af4926b63dc119a..4f7e63c90e3d68da338b8964f08b7c65ebaf1ffd 100644
> --- a/drivers/mmc/host/sdhci-cadence.c
> +++ b/drivers/mmc/host/sdhci-cadence.c
> @@ -59,16 +59,28 @@
>   */
>  #define SDHCI_CDNS_MAX_TUNING_LOOP	40
>  
> +struct sdhci_cdns_priv;
> +
>  struct sdhci_cdns_sd4_phy_param {
>  	u8 addr;
>  	u8 data;
>  };
>  
> +struct sdhci_cdns_data {
> +	int (*phy_init)(struct sdhci_cdns_priv *priv);
> +	int (*set_tune_val)(struct sdhci_host *host, unsigned int val);
> +};
> +
> +struct sdhci_cdns_sd4_phy {
> +	unsigned int nr_phy_params;
> +	struct sdhci_cdns_sd4_phy_param phy_params[];
> +};
> +
>  struct sdhci_cdns_priv {
>  	void __iomem *hrs_addr;
>  	bool enhanced_strobe;
> -	unsigned int nr_phy_params;
> -	struct sdhci_cdns_sd4_phy_param phy_params[];
> +	const struct sdhci_cdns_data *cdns_data;

Simpler if there is just a pointer to struct sdhci_cdns_of_data
and get rid of struct sdhci_cdns_data.

> +	void *phy;
>  };
>  
>  struct sdhci_cdns_sd4_phy_cfg {
> @@ -76,6 +88,13 @@ struct sdhci_cdns_sd4_phy_cfg {
>  	u8 addr;
>  };
>  
> +struct sdhci_cdns_of_data {
> +	const struct sdhci_pltfm_data *pltfm_data;

Kernel style is not to unnecessarily have structures that point to
other structures or contain other structures.

Here, please just put the struct not a pointer i.e.

	struct sdhci_pltfm_data *pltfm_data;

> +	const struct sdhci_cdns_data *cdns_data;

Please get rid of struct sdhci_cdns_data. Instead just put its members here

> +	int (*phy_probe)(struct platform_device *pdev,
> +			 struct sdhci_cdns_priv *priv);
> +};
> +
>  static const struct sdhci_cdns_sd4_phy_cfg sdhci_cdns_sd4_phy_cfgs[] = {
>  	{ "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
>  	{ "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
> @@ -135,9 +154,9 @@ static unsigned int sdhci_cdns_sd4_phy_param_count(struct device_node *np)
>  }
>  
>  static void sdhci_cdns_sd4_phy_param_parse(struct device_node *np,
> -					   struct sdhci_cdns_priv *priv)
> +					   struct sdhci_cdns_sd4_phy *phy)
>  {
> -	struct sdhci_cdns_sd4_phy_param *p = priv->phy_params;
> +	struct sdhci_cdns_sd4_phy_param *p = phy->phy_params;
>  	u32 val;
>  	int ret, i;
>  
> @@ -156,10 +175,11 @@ static void sdhci_cdns_sd4_phy_param_parse(struct device_node *np,
>  static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_priv *priv)
>  {
>  	int ret, i;
> +	struct sdhci_cdns_sd4_phy *phy = priv->phy;
>  
> -	for (i = 0; i < priv->nr_phy_params; i++) {
> -		ret = sdhci_cdns_sd4_write_phy_reg(priv, priv->phy_params[i].addr,
> -						   priv->phy_params[i].data);
> +	for (i = 0; i < phy->nr_phy_params; i++) {
> +		ret = sdhci_cdns_sd4_write_phy_reg(priv, phy->phy_params[i].addr,
> +						   phy->phy_params[i].data);
>  		if (ret)
>  			return ret;
>  	}
> @@ -202,6 +222,27 @@ static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
>  	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
>  }
>  
> +static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
> +				    struct sdhci_cdns_priv *priv)
> +{
> +	unsigned int nr_phy_params;
> +	struct sdhci_cdns_sd4_phy *phy;
> +	struct device *dev = &pdev->dev;
> +
> +	nr_phy_params = sdhci_cdns_sd4_phy_param_count(dev->of_node);
> +	phy = devm_kzalloc(dev, struct_size(phy, phy_params, nr_phy_params),
> +			   GFP_KERNEL);
> +	if (!phy)
> +		return -ENOMEM;
> +
> +	phy->nr_phy_params = nr_phy_params;
> +
> +	sdhci_cdns_sd4_phy_param_parse(dev->of_node, phy);
> +	priv->phy = phy;
> +
> +	return 0;
> +}
> +
>  static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val)
>  {
>  	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> @@ -323,10 +364,25 @@ static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
>  	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
>  };
>  
> +static const struct sdhci_cdns_of_data sdhci_cdns_uniphier_of_data = {
> +	.pltfm_data = &sdhci_cdns_uniphier_pltfm_data,
> +};
> +
>  static const struct sdhci_pltfm_data sdhci_cdns_sd4_pltfm_data = {
>  	.ops = &sdhci_cdns_sd4_ops,
>  };
>  
> +static const struct sdhci_cdns_data sdhci_cdns_sd4_data = {
> +	.phy_init = sdhci_cdns_sd4_phy_init,
> +	.set_tune_val = sdhci_cdns_sd4_set_tune_val,
> +};
> +
> +static const struct sdhci_cdns_of_data sdhci_cdns_sd4_of_data = {
> +	.pltfm_data = &sdhci_cdns_sd4_pltfm_data,
> +	.cdns_data = &sdhci_cdns_sd4_data,
> +	.phy_probe = sdhci_cdns_sd4_phy_probe,
> +};
> +
>  static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
>  					     struct mmc_ios *ios)
>  {
> @@ -350,11 +406,10 @@ static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
>  static int sdhci_cdns_probe(struct platform_device *pdev)
>  {
>  	struct sdhci_host *host;
> -	const struct sdhci_pltfm_data *data;
> +	const struct sdhci_cdns_of_data *data;
>  	struct sdhci_pltfm_host *pltfm_host;
>  	struct sdhci_cdns_priv *priv;
>  	struct clk *clk;
> -	unsigned int nr_phy_params;
>  	int ret;
>  	struct device *dev = &pdev->dev;
>  	static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
> @@ -368,12 +423,12 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
>  		return ret;
>  
>  	data = of_device_get_match_data(dev);
> -	if (!data)
> -		data = &sdhci_cdns_sd4_pltfm_data;
> +	if (!data) {
> +		ret = -EINVAL;
> +		goto disable_clk;
> +	}
>  
> -	nr_phy_params = sdhci_cdns_sd4_phy_param_count(dev->of_node);
> -	host = sdhci_pltfm_init(pdev, data,
> -				struct_size(priv, phy_params, nr_phy_params));
> +	host = sdhci_pltfm_init(pdev, data->pltfm_data, sizeof(*priv));
>  	if (IS_ERR(host)) {
>  		ret = PTR_ERR(host);
>  		goto disable_clk;
> @@ -383,9 +438,9 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
>  	pltfm_host->clk = clk;
>  
>  	priv = sdhci_pltfm_priv(pltfm_host);
> -	priv->nr_phy_params = nr_phy_params;
>  	priv->hrs_addr = host->ioaddr;
>  	priv->enhanced_strobe = false;
> +	priv->cdns_data = data->cdns_data;
>  	host->ioaddr += SDHCI_CDNS_SRS_BASE;
>  	host->mmc_host_ops.hs400_enhanced_strobe =
>  				sdhci_cdns_hs400_enhanced_strobe;
> @@ -398,9 +453,11 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
>  	if (ret)
>  		goto free;
>  
> -	sdhci_cdns_sd4_phy_param_parse(dev->of_node, priv);
> +	ret = data->phy_probe(pdev, priv);
> +	if (ret)
> +		goto free;
>  
> -	ret = sdhci_cdns_sd4_phy_init(priv);
> +	ret = priv->cdns_data->phy_init(priv);

As was pointed out last time, you are dereferncing cdns_data unconditionally
when it could be NULL e.g. in the case of sdhci_cdns_uniphier_of_data

>  	if (ret)
>  		goto free;
>  
> @@ -429,7 +486,7 @@ static int sdhci_cdns_resume(struct device *dev)
>  	if (ret)
>  		return ret;
>  
> -	ret = sdhci_cdns_sd4_phy_init(priv);
> +	ret = priv->cdns_data->phy_init(priv);
>  	if (ret)
>  		goto disable_clk;
>  
> @@ -453,9 +510,12 @@ static const struct dev_pm_ops sdhci_cdns_pm_ops = {
>  static const struct of_device_id sdhci_cdns_match[] = {
>  	{
>  		.compatible = "socionext,uniphier-sd4hc",
> -		.data = &sdhci_cdns_uniphier_pltfm_data,
> +		.data = &sdhci_cdns_uniphier_of_data,
> +	},
> +	{
> +		.compatible = "cdns,sd4hc",
> +		.data = &sdhci_cdns_sd4_of_data,
>  	},
> -	{ .compatible = "cdns,sd4hc" },
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, sdhci_cdns_match);


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 6/6] mmc: sdhci-cadence: Add debug option for sdhci-cadence driver
  2023-02-27 18:31 ` [PATCH v3 6/6] mmc: sdhci-cadence: Add debug option for sdhci-cadence driver Piyush Malgujar
@ 2023-03-03  8:36   ` Adrian Hunter
  0 siblings, 0 replies; 15+ messages in thread
From: Adrian Hunter @ 2023-03-03  8:36 UTC (permalink / raw)
  To: Piyush Malgujar, linux-mmc, linux-kernel, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva

On 27/02/23 20:31, Piyush Malgujar wrote:
> From: Jayanthi Annadurai <jannadurai@marvell.com>
> 
> Use Kernel config CONFIG_MMC_DEBUG to support dumping PHY and host
> controller register configuration for debug.
> 
> Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> ---
>  drivers/mmc/host/sdhci-cadence.c | 154 +++++++++++++++++++++++++++++++
>  1 file changed, 154 insertions(+)
> 
> diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
> index badff2df70b904779c70775b02b40086780b118d..c448a100f32c8c44a0da7c71c2aa5d25ac5a4b44 100644
> --- a/drivers/mmc/host/sdhci-cadence.c
> +++ b/drivers/mmc/host/sdhci-cadence.c
> @@ -115,6 +115,10 @@
>  #define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY		GENMASK(15, 8)
>  #define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY		GENMASK(7, 0)
>  
> +#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0				0x201C
> +#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG1				0x2020
> +#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG2				0x2024
> +
>  #define SDHCI_CDNS_SD6_PHY_CTRL					0x2080
>  #define	SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING		GENMASK(9, 4)
>  
> @@ -968,6 +972,154 @@ static void sdhci_cdns_sd6_calc_phy(struct sdhci_cdns_sd6_phy *phy)
>  	}
>  }
>  
> +#ifdef CONFIG_MMC_DEBUG

At this point this might as well be instead:

#if defined(DEBUG) || IS_ENABLED(CONFIG_DYNAMIC_DEBUG)

> +
> +static
> +void sdhci_cdns_sd6_phy_dump(struct sdhci_cdns_sd6_phy *phy,
> +			     struct sdhci_host *host)
> +{
> +	dev_dbg(mmc_dev(host->mmc), "PHY Timings\n");
> +	dev_dbg(mmc_dev(host->mmc), "mode %d t_sdclk %d\n", phy->mode,
> +		phy->t_sdclk);
> +
> +	dev_dbg(mmc_dev(host->mmc), "cp_clk_wr_delay %d\n",
> +		phy->settings.cp_clk_wr_delay);
> +	dev_dbg(mmc_dev(host->mmc), "cp_clk_wrdqs_delay %d\n",
> +		phy->settings.cp_clk_wrdqs_delay);
> +	dev_dbg(mmc_dev(host->mmc), "cp_data_select_oe_end %d\n",
> +		phy->settings.cp_data_select_oe_end);
> +	dev_dbg(mmc_dev(host->mmc), "cp_dll_bypass_mode %d\n",
> +		phy->settings.cp_dll_bypass_mode);
> +	dev_dbg(mmc_dev(host->mmc), "cp_dll_locked_mode %d\n",
> +		phy->settings.cp_dll_locked_mode);
> +	dev_dbg(mmc_dev(host->mmc), "cp_dll_start_point %d\n",
> +		phy->settings.cp_dll_start_point);
> +	dev_dbg(mmc_dev(host->mmc), "cp_io_mask_always_on %d\n",
> +		phy->settings.cp_io_mask_always_on);
> +	dev_dbg(mmc_dev(host->mmc), "cp_io_mask_end %d\n",
> +		phy->settings.cp_io_mask_end);
> +	dev_dbg(mmc_dev(host->mmc), "cp_io_mask_start %d\n",
> +		phy->settings.cp_io_mask_start);
> +	dev_dbg(mmc_dev(host->mmc), "cp_rd_del_sel %d\n",
> +		phy->settings.cp_rd_del_sel);
> +	dev_dbg(mmc_dev(host->mmc), "cp_read_dqs_cmd_delay %d\n",
> +		phy->settings.cp_read_dqs_cmd_delay);
> +	dev_dbg(mmc_dev(host->mmc), "cp_read_dqs_delay %d\n",
> +		phy->settings.cp_read_dqs_delay);
> +	dev_dbg(mmc_dev(host->mmc), "cp_sw_half_cycle_shift %d\n",
> +		phy->settings.cp_sw_half_cycle_shift);
> +	dev_dbg(mmc_dev(host->mmc), "cp_sync_method %d\n",
> +		phy->settings.cp_sync_method);
> +	dev_dbg(mmc_dev(host->mmc), "cp_use_ext_lpbk_dqs %d\n",
> +		phy->settings.cp_use_ext_lpbk_dqs);
> +	dev_dbg(mmc_dev(host->mmc), "cp_use_lpbk_dqs %d\n",
> +		phy->settings.cp_use_lpbk_dqs);
> +	dev_dbg(mmc_dev(host->mmc), "cp_use_phony_dqs %d\n",
> +		phy->settings.cp_use_phony_dqs);
> +	dev_dbg(mmc_dev(host->mmc), "cp_use_phony_dqs_cmd %d\n",
> +		phy->settings.cp_use_phony_dqs_cmd);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_extended_rd_mode %d\n",
> +		phy->settings.sdhc_extended_rd_mode);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_extended_wr_mode %d\n",
> +		phy->settings.sdhc_extended_wr_mode);
> +
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_hcsdclkadj %d\n",
> +		phy->settings.sdhc_hcsdclkadj);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_idelay_val %d\n",
> +		phy->settings.sdhc_idelay_val);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_rdcmd_en %d\n",
> +		phy->settings.sdhc_rdcmd_en);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_rddata_en %d\n",
> +		phy->settings.sdhc_rddata_en);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_rw_compensate %d\n",
> +		phy->settings.sdhc_rw_compensate);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_sdcfsh %d\n",
> +		phy->settings.sdhc_sdcfsh);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_sdcfsl %d\n",
> +		phy->settings.sdhc_sdcfsl);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_wrcmd0_dly %d %d\n",
> +		phy->settings.sdhc_wrcmd0_dly,
> +		phy->settings.sdhc_wrcmd0_sdclk_dly);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_wrcmd1_dly %d %d\n",
> +		phy->settings.sdhc_wrcmd1_dly,
> +		phy->settings.sdhc_wrcmd1_sdclk_dly);
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_wrdata0_dly %d %d\n",
> +		phy->settings.sdhc_wrdata0_dly,
> +		phy->settings.sdhc_wrdata0_sdclk_dly);
> +
> +	dev_dbg(mmc_dev(host->mmc), "sdhc_wrdata1_dly %d %d\n",
> +		phy->settings.sdhc_wrdata1_dly,
> +		phy->settings.sdhc_wrdata1_sdclk_dly);
> +	dev_dbg(mmc_dev(host->mmc), "hs200_tune_val %d\n",
> +		phy->settings.hs200_tune_val);
> +}
> +
> +static
> +void sdhci_cdns_sd6_dump(struct sdhci_cdns_priv *priv, struct sdhci_host *host)
> +{
> +	struct sdhci_cdns_sd6_phy *phy = priv->phy;
> +	int id;
> +
> +	sdhci_cdns_sd6_phy_dump(phy);
> +
> +	dev_dbg(mmc_dev(host->mmc), "Host controller Register Dump\n");
> +	for (id = 0; id < 14; id++) {
> +		dev_dbg(mmc_dev(host->mmc), "HRS%d 0x%x\n", id,
> +			readl(priv->hrs_addr + (id * 4)));
> +	}
> +
> +	id = 29;
> +	dev_dbg(mmc_dev(host->mmc), "HRS%d 0x%x\n", id,
> +		readl(priv->hrs_addr + (id * 4)));
> +	id = 30;
> +	dev_dbg(mmc_dev(host->mmc), "HRS%d 0x%x\n", id,
> +		readl(priv->hrs_addr + (id * 4)));
> +
> +	for (id = 0; id < 27; id++) {
> +		dev_dbg(mmc_dev(host->mmc), "SRS%d 0x%x\n", id,
> +			readl(priv->hrs_addr + 0x200 + (id * 4)));
> +	}
> +
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DQS_TIMING 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_DQS_TIMING));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_GATE_LPBK 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_GATE_LPBK));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_MASTER 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_DLL_MASTER));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_SLAVE 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_DLL_SLAVE));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_CTRL 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_CTRL));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_GPIO_CTRL0 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_GPIO_CTRL0));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DQ_TIMING 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_DQ_TIMING));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_OBS_REG1 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_DLL_OBS_REG1));
> +	dev_dbg(mmc_dev(host->mmc), "SDHCI_CDNS_SD6_PHY_DLL_OBS_REG2 0x%x\n",
> +		sdhci_cdns_sd6_read_phy_reg(priv,
> +					    SDHCI_CDNS_SD6_PHY_DLL_OBS_REG2));
> +}
> +
> +#else
> +
> +static inline void sdhci_cdns_sd6_dump(struct sdhci_cdns_priv *priv,
> +				       struct sdhci_host *host)
> +{
> +}
> +
> +#endif
> +
>  static
>  int sdhci_cdns_sd6_get_delay_params(struct device *dev,
>  				    struct sdhci_cdns_priv *priv)
> @@ -1319,6 +1471,8 @@ static void sdhci_cdns_sd6_set_clock(struct sdhci_host *host,
>  		pr_debug("%s: phy init failed\n", __func__);
>  
>  	sdhci_set_clock(host, clock);
> +
> +	sdhci_cdns_sd6_dump(priv, host);
>  }
>  
>  static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/6] mmc: sdhci-cadence: SD6 controller support
  2023-02-27 18:31 ` [PATCH v3 3/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
@ 2023-03-03  8:48   ` Adrian Hunter
  0 siblings, 0 replies; 15+ messages in thread
From: Adrian Hunter @ 2023-03-03  8:48 UTC (permalink / raw)
  To: Piyush Malgujar, linux-mmc, linux-kernel, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree
  Cc: jannadurai, cchavva, Dhananjay Kangude

On 27/02/23 20:31, Piyush Malgujar wrote:
> From: Dhananjay Kangude <dkangude@cadence.com>
> 
> Add support for SD6 controller and related ops along with
> support for HS400 and HS400ES emmc modes.
> Updated HS200 tuning values and support to read tune configuration
> from FDT and support to configure and read host side drive strength,
> slew from device tree.
> 
> Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
> Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> ---
> v2 ---> v3
> - Removed value stored to 'clk_wr_delay' which was never read
> - Removed some whitespace changes
> v1 ---> v2
> - Separated out the reformating and renaming of code as
>   preparation patch to add SD6 support
> - Used proper property names as per dtschema
> - Fixed issues related to PTR_ERR and unreachable code
> Reported-by: kernel test robot <lkp@intel.com>
> Reported-by: Dan Carpenter <error27@gmail.com>
> ---
>  drivers/mmc/host/sdhci-cadence.c | 1260 +++++++++++++++++++++++++++++-
>  1 file changed, 1255 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
> index 4f7e63c90e3d68da338b8964f08b7c65ebaf1ffd..4b32757dd3750af6428e0d4177b419b3a9e8696d 100644
> --- a/drivers/mmc/host/sdhci-cadence.c
> +++ b/drivers/mmc/host/sdhci-cadence.c
> @@ -15,8 +15,20 @@
>  
>  #include "sdhci-pltfm.h"
>  
> +#define SDMCLK_MAX_FREQ		200000000
> +
> +#define DEFAULT_CMD_DELAY		16
> +#define SDHCI_CDNS_TUNE_START		16
> +#define SDHCI_CDNS_TUNE_STEP		6
> +#define SDHCI_CDNS_TUNE_ITERATIONS	40
> +
> +#define SDHCI_CDNS_HRS00			0x00
> +#define SDHCI_CDNS_HRS00_SWR			BIT(0)
> +
> +#define SDHCI_CDNS_HRS02			0x08		/* PHY access port */
> +#define SDHCI_CDNS_HRS04			0x10		/* PHY access port */
> +
>  /* SD 4.0 Controller HRS - Host Register Set (specific to Cadence) */
> -#define SDHCI_CDNS_HRS04			0x10	/* PHY access port */
>  #define SDHCI_CDNS_SD4_HRS04_ACK		BIT(26)
>  #define SDHCI_CDNS_SD4_HRS04_RD			BIT(25)
>  #define SDHCI_CDNS_SD4_HRS04_WR			BIT(24)
> @@ -29,12 +41,89 @@
>  #define   SDHCI_CDNS_HRS06_TUNE			GENMASK(13, 8)
>  #define   SDHCI_CDNS_HRS06_MODE			GENMASK(2, 0)
>  #define   SDHCI_CDNS_HRS06_MODE_SD		0x0
> +#define SDHCI_CDNS_HRS06_MODE_LEGACY		0x1
>  #define   SDHCI_CDNS_HRS06_MODE_MMC_SDR		0x2
>  #define   SDHCI_CDNS_HRS06_MODE_MMC_DDR		0x3
>  #define   SDHCI_CDNS_HRS06_MODE_MMC_HS200	0x4
>  #define   SDHCI_CDNS_HRS06_MODE_MMC_HS400	0x5
>  #define   SDHCI_CDNS_HRS06_MODE_MMC_HS400ES	0x6
>  
> +/* SD 6.0 Controller HRS - Host Register Set (Specific to Cadence) */
> +#define SDHCI_CDNS_SD6_HRS04_ADDR		GENMASK(15, 0)
> +
> +#define SDHCI_CDNS_HRS05			0x14
> +
> +#define SDHCI_CDNS_HRS07			0x1C
> +#define	SDHCI_CDNS_HRS07_RW_COMPENSATE		GENMASK(20, 16)
> +#define	SDHCI_CDNS_HRS07_IDELAY_VAL		GENMASK(4, 0)
> +
> +#define SDHCI_CDNS_HRS09			0x24
> +#define	SDHCI_CDNS_HRS09_RDDATA_EN		BIT(16)
> +#define	SDHCI_CDNS_HRS09_RDCMD_EN		BIT(15)
> +#define	SDHCI_CDNS_HRS09_EXTENDED_WR_MODE	BIT(3)
> +#define	SDHCI_CDNS_HRS09_EXTENDED_RD_MODE	BIT(2)
> +#define	SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE	BIT(1)
> +#define	SDHCI_CDNS_HRS09_PHY_SW_RESET		BIT(0)
> +
> +#define SDHCI_CDNS_HRS10			0x28
> +#define	SDHCI_CDNS_HRS10_HCSDCLKADJ		GENMASK(19, 16)
> +
> +#define SDHCI_CDNS_HRS11			0x2c
> +/*Reset related*/
> +#define SDHCI_CDNS_SRS11_SW_RESET_ALL		BIT(24)
> +#define SDHCI_CDNS_SRS11_SW_RESET_CMD		BIT(25)
> +#define SDHCI_CDNS_SRS11_SW_RESET_DAT		BIT(26)
> +
> +#define SDHCI_CDNS_HRS16			0x40
> +#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY	GENMASK(31, 28)
> +#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY	GENMASK(27, 24)
> +#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY	GENMASK(23, 20)
> +#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY	GENMASK(19, 16)
> +#define SDHCI_CDNS_HRS16_WRDATA1_DLY		GENMASK(15, 12)
> +#define SDHCI_CDNS_HRS16_WRDATA0_DLY		GENMASK(11, 8)
> +#define SDHCI_CDNS_HRS16_WRCMD1_DLY		GENMASK(7, 4)
> +#define SDHCI_CDNS_HRS16_WRCMD0_DLY		GENMASK(3, 0)
> +
> +/* PHY registers for SD6 controller */
> +#define SDHCI_CDNS_SD6_PHY_DQ_TIMING				0x2000
> +#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON		BIT(31)
> +#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END		GENMASK(29, 27)
> +#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START		GENMASK(26, 24)
> +#define	SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END		GENMASK(2, 0)
> +
> +#define SDHCI_CDNS_SD6_PHY_DQS_TIMING				0x2004
> +#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS		BIT(22)
> +#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS		BIT(21)
> +#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS		BIT(20)
> +#define	SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD		BIT(19)
> +
> +#define SDHCI_CDNS_SD6_PHY_GATE_LPBK				0x2008
> +#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD		BIT(31)
> +#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT	BIT(28)
> +#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL			GENMASK(24, 19)
> +#define	SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON		BIT(6)
> +
> +#define SDHCI_CDNS_SD6_PHY_DLL_MASTER				0x200C
> +#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_BYPASS_MODE		BIT(23)
> +#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_PHASE_DETECT_SEL		GENMASK(22, 20)
> +#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_LOCK_NUM		GENMASK(18, 16)
> +#define	SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_START_POINT		GENMASK(7, 0)
> +
> +#define SDHCI_CDNS_SD6_PHY_DLL_SLAVE				0x2010
> +#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_CMD_DELAY		GENMASK(31, 24)
> +#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WRDQS_DELAY		GENMASK(23, 16)
> +#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY		GENMASK(15, 8)
> +#define	SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY		GENMASK(7, 0)
> +
> +#define SDHCI_CDNS_SD6_PHY_CTRL					0x2080
> +#define	SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING		GENMASK(9, 4)
> +
> +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0				0x2088
> +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV			GENMASK(6, 5)
> +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV_OVR_EN		BIT(4)
> +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW			GENMASK(2, 1)
> +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW_OVR_EN		BIT(0)
> +
>  /* SRS - Slot Register Set (SDHCI-compatible) */
>  #define SDHCI_CDNS_SRS_BASE		0x200
>  
> @@ -59,6 +148,10 @@
>   */
>  #define SDHCI_CDNS_MAX_TUNING_LOOP	40
>  
> +static int tune_val_start = SDHCI_CDNS_TUNE_START;
> +static int tune_val_step = SDHCI_CDNS_TUNE_STEP;
> +static int max_tune_iter = SDHCI_CDNS_TUNE_ITERATIONS;
> +
>  struct sdhci_cdns_priv;
>  
>  struct sdhci_cdns_sd4_phy_param {
> @@ -109,6 +202,558 @@ static const struct sdhci_cdns_sd4_phy_cfg sdhci_cdns_sd4_phy_cfgs[] = {
>  	{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
>  };
>  
> +enum sdhci_cdns_sd6_phy_lock_mode {
> +	SDHCI_CDNS_SD6_PHY_LOCK_MODE_FULL_CLK = 0,
> +	SDHCI_CDNS_SD6_PHY_LOCK_MODE_HALF_CLK = 2,
> +	SDHCI_CDNS_SD6_PHY_LOCK_MODE_SATURATION = 3,
> +};
> +
> +struct sdhci_cdns_sd6_phy_timings {
> +	u32 t_cmd_output_min;
> +	u32 t_cmd_output_max;
> +	u32 t_dat_output_min;
> +	u32 t_dat_output_max;
> +	u32 t_cmd_input_min;
> +	u32 t_cmd_input_max;
> +	u32 t_dat_input_min;
> +	u32 t_dat_input_max;
> +	u32 t_sdclk_min;
> +	u32 t_sdclk_max;
> +};
> +
> +struct sdhci_cdns_sd6_phy_delays {
> +	u32 phy_sdclk_delay;
> +	u32 phy_cmd_o_delay;
> +	u32 phy_dat_o_delay;
> +	u32 iocell_input_delay;
> +	u32 iocell_output_delay;
> +	u32 delay_element_org;
> +	u32 delay_element;
> +};
> +
> +struct sdhci_cdns_sd6_phy_settings {
> +	/* SDHCI_CDNS_SD6_PHY_DLL_SLAVE */
> +	u32 cp_read_dqs_cmd_delay;
> +	u32 cp_read_dqs_delay;
> +	u32 cp_clk_wr_delay;
> +	u32 cp_clk_wrdqs_delay;
> +
> +	/* SDHCI_CDNS_SD6_PHY_DLL_MASTER */
> +	u32 cp_dll_bypass_mode;
> +	u32 cp_dll_start_point;
> +
> +	/* SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0 */
> +	u32 cp_dll_locked_mode;
> +
> +	/* SDHCI_CDNS_SD6_PHY_GATE_LPBK */
> +	u32 cp_gate_cfg_always_on;
> +	u32 cp_sync_method;
> +	u32 cp_rd_del_sel;
> +	u32 cp_sw_half_cycle_shift;
> +	u32 cp_underrun_suppress;
> +
> +	/* SDHCI_CDNS_SD6_PHY_DQ_TIMING */
> +	u32 cp_io_mask_always_on;
> +	u32 cp_io_mask_end;
> +	u32 cp_io_mask_start;
> +	u32 cp_data_select_oe_end;
> +
> +	/* SDHCI_CDNS_SD6_PHY_DQS_TIMING */
> +	u32 cp_use_ext_lpbk_dqs;
> +	u32 cp_use_lpbk_dqs;
> +	u8 cp_use_phony_dqs;
> +	u8 cp_use_phony_dqs_cmd;
> +
> +	/* HRS 09 */
> +	u8 sdhc_extended_rd_mode;
> +	u8 sdhc_extended_wr_mode;
> +	u32 sdhc_rdcmd_en;
> +	u32 sdhc_rddata_en;
> +
> +	/* HRS10 */
> +	u32 sdhc_hcsdclkadj;
> +
> +	/* HRS 07 */
> +	u32 sdhc_idelay_val;
> +	u32 sdhc_rw_compensate;
> +
> +	/* SRS 11 */
> +	u32 sdhc_sdcfsh;
> +	u32 sdhc_sdcfsl;
> +
> +	/* HRS 16 */
> +	u32 sdhc_wrcmd0_dly;
> +	u32 sdhc_wrcmd0_sdclk_dly;
> +	u32 sdhc_wrcmd1_dly;
> +	u32 sdhc_wrcmd1_sdclk_dly;
> +	u32 sdhc_wrdata0_dly;
> +	u32 sdhc_wrdata0_sdclk_dly;
> +	u32 sdhc_wrdata1_dly;
> +	u32 sdhc_wrdata1_sdclk_dly;
> +
> +	u32 hs200_tune_val;
> +	u32 drive;
> +	u32 slew;
> +};
> +
> +struct sdhci_cdns_sd6_phy_intermediate_results {
> +	u32 t_sdmclk_calc;
> +	u32 dll_max_value;
> +};
> +
> +struct sdhci_cdns_sd6_phy {
> +	struct sdhci_cdns_sd6_phy_timings t;
> +	struct sdhci_cdns_sd6_phy_delays d;
> +	u32 t_sdmclk;
> +	struct sdhci_cdns_sd6_phy_settings settings;
> +	struct sdhci_cdns_sd6_phy_intermediate_results vars;
> +	bool ddr;
> +	bool tune_cmd;
> +	bool tune_dat;
> +	bool strobe_cmd;
> +	bool strobe_dat;
> +	int mode;
> +	int t_sdclk;
> +};
> +
> +static void init_hs(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 2000, .t_cmd_output_max = t_sdclk - 6000,
> +		.t_dat_output_min = 2000, .t_dat_output_max = t_sdclk - 6000,
> +		.t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 2500,
> +		.t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 2500,
> +		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_uhs_sdr12(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
> +		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
> +		.t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 1500,
> +		.t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 1500,
> +		.t_sdclk_min = 1000000 / 25, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_uhs_sdr25(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
> +		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
> +		.t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 1500,
> +		.t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 1500,
> +		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_uhs_sdr50(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
> +		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
> +		.t_cmd_input_min = 7500, .t_cmd_input_max = t_sdclk + 1500,
> +		.t_dat_input_min = 7500, .t_dat_input_max = t_sdclk + 1500,
> +		.t_sdclk_min = 1000000 / 100, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_uhs_sdr104(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400,
> +		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 1400,
> +		.t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000,
> +		.t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000,
> +		.t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100
> +	};
> +}
> +
> +static void init_uhs_ddr50(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000,
> +		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000,
> +		.t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 1500,
> +		.t_dat_input_min = 7000, .t_dat_input_max = t_sdclk + 1500,
> +		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_emmc_legacy(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000,
> +		.t_dat_output_min = 3000, .t_dat_output_max = t_sdclk - 3000,
> +		.t_cmd_input_min = 11700, .t_cmd_input_max = t_sdclk + 8300,
> +		.t_dat_input_min = 11700, .t_dat_input_max = t_sdclk + 8300,
> +		.t_sdclk_min = 1000000 / 25, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_emmc_sdr(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000,
> +		.t_dat_output_min = 3000, .t_dat_output_max = t_sdclk - 3000,
> +		.t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 2500,
> +		.t_dat_input_min = 13700, .t_dat_input_max = t_sdclk + 2500,
> +		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_emmc_ddr(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000,
> +		.t_dat_output_min = 2500, .t_dat_output_max = t_sdclk - 2500,
> +		.t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 2500,
> +		.t_dat_input_min = 7000, .t_dat_input_max = t_sdclk + 1500,
> +		.t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4
> +	};
> +}
> +
> +static void init_emmc_hs200(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400,
> +		.t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 1400,
> +		.t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000,
> +		.t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000,
> +		.t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100
> +	};
> +}
> +
> +/* HS400 and HS400ES */
> +static void init_emmc_hs400(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk)
> +{
> +	*t = (struct sdhci_cdns_sd6_phy_timings){
> +		.t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400,
> +		.t_dat_output_min = 400, .t_dat_output_max = t_sdclk - 400,
> +		.t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000,
> +		.t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000,
> +		.t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100
> +	};
> +}
> +
> +static void (*init_timings[])(struct sdhci_cdns_sd6_phy_timings*, int) = {
> +	&init_hs, &init_emmc_legacy, &init_emmc_sdr,
> +	&init_emmc_ddr, &init_emmc_hs200, &init_emmc_hs400,
> +	&init_uhs_sdr12, &init_uhs_sdr25, &init_uhs_sdr50,
> +	&init_uhs_sdr104, &init_uhs_ddr50
> +};
> +
> +static u32 read_dqs_cmd_delay, clk_wrdqs_delay, clk_wr_delay, read_dqs_delay;
> +
> +static u32 sdhci_cdns_sd6_get_mode(struct sdhci_host *host, unsigned int timing);
> +
> +static int sdhci_cdns_sd6_phy_lock_dll(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	u32 delay_element = phy->d.delay_element_org;
> +	u32 delay_elements_in_sdmclk;
> +	enum sdhci_cdns_sd6_phy_lock_mode mode;
> +
> +	delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk, delay_element);
> +	if (delay_elements_in_sdmclk > 256) {
> +		delay_element *= 2;
> +		delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk,
> +							delay_element);
> +
> +		if (delay_elements_in_sdmclk > 256)
> +			return -1;
> +
> +		mode = SDHCI_CDNS_SD6_PHY_LOCK_MODE_HALF_CLK;
> +		phy->vars.dll_max_value = 127;
> +	} else {
> +		mode = SDHCI_CDNS_SD6_PHY_LOCK_MODE_FULL_CLK;
> +		phy->vars.dll_max_value = 255;
> +	}
> +
> +	phy->vars.t_sdmclk_calc = delay_element * delay_elements_in_sdmclk;
> +	phy->d.delay_element = delay_element;
> +	phy->settings.cp_dll_locked_mode = mode;
> +	phy->settings.cp_dll_bypass_mode = 0;
> +
> +	return 0;
> +}
> +
> +static void sdhci_cdns_sd6_phy_dll_bypass(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	phy->vars.dll_max_value = 256;
> +	phy->settings.cp_dll_bypass_mode = 1;
> +	phy->settings.cp_dll_locked_mode =
> +		SDHCI_CDNS_SD6_PHY_LOCK_MODE_SATURATION;
> +}
> +
> +static void sdhci_cdns_sd6_phy_configure_dll(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	if (phy->settings.sdhc_extended_wr_mode == 0) {
> +		if (sdhci_cdns_sd6_phy_lock_dll(phy) == 0)
> +			return;
> +	}
> +	sdhci_cdns_sd6_phy_dll_bypass(phy);
> +}
> +
> +static void sdhci_cdns_sd6_phy_calc_out(struct sdhci_cdns_sd6_phy *phy,
> +					bool cmd_not_dat)
> +{
> +	u32 wr0_dly = 0, wr1_dly = 0, output_min, output_max, phy_o_delay,
> +	    clk_wr_delay = 0, wr0_sdclk_dly = 0, wr1_sdclk_dly = 0;
> +	bool data_ddr = phy->ddr && !cmd_not_dat;
> +	int t;
> +
> +	if (cmd_not_dat) {
> +		output_min = phy->t.t_cmd_output_min;
> +		output_max = phy->t.t_cmd_output_max;
> +		phy_o_delay = phy->d.phy_cmd_o_delay;
> +	} else {
> +		output_min = phy->t.t_dat_output_min;
> +		output_max = phy->t.t_dat_output_max;
> +		phy_o_delay = phy->d.phy_dat_o_delay;
> +	}
> +
> +	if (data_ddr) {
> +		wr0_sdclk_dly = 1;
> +		wr1_sdclk_dly = 1;
> +	}
> +
> +	t = phy_o_delay - phy->d.phy_sdclk_delay - output_min;
> +	if (t < 0 && phy->settings.sdhc_extended_wr_mode == 1) {
> +		u32 n_half_cycle = DIV_ROUND_UP(-t * 2, phy->t_sdmclk);
> +
> +		wr0_dly = (n_half_cycle + 1) / 2;
> +		if (data_ddr)
> +			wr1_dly = (n_half_cycle + 1) / 2;
> +		else
> +			wr1_dly = (n_half_cycle + 1) % 2 + wr0_dly - 1;
> +	}
> +
> +	if (phy->settings.sdhc_extended_wr_mode == 0) {
> +		u32 out_hold, out_setup, out_hold_margin;
> +		u32 n;
> +
> +		if (!data_ddr)
> +			wr0_dly = 1;
> +
> +		out_setup = output_max;
> +		out_hold = output_min;
> +		out_hold_margin = DIV_ROUND_UP(out_setup - out_hold, 4);
> +		out_hold += out_hold_margin;
> +
> +		if (phy->settings.cp_dll_bypass_mode == 0)
> +			n = DIV_ROUND_UP(256 * out_hold, phy->vars.t_sdmclk_calc);
> +		else
> +			n = DIV_ROUND_UP(out_hold, phy->d.delay_element) - 1;
> +
> +		if (n <= phy->vars.dll_max_value)
> +			clk_wr_delay = n;
> +		else
> +			clk_wr_delay = 255;
> +	} else {
> +		/*  sdhc_extended_wr_mode = 1 - PHY IO cell work in SDR mode */
> +		clk_wr_delay = 0;
> +	}
> +
> +	if (cmd_not_dat) {
> +		phy->settings.sdhc_wrcmd0_dly = wr0_dly;
> +		phy->settings.sdhc_wrcmd1_dly = wr1_dly;
> +		phy->settings.cp_clk_wrdqs_delay = clk_wr_delay;
> +		phy->settings.sdhc_wrcmd0_sdclk_dly = wr0_sdclk_dly;
> +		phy->settings.sdhc_wrcmd1_sdclk_dly = wr1_sdclk_dly;
> +	} else {
> +		phy->settings.sdhc_wrdata0_dly = wr0_dly;
> +		phy->settings.sdhc_wrdata1_dly = wr1_dly;
> +		phy->settings.cp_clk_wr_delay = clk_wr_delay;
> +		phy->settings.sdhc_wrdata0_sdclk_dly = wr0_sdclk_dly;
> +		phy->settings.sdhc_wrdata1_sdclk_dly = wr1_sdclk_dly;
> +	}
> +}
> +
> +static void sdhci_cdns_sd6_phy_calc_cmd_out(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	sdhci_cdns_sd6_phy_calc_out(phy, true);
> +}
> +
> +static void sdhci_cdns_sd6_phy_calc_cmd_in(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	phy->settings.cp_io_mask_end =
> +		((phy->d.iocell_output_delay + phy->d.iocell_input_delay) * 2)
> +		/ phy->t_sdmclk;
> +
> +	if (phy->settings.cp_io_mask_end >= 8)
> +		phy->settings.cp_io_mask_end = 7;
> +
> +	if (phy->strobe_cmd && phy->settings.cp_io_mask_end > 0)
> +		phy->settings.cp_io_mask_end--;
> +
> +	if (phy->strobe_cmd) {
> +		phy->settings.cp_use_phony_dqs_cmd = 0;
> +		phy->settings.cp_read_dqs_cmd_delay = 64;
> +	} else {
> +		phy->settings.cp_use_phony_dqs_cmd = 1;
> +		phy->settings.cp_read_dqs_cmd_delay = 0;
> +	}
> +
> +	if ((phy->mode == MMC_TIMING_MMC_HS400 && !phy->strobe_cmd) ||
> +	    phy->mode == MMC_TIMING_MMC_HS200)
> +		phy->settings.cp_read_dqs_cmd_delay =
> +			phy->settings.hs200_tune_val;
> +}
> +
> +static void sdhci_cdns_sd6_phy_calc_dat_in(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	u32 hcsdclkadj = 0;
> +
> +	if (phy->strobe_dat) {
> +		phy->settings.cp_use_phony_dqs = 0;
> +		phy->settings.cp_read_dqs_delay = 64;
> +	} else {
> +		phy->settings.cp_use_phony_dqs = 1;
> +		phy->settings.cp_read_dqs_delay = 0;
> +	}
> +
> +	if (phy->mode == MMC_TIMING_MMC_HS200)
> +		phy->settings.cp_read_dqs_delay =
> +			phy->settings.hs200_tune_val;
> +
> +	if (phy->strobe_dat) {
> +		/* dqs loopback input via IO cell */
> +		hcsdclkadj += phy->d.iocell_input_delay;
> +		/* dfi_dqs_in: mem_dqs -> clean_dqs_mod; delay of hic_dll_dqs_nand2 */
> +		hcsdclkadj += phy->d.delay_element / 2;
> +		/* delay line */
> +		hcsdclkadj += phy->t_sdclk / 2;
> +		/* PHY FIFO write pointer */
> +		hcsdclkadj += phy->t_sdclk / 2 + phy->d.delay_element;
> +		/* 1st synchronizer */
> +		hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk)
> +			* phy->t_sdmclk - hcsdclkadj;
> +		/*
> +		 * 2nd synchronizer + PHY FIFO read pointer + PHY rddata
> +		 * + PHY rddata registered, + FIFO 1st ciu_en
> +		 */
> +		hcsdclkadj += 5 * phy->t_sdmclk;
> +		/* FIFO 2st ciu_en */
> +		hcsdclkadj += phy->t_sdclk;
> +
> +		hcsdclkadj /= phy->t_sdclk;
> +	} else {
> +		u32 n;
> +
> +		/* rebar PHY delay */
> +		hcsdclkadj += 2 * phy->t_sdmclk;
> +		/* rebar output via IO cell */
> +		hcsdclkadj += phy->d.iocell_output_delay;
> +		/* dqs loopback input via IO cell */
> +		hcsdclkadj += phy->d.iocell_input_delay;
> +		/* dfi_dqs_in: mem_dqs -> clean_dqs_mod delay of hic_dll_dqs_nand2 */
> +		hcsdclkadj += phy->d.delay_element / 2;
> +		/* dll: one delay element between SIGI_0 and SIGO_0 */
> +		hcsdclkadj += phy->d.delay_element;
> +		/* dfi_dqs_in: mem_dqs_delayed -> clk_dqs delay of hic_dll_dqs_nand2 */
> +		hcsdclkadj += phy->d.delay_element / 2;
> +		/* deskew DLL: clk_dqs -> clk_dqN: one delay element */
> +		hcsdclkadj += phy->d.delay_element;
> +
> +		if (phy->t_sdclk == phy->t_sdmclk)
> +			n = (hcsdclkadj - 2 * phy->t_sdmclk) / phy->t_sdclk;
> +		else
> +			n = hcsdclkadj / phy->t_sdclk;
> +
> +		/* phase shift within one t_sdclk clock cycle caused by rebar - lbk dqs delay */
> +		hcsdclkadj = hcsdclkadj % phy->t_sdclk;
> +		/* PHY FIFO write pointer */
> +		hcsdclkadj += phy->t_sdclk / 2;
> +		/* 1st synchronizer */
> +		hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk)
> +			* phy->t_sdmclk - hcsdclkadj;
> +		/*
> +		 * 2nd synchronizer + PHY FIFO read pointer + PHY rddata
> +		 * + PHY rddata registered
> +		 */
> +		hcsdclkadj += 4 * phy->t_sdmclk;
> +
> +		if ((phy->t_sdclk / phy->t_sdmclk) > 1) {
> +			u32 tmp1, tmp2;
> +
> +			tmp1 = hcsdclkadj;
> +			tmp2 = (hcsdclkadj / phy->t_sdclk) * phy->t_sdclk
> +				+ phy->t_sdclk - phy->t_sdmclk;
> +			if (tmp1 == tmp2)
> +				tmp2 += phy->t_sdclk;
> +
> +			/* FIFO aligns to clock cycle before ciu_en */
> +			hcsdclkadj += tmp2 - tmp1;
> +		}
> +
> +		/* FIFO 1st ciu_en */
> +		hcsdclkadj += phy->t_sdmclk;
> +		/* FIFO 2nd ciu_en */
> +		hcsdclkadj += phy->t_sdclk;
> +
> +		hcsdclkadj /= phy->t_sdclk;
> +
> +		hcsdclkadj += n;
> +
> +		if ((phy->t_sdclk / phy->t_sdmclk) >= 2) {
> +			if (phy->mode == MMC_TIMING_UHS_DDR50 ||
> +			    phy->mode == MMC_TIMING_MMC_DDR52)
> +				hcsdclkadj -= 2;
> +			else
> +				hcsdclkadj -= 1;
> +		} else if ((phy->t_sdclk / phy->t_sdmclk) == 1) {
> +			hcsdclkadj += 2;
> +		}
> +
> +		if (phy->tune_dat)
> +			hcsdclkadj -= 1;
> +	}
> +
> +	if (hcsdclkadj > 15)
> +		hcsdclkadj = 15;
> +
> +	phy->settings.sdhc_hcsdclkadj = hcsdclkadj;
> +}
> +
> +static void sdhci_cdns_sd6_phy_calc_dat_out(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	sdhci_cdns_sd6_phy_calc_out(phy, false);
> +}
> +
> +static void sdhci_cdns_sd6_phy_calc_io(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	u32 rw_compensate;
> +
> +	rw_compensate = (phy->d.iocell_input_delay + phy->d.iocell_output_delay)
> +		/ phy->t_sdmclk + phy->settings.sdhc_wrdata0_dly + 5 + 3;
> +
> +	phy->settings.sdhc_idelay_val = (2 * phy->d.iocell_input_delay)
> +		/ phy->t_sdmclk;
> +
> +	phy->settings.cp_io_mask_start = 0;
> +	if (phy->t_sdclk == phy->t_sdmclk && rw_compensate > 10)
> +		phy->settings.cp_io_mask_start = 2 * (rw_compensate - 10);
> +
> +	if (phy->mode == MMC_TIMING_UHS_SDR104)
> +		phy->settings.cp_io_mask_start++;
> +
> +	if (phy->t_sdclk == phy->t_sdmclk && phy->mode == MMC_TIMING_UHS_SDR50)
> +		phy->settings.cp_io_mask_start++;
> +
> +	phy->settings.sdhc_rw_compensate = rw_compensate;
> +}
> +
> +static void sdhci_cdns_sd6_phy_calc_settings(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	sdhci_cdns_sd6_phy_calc_cmd_out(phy);
> +	sdhci_cdns_sd6_phy_calc_cmd_in(phy);
> +	sdhci_cdns_sd6_phy_calc_dat_out(phy);
> +	sdhci_cdns_sd6_phy_calc_dat_in(phy);
> +	sdhci_cdns_sd6_phy_calc_io(phy);
> +}
> +
>  static int sdhci_cdns_sd4_write_phy_reg(struct sdhci_cdns_priv *priv,
>  					u8 addr, u8 data)
>  {
> @@ -183,7 +828,276 @@ static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_priv *priv)
>  		if (ret)
>  			return ret;
>  	}
> +	return 0;
> +}
> +
> +static u32 sdhci_cdns_sd6_read_phy_reg(struct sdhci_cdns_priv *priv,
> +				       u32 addr)
> +{
> +	writel(FIELD_PREP(SDHCI_CDNS_SD6_HRS04_ADDR, addr),
> +	       priv->hrs_addr + SDHCI_CDNS_HRS04);
> +	return readl(priv->hrs_addr + SDHCI_CDNS_HRS05);
> +}
> +
> +static void sdhci_cdns_sd6_write_phy_reg(struct sdhci_cdns_priv *priv,
> +					 u32 addr, u32 data)
> +{
> +	writel(FIELD_PREP(SDHCI_CDNS_SD6_HRS04_ADDR, addr),
> +	       priv->hrs_addr + SDHCI_CDNS_HRS04);
> +	writel(data, priv->hrs_addr + SDHCI_CDNS_HRS05);
> +}
> +
> +static int sdhci_cdns_sd6_dll_reset(struct sdhci_cdns_priv *priv, bool reset)
> +{
> +	u32 reg;
> +	int ret = 0;
> +
> +	reg = readl(priv->hrs_addr + SDHCI_CDNS_HRS09);
> +	if (reset)
> +		reg &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
> +	else
> +		reg |= SDHCI_CDNS_HRS09_PHY_SW_RESET;
> +
> +	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS09);
> +
> +	if (!reset)
> +		ret = readl_poll_timeout(priv->hrs_addr + SDHCI_CDNS_HRS09,
> +					 reg,
> +					 (reg &
> +					  SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE),
> +					 0, 0);
> +
> +	return ret;
> +}
> +
> +static void sdhci_cdns_sd6_calc_phy(struct sdhci_cdns_sd6_phy *phy)
> +{
> +	if (phy->mode == MMC_TIMING_MMC_HS) {
> +		phy->settings.cp_clk_wr_delay = 0;
> +		phy->settings.cp_clk_wrdqs_delay = 0;
> +		phy->settings.cp_data_select_oe_end = 1;
> +		phy->settings.cp_dll_bypass_mode = 1;
> +		phy->settings.cp_dll_locked_mode = 3;
> +		phy->settings.cp_dll_start_point = 4;
> +		phy->settings.cp_gate_cfg_always_on = 1;
> +		phy->settings.cp_io_mask_always_on = 0;
> +		phy->settings.cp_io_mask_end = 0;
> +		phy->settings.cp_io_mask_start = 0;
> +		phy->settings.cp_rd_del_sel = 52;
> +		phy->settings.cp_read_dqs_cmd_delay = 0;
> +		phy->settings.cp_read_dqs_delay = 0;
> +		phy->settings.cp_sw_half_cycle_shift = 0;
> +		phy->settings.cp_sync_method = 1;
> +		phy->settings.cp_underrun_suppress = 1;
> +		phy->settings.cp_use_ext_lpbk_dqs = 1;
> +		phy->settings.cp_use_lpbk_dqs = 1;
> +		phy->settings.cp_use_phony_dqs = 1;
> +		phy->settings.cp_use_phony_dqs_cmd = 1;
> +		phy->settings.sdhc_extended_rd_mode = 1;
> +		phy->settings.sdhc_extended_wr_mode = 1;
> +		phy->settings.sdhc_hcsdclkadj = 2;
> +		phy->settings.sdhc_idelay_val = 0;
> +		phy->settings.sdhc_rdcmd_en = 1;
> +		phy->settings.sdhc_rddata_en = 1;
> +		phy->settings.sdhc_rw_compensate = 9;
> +		phy->settings.sdhc_sdcfsh = 0;
> +		phy->settings.sdhc_sdcfsl = 4;
> +		phy->settings.sdhc_wrcmd0_dly = 1;
> +		phy->settings.sdhc_wrcmd0_sdclk_dly = 0;
> +		phy->settings.sdhc_wrcmd1_dly = 0;
> +		phy->settings.sdhc_wrcmd1_sdclk_dly = 0;
> +		phy->settings.sdhc_wrdata0_dly = 1;
> +		phy->settings.sdhc_wrdata0_sdclk_dly = 0;
> +		phy->settings.sdhc_wrdata1_dly = 0;
> +		phy->settings.sdhc_wrdata1_sdclk_dly = 0;
> +	}
> +}
> +
> +static
> +int sdhci_cdns_sd6_get_delay_params(struct device *dev,
> +				    struct sdhci_cdns_priv *priv)
> +{
> +	struct sdhci_cdns_sd6_phy *phy = priv->phy;
> +	int ret;
> +
> +	of_property_read_u32(dev->of_node, "cdns,iocell-input-delay-ps",
> +			     &phy->d.iocell_input_delay);
> +	of_property_read_u32(dev->of_node, "cdns,iocell-output-delay-ps",
> +			     &phy->d.iocell_output_delay);
> +	of_property_read_u32(dev->of_node, "cdns,delay-element-ps",
> +			     &phy->d.delay_element);
> +	ret = of_property_read_u32(dev->of_node, "cdns,read-dqs-cmd-delay-ps",
> +				   &phy->settings.cp_read_dqs_cmd_delay);
> +	if (ret)
> +		phy->settings.cp_read_dqs_cmd_delay = DEFAULT_CMD_DELAY;
> +
> +	ret = of_property_read_u32(dev->of_node, "cdns,tune-val-start-ps",
> +				   &tune_val_start);
> +	if (ret)
> +		tune_val_start = SDHCI_CDNS_TUNE_START;
> +
> +	ret = of_property_read_u32(dev->of_node, "cdns,tune-val-step-ps",
> +				   &tune_val_step);
> +	if (ret)
> +		tune_val_step = SDHCI_CDNS_TUNE_STEP;
> +
> +	read_dqs_cmd_delay = phy->settings.cp_read_dqs_cmd_delay;
> +	clk_wrdqs_delay = phy->settings.cp_clk_wrdqs_delay;
> +	clk_wr_delay = phy->settings.cp_clk_wr_delay;
> +	read_dqs_delay = phy->settings.cp_read_dqs_delay;
> +	return 0;
> +}
> +
> +static int sdhci_cdns_sd6_phy_init(struct sdhci_cdns_priv *priv)
> +{
> +	int ret;
> +	u32 reg;
> +	struct sdhci_cdns_sd6_phy *phy = priv->phy;
> +
> +	sdhci_cdns_sd6_dll_reset(priv, true);
> +
> +	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQS_TIMING);
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD;
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS,
> +			phy->settings.cp_use_ext_lpbk_dqs);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS,
> +			phy->settings.cp_use_lpbk_dqs);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS,
> +			  phy->settings.cp_use_phony_dqs);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD,
> +			  phy->settings.cp_use_phony_dqs_cmd);
> +	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQS_TIMING, reg);
> +
> +	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GATE_LPBK);
> +	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON;
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD,
> +			phy->settings.cp_sync_method);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT,
> +			phy->settings.cp_sw_half_cycle_shift);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL,
> +			phy->settings.cp_rd_del_sel);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON,
> +			phy->settings.cp_gate_cfg_always_on);
> +	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GATE_LPBK, reg);
> +
> +	reg = 0x0;
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_BYPASS_MODE,
> +			 phy->settings.cp_dll_bypass_mode);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_PHASE_DETECT_SEL, 2);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_LOCK_NUM, 0);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_START_POINT,
> +			phy->settings.cp_dll_start_point);
> +	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DLL_MASTER, reg);
> +
> +	reg = 0x0;
> +	reg = FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_CMD_DELAY,
> +			 phy->settings.cp_read_dqs_cmd_delay);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WRDQS_DELAY,
> +			  phy->settings.cp_clk_wrdqs_delay);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY,
> +			  phy->settings.cp_clk_wr_delay);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY,
> +			  phy->settings.cp_read_dqs_delay);
> +	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DLL_SLAVE, reg);
> +
> +	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_CTRL);
> +	reg &= ~SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING;
> +	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_CTRL, reg);
> +
> +	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0);
> +	reg &= ~0x77;
> +	reg |= SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV_OVR_EN |
> +		SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW_OVR_EN;
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV,
> +		phy->settings.drive);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW,
> +		phy->settings.slew);
> +	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0, reg);
> +
> +	ret = sdhci_cdns_sd6_dll_reset(priv, false);
> +	if (ret)
> +		return ret;
> +
> +	reg = sdhci_cdns_sd6_read_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQ_TIMING);
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START;
> +	reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END;
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON,
> +			phy->settings.cp_io_mask_always_on);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END,
> +			  phy->settings.cp_io_mask_end);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START,
> +			  phy->settings.cp_io_mask_start);
> +	reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END,
> +			phy->settings.cp_data_select_oe_end);
> +	sdhci_cdns_sd6_write_phy_reg(priv, SDHCI_CDNS_SD6_PHY_DQ_TIMING, reg);
> +
> +	reg = readl(priv->hrs_addr + SDHCI_CDNS_HRS09);
> +	if (phy->settings.sdhc_extended_wr_mode)
> +		reg |= SDHCI_CDNS_HRS09_EXTENDED_WR_MODE;
> +	else
> +		reg &= ~SDHCI_CDNS_HRS09_EXTENDED_WR_MODE;
> +
> +	if (phy->settings.sdhc_extended_rd_mode)
> +		reg |= SDHCI_CDNS_HRS09_EXTENDED_RD_MODE;
> +	else
> +		reg &= ~SDHCI_CDNS_HRS09_EXTENDED_RD_MODE;
> +
> +	if (phy->settings.sdhc_rddata_en)
> +		reg |= SDHCI_CDNS_HRS09_RDDATA_EN;
> +	else
> +		reg &= ~SDHCI_CDNS_HRS09_RDDATA_EN;
> +
> +	if (phy->settings.sdhc_rdcmd_en)
> +		reg |= SDHCI_CDNS_HRS09_RDCMD_EN;
> +	else
> +		reg &= ~SDHCI_CDNS_HRS09_RDCMD_EN;
> +
> +	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS09);
> +
> +	writel(0x30004, priv->hrs_addr + SDHCI_CDNS_HRS02);
> +
> +	reg = 0x0;
> +	reg = FIELD_PREP(SDHCI_CDNS_HRS10_HCSDCLKADJ, phy->settings.sdhc_hcsdclkadj);
> +	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS10);
> +
> +	if (phy->mode != MMC_TIMING_MMC_HS && phy->mode != MMC_TIMING_MMC_DDR52) {
> +		reg = 0x0;
> +		reg = FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY,
> +				 phy->settings.sdhc_wrdata1_sdclk_dly);
> +		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY,
> +				  phy->settings.sdhc_wrdata0_sdclk_dly);
> +		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY,
> +				  phy->settings.sdhc_wrcmd1_sdclk_dly);
> +		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY,
> +				  phy->settings.sdhc_wrcmd0_sdclk_dly);
> +		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_DLY,
> +				  phy->settings.sdhc_wrdata1_dly);
> +		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_DLY,
> +				  phy->settings.sdhc_wrdata0_dly);
> +		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_DLY,
> +				  phy->settings.sdhc_wrcmd1_dly);
> +		reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_DLY,
> +				  phy->settings.sdhc_wrcmd0_dly);
> +	} else {
> +		reg = 0x202;
> +	}
>  
> +	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS16);
> +
> +	reg = 0x0;
> +	reg = FIELD_PREP(SDHCI_CDNS_HRS07_RW_COMPENSATE,
> +			 phy->settings.sdhc_rw_compensate);
> +	reg |= FIELD_PREP(SDHCI_CDNS_HRS07_IDELAY_VAL,
> +			 phy->settings.sdhc_idelay_val);
> +	writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS07);
>  	return 0;
>  }
>  
> @@ -194,6 +1108,19 @@ static void *sdhci_cdns_priv(struct sdhci_host *host)
>  	return sdhci_pltfm_priv(pltfm_host);
>  }
>  
> +static int sdhci_cdns_sd6_set_tune_val(struct sdhci_host *host,
> +				       unsigned int val)
> +{
> +	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> +	struct sdhci_cdns_sd6_phy *phy = priv->phy;
> +
> +	phy->settings.hs200_tune_val = val;
> +	phy->settings.cp_read_dqs_cmd_delay = val;
> +	phy->settings.cp_read_dqs_delay = val;
> +
> +	return sdhci_cdns_sd6_phy_init(priv);
> +}
> +
>  static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
>  {
>  	/*
> @@ -203,6 +1130,11 @@ static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
>  	return host->max_clk;
>  }
>  
> +static unsigned int sdhci_cdns_get_max_clock(struct sdhci_host *host)
> +{
> +	return SDMCLK_MAX_FREQ;
> +}
> +
>  static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
>  {
>  	u32 tmp;
> @@ -222,6 +1154,118 @@ static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
>  	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
>  }
>  
> +static int sdhci_cdns_sd6_phy_update_timings(struct sdhci_host *host)
> +{
> +	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> +	struct sdhci_cdns_sd6_phy *phy = priv->phy;
> +	int t_sdmclk = phy->t_sdmclk;
> +	int mode;
> +
> +	mode = sdhci_cdns_sd6_get_mode(host, host->mmc->ios.timing);
> +	/* initialize input */
> +	init_timings[mode](&phy->t, phy->t_sdclk);
> +
> +	phy->mode = host->mmc->ios.timing;
> +	phy->strobe_dat = false;
> +
> +	switch (phy->mode) {
> +	case MMC_TIMING_UHS_SDR104:
> +		phy->tune_cmd = true;
> +		phy->tune_dat = true;
> +		break;
> +	case MMC_TIMING_UHS_DDR50:
> +		phy->ddr = true;
> +		break;
> +	case MMC_TIMING_MMC_DDR52:
> +		phy->ddr = true;
> +		break;
> +	case MMC_TIMING_MMC_HS200:
> +		phy->tune_dat = true;
> +		phy->tune_cmd = true;
> +		break;
> +	case MMC_TIMING_MMC_HS400:
> +		phy->tune_cmd = true;
> +		phy->ddr = true;
> +		phy->strobe_dat = true;
> +		break;
> +	}
> +
> +	if (priv->enhanced_strobe)
> +		phy->strobe_cmd = true;
> +
> +	phy->d.phy_sdclk_delay = 2 * t_sdmclk;
> +	phy->d.phy_cmd_o_delay = 2 * t_sdmclk + t_sdmclk / 2;
> +	phy->d.phy_dat_o_delay = 2 * t_sdmclk + t_sdmclk / 2;
> +
> +	if (phy->t_sdclk == phy->t_sdmclk) {
> +		phy->settings.sdhc_extended_wr_mode = 0;
> +		phy->settings.sdhc_extended_rd_mode = 0;
> +	} else {
> +		phy->settings.sdhc_extended_wr_mode = 1;
> +		phy->settings.sdhc_extended_rd_mode = 1;
> +	}
> +
> +	phy->settings.cp_gate_cfg_always_on = 1;
> +
> +	sdhci_cdns_sd6_phy_configure_dll(phy);
> +
> +	sdhci_cdns_sd6_phy_calc_settings(phy);
> +
> +	return 0;
> +}
> +
> +static u32 sdhci_cdns_sd6_get_mode(struct sdhci_host *host,
> +				   unsigned int timing)
> +{
> +	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> +	u32 mode;
> +
> +	switch (timing) {
> +	case MMC_TIMING_MMC_HS:
> +		mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
> +		break;
> +	case MMC_TIMING_MMC_DDR52:
> +		mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
> +		break;
> +	case MMC_TIMING_MMC_HS200:
> +		mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
> +		break;
> +	case MMC_TIMING_MMC_HS400:
> +		if (priv->enhanced_strobe)
> +			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES;
> +		else
> +			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
> +		break;
> +	case MMC_TIMING_SD_HS:
> +		mode = SDHCI_CDNS_HRS06_MODE_SD;
> +		break;
> +	default:
> +		mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
> +		break;
> +	}
> +
> +	return mode;
> +}
> +
> +static void sdhci_cdns_sd6_set_clock(struct sdhci_host *host,
> +				     unsigned int clock)
> +{
> +	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> +	struct sdhci_cdns_sd6_phy *phy = priv->phy;
> +
> +	phy->t_sdclk = DIV_ROUND_DOWN_ULL(1e12, clock);
> +
> +	pr_debug("%s %d %d\n", __func__, phy->mode, clock);
> +
> +	if (sdhci_cdns_sd6_phy_update_timings(host))
> +		pr_debug("%s: update timings failed\n", __func__);
> +
> +	if (sdhci_cdns_sd6_phy_init(priv))
> +		pr_debug("%s: phy init failed\n", __func__);
> +
> +	sdhci_set_clock(host, clock);
> +}
> +
>  static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
>  				    struct sdhci_cdns_priv *priv)
>  {
> @@ -243,6 +1287,106 @@ static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int sdhci_cdns_sd6_phy_probe(struct platform_device *pdev,
> +				    struct sdhci_cdns_priv *priv)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct sdhci_cdns_sd6_phy *phy;
> +	u32 val;
> +	struct clk *clk;
> +	int ret;
> +	const char *mode_name;
> +
> +	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
> +	if (!phy)
> +		return -ENOMEM;
> +
> +	clk = devm_clk_get(dev, "sdmclk");
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "sdmclk get error\n");
> +		return PTR_ERR(clk);
> +	}
> +
> +	val = clk_get_rate(clk);
> +	phy->t_sdmclk = DIV_ROUND_DOWN_ULL(1e12, val);
> +
> +	ret = of_property_read_u32(dev->of_node, "cdns,host_slew",
> +				   &phy->settings.slew);
> +	if (ret)
> +		phy->settings.slew = 3;
> +
> +	ret = of_property_read_u32(dev->of_node, "cdns,host_drive",
> +				   &phy->settings.drive);
> +	if (ret)
> +		phy->settings.drive = 2;
> +
> +	ret = of_property_read_u32(dev->of_node, "cdns,iocell-input-delay",
> +				   &phy->d.iocell_input_delay);
> +	if (ret)
> +		phy->d.iocell_input_delay = 2500;
> +
> +	ret = of_property_read_u32(dev->of_node, "cdns,iocell-output-delay",
> +				   &phy->d.iocell_output_delay);
> +	if (ret)
> +		phy->d.iocell_output_delay = 2500;
> +
> +	ret = of_property_read_u32(dev->of_node, "cdns,delay-element",
> +				   &phy->d.delay_element);
> +	if (ret)
> +		phy->d.delay_element = 24;
> +
> +	ret = of_property_read_string_index(dev->of_node, "cdns,mode", 0,
> +					    &mode_name);
> +	if (!ret) {
> +		if (!strcmp("emmc_sdr", mode_name))
> +			phy->mode = MMC_TIMING_MMC_HS;
> +		else if (!strcmp("emmc_ddr", mode_name))
> +			phy->mode = MMC_TIMING_MMC_DDR52;
> +		else if (!strcmp("emmc_hs200", mode_name))
> +			phy->mode = MMC_TIMING_MMC_HS200;
> +		else if (!strcmp("emmc_hs400", mode_name))
> +			phy->mode = MMC_TIMING_MMC_HS400;
> +		else if (!strcmp("sd_hs", mode_name))
> +			phy->mode = MMC_TIMING_SD_HS;
> +		else
> +			phy->mode = MMC_TIMING_MMC_HS;
> +	} else {
> +		phy->mode = MMC_TIMING_MMC_HS;
> +	}
> +
> +	phy->d.delay_element_org = phy->d.delay_element;
> +	phy->d.iocell_input_delay = 650;
> +	phy->d.iocell_output_delay = 1800;
> +
> +	switch (phy->mode) {
> +	case MMC_TIMING_MMC_HS:
> +		phy->t_sdclk =  10000;
> +		break;
> +	case MMC_TIMING_MMC_DDR52:
> +		phy->t_sdclk = 10000;
> +		break;
> +	case MMC_TIMING_MMC_HS200:
> +		phy->t_sdclk = 5000;
> +		break;
> +	case MMC_TIMING_MMC_HS400:
> +		phy->t_sdclk = 5000;
> +		break;
> +	case MMC_TIMING_SD_HS:
> +		phy->t_sdclk = 100000;
> +		break;
> +	default:
> +		phy->t_sdclk = 10000;
> +		break;
> +	}
> +
> +	priv->phy = phy;
> +
> +	sdhci_cdns_sd6_get_delay_params(dev, priv);
> +
> +	sdhci_cdns_sd6_calc_phy(phy);
> +	return 0;
> +}
> +
>  static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val)
>  {
>  	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> @@ -276,6 +1420,58 @@ static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val
>  	return 0;
>  }
>  
> +/*
> + * In SD mode, software must not use the hardware tuning and instead perform
> + * an almost identical procedure to eMMC.
> + */
> +static int sdhci_cdns_sd6_execute_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> +	int cur_streak = 0;
> +	int max_streak = 0;
> +	int end_of_streak = 0;
> +	int i, midpoint, iter = 0;
> +
> +	/*
> +	 * Do not execute tuning for UHS_SDR50 or UHS_DDR50.
> +	 * The delay is set by probe, based on the DT properties.
> +	 */
> +	if (host->timing != MMC_TIMING_MMC_HS200 &&
> +	    host->timing != MMC_TIMING_UHS_SDR104)
> +		return 0;
> +
> +	for (i = tune_val_start; iter < max_tune_iter; iter++, i += tune_val_step) {
> +		if (priv->cdns_data->set_tune_val(host, i) ||
> +		    mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
> +			cur_streak = 0;
> +		} else { /* good */
> +			cur_streak++;
> +			if (cur_streak > max_streak) {
> +				max_streak = cur_streak;
> +				end_of_streak = i;
> +				pr_debug("%s (%d-%d = %d)", __func__,
> +					 end_of_streak - ((cur_streak - 1) * tune_val_step),
> +					 end_of_streak, cur_streak);
> +			} else {
> +				pr_debug("%s (%d-%d)", __func__,
> +					 i - ((cur_streak - 1) * tune_val_step), i);
> +			}
> +		}
> +	}
> +
> +	if (!max_streak) {
> +		dev_err(mmc_dev(host->mmc), "no tuning point found\n");
> +		return -EIO;
> +	}
> +
> +	pr_debug("max_streak: %d-%d", end_of_streak - ((max_streak - 1) * tune_val_step),
> +		 end_of_streak);
> +
> +	midpoint = end_of_streak - (((max_streak - 1) * tune_val_step) / 2);
> +
> +	return priv->cdns_data->set_tune_val(host, midpoint);
> +}
> +
>  /*
>   * In SD mode, software must not use the hardware tuning and instead perform
>   * an almost identical procedure to eMMC.
> @@ -338,11 +1534,15 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
>  		else
>  			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
>  		break;
> -	default:
> +	case MMC_TIMING_SD_HS:
>  		mode = SDHCI_CDNS_HRS06_MODE_SD;
>  		break;
> +	default:
> +		mode = SDHCI_CDNS_HRS06_MODE_LEGACY;
> +		break;
>  	}
>  
> +	pr_debug("%s mode %d timing %d\n", __func__, mode, timing);
>  	sdhci_cdns_set_emmc_mode(priv, mode);
>  
>  	/* For SD, fall back to the default handler */
> @@ -350,6 +1550,24 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
>  		sdhci_set_uhs_signaling(host, timing);
>  }
>  
> +static void sdhci_cdns_sd6_set_uhs_signaling(struct sdhci_host *host,
> +					     unsigned int timing)
> +{
> +	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> +	struct sdhci_cdns_sd6_phy *phy = priv->phy;
> +
> +	sdhci_cdns_set_uhs_signaling(host, timing);
> +
> +	if ((phy->mode == -1) || (phy->t_sdclk == -1))
> +		return;
> +
> +	if (sdhci_cdns_sd6_phy_update_timings(host))
> +		pr_debug("%s: update timings failed\n", __func__);
> +
> +	if (sdhci_cdns_sd6_phy_init(priv))
> +		pr_debug("%s: phy init failed\n", __func__);
> +}
> +
>  static const struct sdhci_ops sdhci_cdns_sd4_ops = {
>  	.set_clock = sdhci_set_clock,
>  	.get_timeout_clock = sdhci_cdns_get_timeout_clock,
> @@ -359,6 +1577,16 @@ static const struct sdhci_ops sdhci_cdns_sd4_ops = {
>  	.set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
>  };
>  
> +static const struct sdhci_ops sdhci_cdns_sd6_ops = {
> +	.get_max_clock = sdhci_cdns_get_max_clock,
> +	.set_clock = sdhci_cdns_sd6_set_clock,
> +	.get_timeout_clock = sdhci_cdns_get_timeout_clock,
> +	.set_bus_width = sdhci_set_bus_width,
> +	.reset = sdhci_reset,
> +	.platform_execute_tuning = sdhci_cdns_sd6_execute_tuning,
> +	.set_uhs_signaling = sdhci_cdns_sd6_set_uhs_signaling,
> +};
> +
>  static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
>  	.ops = &sdhci_cdns_sd4_ops,
>  	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
> @@ -372,17 +1600,32 @@ static const struct sdhci_pltfm_data sdhci_cdns_sd4_pltfm_data = {
>  	.ops = &sdhci_cdns_sd4_ops,
>  };
>  
> +static const struct sdhci_pltfm_data sdhci_cdns_sd6_pltfm_data = {
> +	.ops = &sdhci_cdns_sd6_ops,
> +};
> +
>  static const struct sdhci_cdns_data sdhci_cdns_sd4_data = {
>  	.phy_init = sdhci_cdns_sd4_phy_init,
>  	.set_tune_val = sdhci_cdns_sd4_set_tune_val,
>  };
>  
> +static const struct sdhci_cdns_data sdhci_cdns_sd6_data = {
> +	.phy_init = sdhci_cdns_sd6_phy_init,
> +	.set_tune_val = sdhci_cdns_sd6_set_tune_val,
> +};
> +
>  static const struct sdhci_cdns_of_data sdhci_cdns_sd4_of_data = {
>  	.pltfm_data = &sdhci_cdns_sd4_pltfm_data,
>  	.cdns_data = &sdhci_cdns_sd4_data,
>  	.phy_probe = sdhci_cdns_sd4_phy_probe,
>  };
>  
> +static const struct sdhci_cdns_of_data sdhci_cdns_sd6_of_data = {
> +	.pltfm_data = &sdhci_cdns_sd6_pltfm_data,
> +	.cdns_data = &sdhci_cdns_sd6_data,
> +	.phy_probe = sdhci_cdns_sd6_phy_probe,
> +};
> +
>  static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
>  					     struct mmc_ios *ios)
>  {
> @@ -412,7 +1655,6 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
>  	struct clk *clk;
>  	int ret;
>  	struct device *dev = &pdev->dev;
> -	static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;

This change affects all devicesnot just v6

>  
>  	clk = devm_clk_get(dev, NULL);
>  	if (IS_ERR(clk))
> @@ -437,6 +1679,10 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
>  	pltfm_host = sdhci_priv(host);
>  	pltfm_host->clk = clk;
>  
> +	host->clk_mul = 0;
> +	host->max_clk = SDMCLK_MAX_FREQ;
> +	host->quirks |=  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
> +	host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;

These changes affects all devicesnot just v6

>  	priv = sdhci_pltfm_priv(pltfm_host);
>  	priv->hrs_addr = host->ioaddr;
>  	priv->enhanced_strobe = false;
> @@ -444,8 +1690,6 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
>  	host->ioaddr += SDHCI_CDNS_SRS_BASE;
>  	host->mmc_host_ops.hs400_enhanced_strobe =
>  				sdhci_cdns_hs400_enhanced_strobe;
> -	sdhci_enable_v4_mode(host);
> -	__sdhci_read_caps(host, &version, NULL, NULL);
>  
>  	sdhci_get_of_property(pdev);
>  
> @@ -461,6 +1705,8 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
>  	if (ret)
>  		goto free;
>  
> +	sdhci_enable_v4_mode(host);
> +	__sdhci_read_caps(host, NULL, NULL, NULL);
>  	ret = sdhci_add_host(host);
>  	if (ret)
>  		goto free;
> @@ -516,6 +1762,10 @@ static const struct of_device_id sdhci_cdns_match[] = {
>  		.compatible = "cdns,sd4hc",
>  		.data = &sdhci_cdns_sd4_of_data,
>  	},
> +	{
> +		.compatible = "cdns,sd6hc",
> +		.data = &sdhci_cdns_sd6_of_data,
> +	},
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, sdhci_cdns_match);


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support
  2023-02-28 10:53   ` Krzysztof Kozlowski
@ 2023-03-24 15:02     ` Piyush Malgujar
  0 siblings, 0 replies; 15+ messages in thread
From: Piyush Malgujar @ 2023-03-24 15:02 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree, jannadurai,
	cchavva

Hi Krzysztof,

Thanks for the review comments.

On Tue, Feb 28, 2023 at 11:53:51AM +0100, Krzysztof Kozlowski wrote:
> On 27/02/2023 19:31, Piyush Malgujar wrote:
> > From: Jayanthi Annadurai <jannadurai@marvell.com>
> > 
> > Add support for SD6 controller support.
> > 
> > Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> > Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> > ---
> >  .../devicetree/bindings/mmc/cdns,sdhci.yaml   | 24 +++++++++++++++++--
> >  1 file changed, 22 insertions(+), 2 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> > index 8b1a0fdcb5e3e2e8b87d8d7678e37f3dad447fc1..0dba17c4f17f82c8ae68e46225ed72418e8361ff 100644
> > --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> > +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> > @@ -4,7 +4,7 @@
> >  $id: http://devicetree.org/schemas/mmc/cdns,sdhci.yaml#
> >  $schema: http://devicetree.org/meta-schemas/core.yaml#
> >  
> > -title: Cadence SD/SDIO/eMMC Host Controller (SD4HC)
> > +title: Cadence SD/SDIO/eMMC Host Controller (SD4HC, SD6HC)
> >  
> >  maintainers:
> >    - Masahiro Yamada <yamada.masahiro@socionext.com>
> > @@ -18,7 +18,9 @@ properties:
> >        - enum:
> >            - microchip,mpfs-sd4hc
> >            - socionext,uniphier-sd4hc
> > -      - const: cdns,sd4hc
> > +      - enum:
> > +          - cdns,sd4hc
> > +          - cdns,sd6hc
> 
> I see here rather random set of changes in each version of this patch.
> This does not really make sense. You are saying that existing (!!!)
> mpfs-sd4hc is compatible with sd6hc. I think you wanted oneOf here, but
> not sure. Can you explain what is your intention? Your commit msg is
> just one line saying the same as subject, so not really helpful.
> 
> 
> Best regards,
> Krzysztof
>

Yes thank you, it should be oneOf as sd6hc is exclusive. I will correct it in the
next version.

Best Regards,
Piyush

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support
  2023-02-28 15:46   ` Rob Herring
@ 2023-03-24 15:13     ` Piyush Malgujar
  0 siblings, 0 replies; 15+ messages in thread
From: Piyush Malgujar @ 2023-03-24 15:13 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-mmc, linux-kernel, adrian.hunter, ulf.hansson,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree, jannadurai,
	cchavva

Hi Rob,

Thanks for the review comments.

On Tue, Feb 28, 2023 at 09:46:48AM -0600, Rob Herring wrote:
> On Mon, Feb 27, 2023 at 10:31:50AM -0800, Piyush Malgujar wrote:
> > From: Jayanthi Annadurai <jannadurai@marvell.com>
> > 
> > Add support for SD6 controller support.
> 
> On what h/w?
> 

This has been used and tested on Marvell CN10K series SOCs.

> > 
> > Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> > Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> > ---
> >  .../devicetree/bindings/mmc/cdns,sdhci.yaml   | 24 +++++++++++++++++--
> >  1 file changed, 22 insertions(+), 2 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> > index 8b1a0fdcb5e3e2e8b87d8d7678e37f3dad447fc1..0dba17c4f17f82c8ae68e46225ed72418e8361ff 100644
> > --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> > +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml
> > @@ -4,7 +4,7 @@
> >  $id: http://devicetree.org/schemas/mmc/cdns,sdhci.yaml#
> >  $schema: http://devicetree.org/meta-schemas/core.yaml#
> >  
> > -title: Cadence SD/SDIO/eMMC Host Controller (SD4HC)
> > +title: Cadence SD/SDIO/eMMC Host Controller (SD4HC, SD6HC)
> >  
> >  maintainers:
> >    - Masahiro Yamada <yamada.masahiro@socionext.com>
> > @@ -18,7 +18,9 @@ properties:
> >        - enum:
> >            - microchip,mpfs-sd4hc
> >            - socionext,uniphier-sd4hc
> > -      - const: cdns,sd4hc
> > +      - enum:
> > +          - cdns,sd4hc
> > +          - cdns,sd6hc
> 
> Other than FPGA implementations IP vendor compatible strings are pretty 
> much useless. Define a compatible for your h/w.
> 

ok, will use Marvell specific string here. 

> >  
> >    reg:
> >      maxItems: 1
> > @@ -111,6 +113,24 @@ properties:
> >      minimum: 0
> >      maximum: 0x7f
> >  
> > +  cdns,iocell-input-delay-ps:
> > +    description: Delay in ps across the input IO cells
> > +
> > +  cdns,iocell-output-delay-ps:
> > +    description: Delay in ps across the output IO cells
> > +
> > +  cdns,delay-element-ps:
> > +    description: Delay element in ps used for calculating phy timings
> > +
> > +  cdns,read-dqs-cmd-delay-ps:
> > +    description: Command delay used in HS200 tuning
> > +
> > +  cdns,tune-val-start-ps:
> > +    description: Staring value of data delay used in HS200 tuning
> > +
> > +  cdns,tune-val-step-ps:
> > +    description: Incremental value of data delay used in HS200 tuning
> 
> Wouldn't any controller implementation need these possibly? IIRC, we 
> have some common properties for this. If not, survey what we do have and 
> come up with something common. Or you can imply all this from the h/w 
> specific compatible you are going to add.
> 

These parameters are for sd6 cadence controller, will add sd6 in the property
string so to imply it's specific to sd6hc.

> Rob

Thanks,
Piyush

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 2/6] mmc: sdhci-cadence: Restructure the code
  2023-03-03  8:36   ` Adrian Hunter
@ 2023-03-24 15:19     ` Piyush Malgujar
  0 siblings, 0 replies; 15+ messages in thread
From: Piyush Malgujar @ 2023-03-24 15:19 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: linux-mmc, linux-kernel, ulf.hansson, robh+dt,
	krzysztof.kozlowski+dt, yamada.masahiro, devicetree, jannadurai,
	cchavva, Dhananjay Kangude

Hi Adrian,

Thanks for the review comments.

On Fri, Mar 03, 2023 at 10:36:30AM +0200, Adrian Hunter wrote:
> On 27/02/23 20:31, Piyush Malgujar wrote:
> > From: Dhananjay Kangude <dkangude@cadence.com>
> > 
> > Restructured the code, added new structures and functions for
> > SD4 operations. Also this adds some abstraction to the code
> > which will make it modular and adaptable for further SD6 operations.
> > 
> > Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
> > Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
> > Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> > Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
> > ---
> >  drivers/mmc/host/sdhci-cadence.c | 100 ++++++++++++++++++++++++-------
> >  1 file changed, 80 insertions(+), 20 deletions(-)
> > 
> > diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
> > index 5276cdbc652f7faac13bb0244af4926b63dc119a..4f7e63c90e3d68da338b8964f08b7c65ebaf1ffd 100644
> > --- a/drivers/mmc/host/sdhci-cadence.c
> > +++ b/drivers/mmc/host/sdhci-cadence.c
> > @@ -59,16 +59,28 @@
> >   */
> >  #define SDHCI_CDNS_MAX_TUNING_LOOP	40
> >  
> > +struct sdhci_cdns_priv;
> > +
> >  struct sdhci_cdns_sd4_phy_param {
> >  	u8 addr;
> >  	u8 data;
> >  };
> >  
> > +struct sdhci_cdns_data {
> > +	int (*phy_init)(struct sdhci_cdns_priv *priv);
> > +	int (*set_tune_val)(struct sdhci_host *host, unsigned int val);
> > +};
> > +
> > +struct sdhci_cdns_sd4_phy {
> > +	unsigned int nr_phy_params;
> > +	struct sdhci_cdns_sd4_phy_param phy_params[];
> > +};
> > +
> >  struct sdhci_cdns_priv {
> >  	void __iomem *hrs_addr;
> >  	bool enhanced_strobe;
> > -	unsigned int nr_phy_params;
> > -	struct sdhci_cdns_sd4_phy_param phy_params[];
> > +	const struct sdhci_cdns_data *cdns_data;
> 
> Simpler if there is just a pointer to struct sdhci_cdns_of_data
> and get rid of struct sdhci_cdns_data.
> 

Sure, will take care of this.

> > +	void *phy;
> >  };
> >  
> >  struct sdhci_cdns_sd4_phy_cfg {
> > @@ -76,6 +88,13 @@ struct sdhci_cdns_sd4_phy_cfg {
> >  	u8 addr;
> >  };
> >  
> > +struct sdhci_cdns_of_data {
> > +	const struct sdhci_pltfm_data *pltfm_data;
> 
> Kernel style is not to unnecessarily have structures that point to
> other structures or contain other structures.
> 
> Here, please just put the struct not a pointer i.e.
> 
> 	struct sdhci_pltfm_data *pltfm_data;
> 

Ok, so, to use 

	struct sdhci_pltfm_data pltfm_data;

> > +	const struct sdhci_cdns_data *cdns_data;
> 
> Please get rid of struct sdhci_cdns_data. Instead just put its members here
> 

Ok, will modify in next version.

> > +	int (*phy_probe)(struct platform_device *pdev,
> > +			 struct sdhci_cdns_priv *priv);
> > +};
> > +
> >  static const struct sdhci_cdns_sd4_phy_cfg sdhci_cdns_sd4_phy_cfgs[] = {
> >  	{ "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
> >  	{ "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
> > @@ -135,9 +154,9 @@ static unsigned int sdhci_cdns_sd4_phy_param_count(struct device_node *np)
> >  }
> >  
> >  static void sdhci_cdns_sd4_phy_param_parse(struct device_node *np,
> > -					   struct sdhci_cdns_priv *priv)
> > +					   struct sdhci_cdns_sd4_phy *phy)
> >  {
> > -	struct sdhci_cdns_sd4_phy_param *p = priv->phy_params;
> > +	struct sdhci_cdns_sd4_phy_param *p = phy->phy_params;
> >  	u32 val;
> >  	int ret, i;
> >  
> > @@ -156,10 +175,11 @@ static void sdhci_cdns_sd4_phy_param_parse(struct device_node *np,
> >  static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_priv *priv)
> >  {
> >  	int ret, i;
> > +	struct sdhci_cdns_sd4_phy *phy = priv->phy;
> >  
> > -	for (i = 0; i < priv->nr_phy_params; i++) {
> > -		ret = sdhci_cdns_sd4_write_phy_reg(priv, priv->phy_params[i].addr,
> > -						   priv->phy_params[i].data);
> > +	for (i = 0; i < phy->nr_phy_params; i++) {
> > +		ret = sdhci_cdns_sd4_write_phy_reg(priv, phy->phy_params[i].addr,
> > +						   phy->phy_params[i].data);
> >  		if (ret)
> >  			return ret;
> >  	}
> > @@ -202,6 +222,27 @@ static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
> >  	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
> >  }
> >  
> > +static int sdhci_cdns_sd4_phy_probe(struct platform_device *pdev,
> > +				    struct sdhci_cdns_priv *priv)
> > +{
> > +	unsigned int nr_phy_params;
> > +	struct sdhci_cdns_sd4_phy *phy;
> > +	struct device *dev = &pdev->dev;
> > +
> > +	nr_phy_params = sdhci_cdns_sd4_phy_param_count(dev->of_node);
> > +	phy = devm_kzalloc(dev, struct_size(phy, phy_params, nr_phy_params),
> > +			   GFP_KERNEL);
> > +	if (!phy)
> > +		return -ENOMEM;
> > +
> > +	phy->nr_phy_params = nr_phy_params;
> > +
> > +	sdhci_cdns_sd4_phy_param_parse(dev->of_node, phy);
> > +	priv->phy = phy;
> > +
> > +	return 0;
> > +}
> > +
> >  static int sdhci_cdns_sd4_set_tune_val(struct sdhci_host *host, unsigned int val)
> >  {
> >  	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
> > @@ -323,10 +364,25 @@ static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
> >  	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
> >  };
> >  
> > +static const struct sdhci_cdns_of_data sdhci_cdns_uniphier_of_data = {
> > +	.pltfm_data = &sdhci_cdns_uniphier_pltfm_data,
> > +};
> > +
> >  static const struct sdhci_pltfm_data sdhci_cdns_sd4_pltfm_data = {
> >  	.ops = &sdhci_cdns_sd4_ops,
> >  };
> >  
> > +static const struct sdhci_cdns_data sdhci_cdns_sd4_data = {
> > +	.phy_init = sdhci_cdns_sd4_phy_init,
> > +	.set_tune_val = sdhci_cdns_sd4_set_tune_val,
> > +};
> > +
> > +static const struct sdhci_cdns_of_data sdhci_cdns_sd4_of_data = {
> > +	.pltfm_data = &sdhci_cdns_sd4_pltfm_data,
> > +	.cdns_data = &sdhci_cdns_sd4_data,
> > +	.phy_probe = sdhci_cdns_sd4_phy_probe,
> > +};
> > +
> >  static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
> >  					     struct mmc_ios *ios)
> >  {
> > @@ -350,11 +406,10 @@ static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
> >  static int sdhci_cdns_probe(struct platform_device *pdev)
> >  {
> >  	struct sdhci_host *host;
> > -	const struct sdhci_pltfm_data *data;
> > +	const struct sdhci_cdns_of_data *data;
> >  	struct sdhci_pltfm_host *pltfm_host;
> >  	struct sdhci_cdns_priv *priv;
> >  	struct clk *clk;
> > -	unsigned int nr_phy_params;
> >  	int ret;
> >  	struct device *dev = &pdev->dev;
> >  	static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
> > @@ -368,12 +423,12 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
> >  		return ret;
> >  
> >  	data = of_device_get_match_data(dev);
> > -	if (!data)
> > -		data = &sdhci_cdns_sd4_pltfm_data;
> > +	if (!data) {
> > +		ret = -EINVAL;
> > +		goto disable_clk;
> > +	}
> >  
> > -	nr_phy_params = sdhci_cdns_sd4_phy_param_count(dev->of_node);
> > -	host = sdhci_pltfm_init(pdev, data,
> > -				struct_size(priv, phy_params, nr_phy_params));
> > +	host = sdhci_pltfm_init(pdev, data->pltfm_data, sizeof(*priv));
> >  	if (IS_ERR(host)) {
> >  		ret = PTR_ERR(host);
> >  		goto disable_clk;
> > @@ -383,9 +438,9 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
> >  	pltfm_host->clk = clk;
> >  
> >  	priv = sdhci_pltfm_priv(pltfm_host);
> > -	priv->nr_phy_params = nr_phy_params;
> >  	priv->hrs_addr = host->ioaddr;
> >  	priv->enhanced_strobe = false;
> > +	priv->cdns_data = data->cdns_data;
> >  	host->ioaddr += SDHCI_CDNS_SRS_BASE;
> >  	host->mmc_host_ops.hs400_enhanced_strobe =
> >  				sdhci_cdns_hs400_enhanced_strobe;
> > @@ -398,9 +453,11 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
> >  	if (ret)
> >  		goto free;
> >  
> > -	sdhci_cdns_sd4_phy_param_parse(dev->of_node, priv);
> > +	ret = data->phy_probe(pdev, priv);
> > +	if (ret)
> > +		goto free;
> >  
> > -	ret = sdhci_cdns_sd4_phy_init(priv);
> > +	ret = priv->cdns_data->phy_init(priv);
> 
> As was pointed out last time, you are dereferncing cdns_data unconditionally
> when it could be NULL e.g. in the case of sdhci_cdns_uniphier_of_data
> 
> >  	if (ret)
> >  		goto free;
> >  
> > @@ -429,7 +486,7 @@ static int sdhci_cdns_resume(struct device *dev)
> >  	if (ret)
> >  		return ret;
> >  
> > -	ret = sdhci_cdns_sd4_phy_init(priv);
> > +	ret = priv->cdns_data->phy_init(priv);
> >  	if (ret)
> >  		goto disable_clk;
> >  
> > @@ -453,9 +510,12 @@ static const struct dev_pm_ops sdhci_cdns_pm_ops = {
> >  static const struct of_device_id sdhci_cdns_match[] = {
> >  	{
> >  		.compatible = "socionext,uniphier-sd4hc",
> > -		.data = &sdhci_cdns_uniphier_pltfm_data,
> > +		.data = &sdhci_cdns_uniphier_of_data,
> > +	},
> > +	{
> > +		.compatible = "cdns,sd4hc",
> > +		.data = &sdhci_cdns_sd4_of_data,
> >  	},
> > -	{ .compatible = "cdns,sd4hc" },
> >  	{ /* sentinel */ }
> >  };
> >  MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
> 

Rest of the comments will take care in next version.

Thanks,
Piyush

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-03-24 15:20 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-27 18:31 [PATCH v3 0/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
2023-02-27 18:31 ` [PATCH v3 1/6] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
2023-02-27 18:31 ` [PATCH v3 2/6] mmc: sdhci-cadence: Restructure the code Piyush Malgujar
2023-03-03  8:36   ` Adrian Hunter
2023-03-24 15:19     ` Piyush Malgujar
2023-02-27 18:31 ` [PATCH v3 3/6] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
2023-03-03  8:48   ` Adrian Hunter
2023-02-27 18:31 ` [PATCH v3 4/6] mmc: sdhci-cadence: enable MMC_SDHCI_IO_ACCESSORS Piyush Malgujar
2023-02-27 18:31 ` [PATCH v3 5/6] dt-bindings: mmc: sdhci-cadence: SD6 support Piyush Malgujar
2023-02-28 10:53   ` Krzysztof Kozlowski
2023-03-24 15:02     ` Piyush Malgujar
2023-02-28 15:46   ` Rob Herring
2023-03-24 15:13     ` Piyush Malgujar
2023-02-27 18:31 ` [PATCH v3 6/6] mmc: sdhci-cadence: Add debug option for sdhci-cadence driver Piyush Malgujar
2023-03-03  8:36   ` Adrian Hunter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.