mmc: am654_sdhci: Add Support for configuring PHY in J721e

Add Support for writing to PHY registers for J721e. There are number of
differences between the J721e 8 bit PHY, J721e 4 bit PHY and AM654 PHY.
Create a driver_data structure with an ops and flags field and use the
flags field to indicate these differences. The differences are as
follows:

1. The J721e 4 bit instance PHY does not have a DLL. Introduce a
DLL_PRESENT flag to make sure that DLL related registers are accessed
only where they are present. Also add a separate set_ios_post()
callback.

2. The J721e 8 bit instance is not muxed with anything else inside the
SoC and hence the IOMUX_ENABLE filed does not exist. Add a flag which is
used to indicate the presence of this field.

3. The register field used to select DLL frequency is 3 bit wide in
J721e as compared to 2 bits in AM65x. Add another flag that
distinguishes these fields.

4. The strobe select field is 8 bit wide as compared to 4 bit wide for
AM65x. Add yet another flag to indicate this difference. Strobe select
is used only for HS400 speed mode, support for which has not been added
in AM65x.

Signed-off-by: Faiz Abbas <faiz_abbas@ti.com>
Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index a4359fc..41a9083 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -36,11 +36,14 @@
 #define OTAPDLYSEL_SHIFT	12
 #define OTAPDLYSEL_MASK		GENMASK(15, 12)
 #define STRBSEL_SHIFT		24
-#define STRBSEL_MASK		GENMASK(27, 24)
+#define STRBSEL_4BIT_MASK	GENMASK(27, 24)
+#define STRBSEL_8BIT_MASK	GENMASK(31, 24)
 #define SEL50_SHIFT		8
 #define SEL50_MASK		BIT(SEL50_SHIFT)
 #define SEL100_SHIFT		9
 #define SEL100_MASK		BIT(SEL100_SHIFT)
+#define FREQSEL_SHIFT		8
+#define FREQSEL_MASK		GENMASK(10, 8)
 #define DLL_TRIM_ICP_SHIFT	4
 #define DLL_TRIM_ICP_MASK	GENMASK(7, 4)
 #define DR_TY_SHIFT		20
@@ -72,11 +75,20 @@
 	u32 otap_del_sel;
 	u32 trm_icp;
 	u32 drv_strength;
+	u32 strb_sel;
 	u32 flags;
 #define DLL_PRESENT	(1 << 0)
+#define IOMUX_PRESENT	(1 << 1)
+#define FREQSEL_2_BIT	(1 << 2)
+#define STRBSEL_4_BIT	(1 << 3)
 	bool dll_on;
 };
 
+struct am654_driver_data {
+	const struct sdhci_ops *ops;
+	u32 flags;
+};
+
 static void am654_sdhci_set_control_reg(struct sdhci_host *host)
 {
 	struct mmc *mmc = (struct mmc *)host->mmc;
@@ -97,7 +109,7 @@
 	struct udevice *dev = host->mmc->dev;
 	struct am654_sdhci_plat *plat = dev_get_platdata(dev);
 	unsigned int speed = host->mmc->clock;
-	int sel50, sel100;
+	int sel50, sel100, freqsel;
 	u32 mask, val;
 	int ret;
 
@@ -121,25 +133,49 @@
 		mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
 		val = (1 << OTAPDLYENA_SHIFT) |
 		      (plat->otap_del_sel << OTAPDLYSEL_SHIFT);
-		regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
-		switch (speed) {
-		case 200000000:
-			sel50 = 0;
-			sel100 = 0;
-			break;
-		case 100000000:
-			sel50 = 0;
-			sel100 = 1;
-			break;
-		default:
-			sel50 = 1;
-			sel100 = 0;
+
+		/* Write to STRBSEL for HS400 speed mode */
+		if (host->mmc->selected_mode == MMC_HS_400) {
+			if (plat->flags & STRBSEL_4_BIT)
+				mask |= STRBSEL_4BIT_MASK;
+			else
+				mask |= STRBSEL_8BIT_MASK;
+
+			val |= plat->strb_sel << STRBSEL_SHIFT;
 		}
 
-		/* Configure PHY DLL frequency */
-		mask = SEL50_MASK | SEL100_MASK;
-		val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
-		regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+		regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+		if (plat->flags & FREQSEL_2_BIT) {
+			switch (speed) {
+			case 200000000:
+				sel50 = 0;
+				sel100 = 0;
+				break;
+			case 100000000:
+				sel50 = 0;
+				sel100 = 1;
+				break;
+			default:
+				sel50 = 1;
+				sel100 = 0;
+			}
+
+			/* Configure PHY DLL frequency */
+			mask = SEL50_MASK | SEL100_MASK;
+			val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+			regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+		} else {
+			switch (speed) {
+			case 200000000:
+				freqsel = 0x0;
+				break;
+			default:
+				freqsel = 0x4;
+			}
+			regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
+					   freqsel << FREQSEL_SHIFT);
+		}
 
 		/* Enable DLL */
 		regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
@@ -164,8 +200,37 @@
 	.set_control_reg	= &am654_sdhci_set_control_reg,
 };
 
+const struct am654_driver_data am654_drv_data = {
+	.ops = &am654_sdhci_ops,
+	.flags = IOMUX_PRESENT | FREQSEL_2_BIT | DLL_PRESENT | STRBSEL_4_BIT,
+};
+
+const struct am654_driver_data j721e_8bit_drv_data = {
+	.ops = &am654_sdhci_ops,
+	.flags = DLL_PRESENT,
+};
+
+static int j721e_4bit_sdhci_set_ios_post(struct sdhci_host *host)
+{
+	struct udevice *dev = host->mmc->dev;
+	struct am654_sdhci_plat *plat = dev_get_platdata(dev);
+	u32 mask, val;
+
+	mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+	val = (1 << OTAPDLYENA_SHIFT) |
+	      (plat->otap_del_sel << OTAPDLYSEL_SHIFT);
+	regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+	return 0;
+}
+
 const struct sdhci_ops j721e_4bit_sdhci_ops = {
-	.set_control_reg	= &am654_sdhci_set_control_reg,
+	.set_ios_post		= &j721e_4bit_sdhci_set_ios_post,
+};
+
+const struct am654_driver_data j721e_4bit_drv_data = {
+	.ops = &j721e_4bit_sdhci_ops,
+	.flags = IOMUX_PRESENT,
 };
 
 int am654_sdhci_init(struct am654_sdhci_plat *plat)
@@ -202,7 +267,8 @@
 	}
 
 	/* Enable pins by setting IO mux to 0 */
-	regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0);
+	if (plat->flags & IOMUX_PRESENT)
+		regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0);
 
 	/* Set slot type based on SD or eMMC */
 	if (plat->non_removable)
@@ -215,6 +281,8 @@
 
 static int am654_sdhci_probe(struct udevice *dev)
 {
+	struct am654_driver_data *drv_data =
+			(struct am654_driver_data *)dev_get_driver_data(dev);
 	struct am654_sdhci_plat *plat = dev_get_platdata(dev);
 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 	struct sdhci_host *host = dev_get_priv(dev);
@@ -242,7 +310,8 @@
 			      AM654_SDHCI_MIN_FREQ);
 	if (ret)
 		return ret;
-	host->ops = (struct sdhci_ops *)dev_get_driver_data(dev);
+
+	host->ops = drv_data->ops;
 	host->mmc->priv = host;
 	upriv->mmc = host->mmc;
 
@@ -265,10 +334,6 @@
 	host->ioaddr = (void *)dev_read_addr(dev);
 	plat->non_removable = dev_read_bool(dev, "non-removable");
 
-	if (device_is_compatible(dev, "ti,am654-sdhci-5.1") ||
-	    device_is_compatible(dev, "ti,j721e-sdhci-8bit"))
-		plat->flags |= DLL_PRESENT;
-
 	ret = dev_read_u32(dev, "ti,otap-del-sel", &plat->otap_del_sel);
 	if (ret)
 		return ret;
@@ -314,23 +379,27 @@
 
 static int am654_sdhci_bind(struct udevice *dev)
 {
+	struct am654_driver_data *drv_data =
+			(struct am654_driver_data *)dev_get_driver_data(dev);
 	struct am654_sdhci_plat *plat = dev_get_platdata(dev);
 
+	plat->flags = drv_data->flags;
+
 	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
 }
 
 static const struct udevice_id am654_sdhci_ids[] = {
 	{
 		.compatible = "ti,am654-sdhci-5.1",
-		.data = (ulong)&am654_sdhci_ops,
+		.data = (ulong)&am654_drv_data,
 	},
 	{
 		.compatible = "ti,j721e-sdhci-8bit",
-		.data = (ulong)&am654_sdhci_ops,
+		.data = (ulong)&j721e_8bit_drv_data,
 	},
 	{
 		.compatible = "ti,j721e-sdhci-4bit",
-		.data = (ulong)&j721e_4bit_sdhci_ops,
+		.data = (ulong)&j721e_4bit_drv_data,
 	},
 	{ }
 };