diff mbox series

[v1,11/17] drivers: mmc: add mmc/cadence driver for agilex5

Message ID 20230621031610.28401-12-jit.loon.lim@intel.com
State Needs Review / ACK, archived
Delegated to: Marek Vasut
Headers show
Series Agilex5 Platform Enablement | expand

Commit Message

Jit Loon Lim June 21, 2023, 3:16 a.m. UTC
This is for new platform enablement for agilex5.
Add mmc and cadence host driver for new platform.

Signed-off-by: Jit Loon Lim <jit.loon.lim@intel.com>
---
 drivers/mmc/mmc.c           |  27 +++---
 drivers/mmc/sdhci-cadence.c | 164 ++++++++++++++++++++++++++++++++----
 2 files changed, 160 insertions(+), 31 deletions(-)
diff mbox series

Patch

diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 1af6af82e6..88c674d44f 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -26,6 +26,7 @@ 
 #include <div64.h>
 #include "mmc_private.h"
 
+#define TIMEOUT_TEN_MS  10
 #define DEFAULT_CMD6_TIMEOUT_MS  500
 
 static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage);
@@ -247,7 +248,7 @@  static int mmc_send_cmd_retry(struct mmc *mmc, struct mmc_cmd *cmd,
 static int mmc_send_cmd_quirks(struct mmc *mmc, struct mmc_cmd *cmd,
 			       struct mmc_data *data, u32 quirk, uint retries)
 {
-	if (IS_ENABLED(CONFIG_MMC_QUIRKS) && mmc->quirks & quirk)
+	if (CONFIG_IS_ENABLED(MMC_QUIRKS) && mmc->quirks & quirk)
 		return mmc_send_cmd_retry(mmc, cmd, data, retries);
 	else
 		return mmc_send_cmd(mmc, cmd, data);
@@ -597,6 +598,11 @@  static int sd_send_op_cond(struct mmc *mmc, bool uhs_en)
 	int err;
 	struct mmc_cmd cmd;
 
+	/* lower timeout, to speed up mmc init since both uses same flow */
+	if (IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5_EMU) ||
+	    IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5_SIMICS))
+		timeout = TIMEOUT_TEN_MS;
+
 	while (1) {
 		cmd.cmdidx = MMC_CMD_APP_CMD;
 		cmd.resp_type = MMC_RSP_R1;
@@ -635,7 +641,7 @@  static int sd_send_op_cond(struct mmc *mmc, bool uhs_en)
 			break;
 
 		if (timeout-- <= 0)
-			return -EOPNOTSUPP;
+			return -ETIMEDOUT;
 
 		udelay(1000);
 	}
@@ -2432,9 +2438,6 @@  static int mmc_startup_v4(struct mmc *mmc)
 
 	mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
 
-	mmc->can_trim =
-		!!(ext_csd[EXT_CSD_SEC_FEATURE] & EXT_CSD_SEC_FEATURE_TRIM_EN);
-
 	return 0;
 error:
 	if (mmc->ext_csd) {
@@ -3130,10 +3133,9 @@  int mmc_init_device(int num)
 #endif
 
 #ifdef CONFIG_CMD_BKOPS_ENABLE
-int mmc_set_bkops_enable(struct mmc *mmc, bool autobkops, bool enable)
+int mmc_set_bkops_enable(struct mmc *mmc)
 {
 	int err;
-	u32 bit = autobkops ? BIT(1) : BIT(0);
 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
 
 	err = mmc_send_ext_csd(mmc, ext_csd);
@@ -3147,21 +3149,18 @@  int mmc_set_bkops_enable(struct mmc *mmc, bool autobkops, bool enable)
 		return -EMEDIUMTYPE;
 	}
 
-	if (enable && (ext_csd[EXT_CSD_BKOPS_EN] & bit)) {
+	if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) {
 		puts("Background operations already enabled\n");
 		return 0;
 	}
 
-	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN,
-			 enable ? bit : 0);
+	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1);
 	if (err) {
-		printf("Failed to %sable manual background operations\n",
-		       enable ? "en" : "dis");
+		puts("Failed to enable manual background operations\n");
 		return err;
 	}
 
-	printf("%sabled %s background operations\n",
-	       enable ? "En" : "Dis", autobkops ? "auto" : "manual");
+	puts("Enabled manual background operations\n");
 
 	return 0;
 }
diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
index 327a05ad11..4154fd33da 100644
--- a/drivers/mmc/sdhci-cadence.c
+++ b/drivers/mmc/sdhci-cadence.c
@@ -5,7 +5,10 @@ 
  */
 
 #include <common.h>
+#include <clk.h>
 #include <dm.h>
+#include <generic-phy.h>
+#include <asm/arch/clock_manager.h>
 #include <asm/global_data.h>
 #include <dm/device_compat.h>
 #include <linux/bitfield.h>
@@ -16,10 +19,15 @@ 
 #include <linux/sizes.h>
 #include <linux/libfdt.h>
 #include <mmc.h>
+#include <reset-uclass.h>
 #include <sdhci.h>
 
+/* General define */
+#define SD_MIN_CLK 400000
+
 /* HRS - Host Register Set (specific to Cadence) */
 #define SDHCI_CDNS_HRS04		0x10		/* PHY access port */
+#define SDHCI_CDNS_HRS05		0x14		/* PHY data access port */
 #define   SDHCI_CDNS_HRS04_ACK			BIT(26)
 #define   SDHCI_CDNS_HRS04_RD			BIT(25)
 #define   SDHCI_CDNS_HRS04_WR			BIT(24)
@@ -66,6 +74,16 @@  struct sdhci_cdns_plat {
 	struct mmc_config cfg;
 	struct mmc mmc;
 	void __iomem *hrs_addr;
+	struct udevice *udev;
+	struct phy phy_dev;
+	bool phy_enabled;
+	struct reset_ctl softreset_ctl;
+};
+
+/* socfpga implementation specific driver private data */
+struct sdhci_socfpga_priv_data {
+	struct sdhci_host host;
+	struct phy phy;
 };
 
 struct sdhci_cdns_phy_cfg {
@@ -94,25 +112,45 @@  static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
 	u32 tmp;
 	int ret;
 
-	tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
-	      FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
-	writel(tmp, reg);
+	if (plat->phy_enabled) {
+		/* retrieve reg. addr */
+		tmp = FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
 
-	tmp |= SDHCI_CDNS_HRS04_WR;
-	writel(tmp, reg);
+		ret = writel(tmp, reg);
+		debug("%s: register = 0x%08x\n", __func__, readl(reg));
 
-	ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 10);
-	if (ret)
-		return ret;
+		/* read existing value, mask it */
+		reg = plat->hrs_addr + SDHCI_CDNS_HRS05;
+		tmp = readl(reg);
+		debug("%s: register = 0x%08x\n", __func__, readl(reg));
 
-	tmp &= ~SDHCI_CDNS_HRS04_WR;
-	writel(tmp, reg);
+		tmp &= ~data;
+		tmp |= data;
+
+		/* write operation */
+		ret = writel(tmp, reg);
+		debug("%s: register = 0x%08x\n", __func__, readl(reg));
+	} else {
+		tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
+			  FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
+		writel(tmp, reg);
+
+		tmp |= SDHCI_CDNS_HRS04_WR;
+		writel(tmp, reg);
+
+		ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 10);
+		if (ret)
+			return ret;
+
+		tmp &= ~SDHCI_CDNS_HRS04_WR;
+		writel(tmp, reg);
+	}
 
 	return 0;
 }
 
 static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat,
-				const void *fdt, int nodeoffset)
+			       const void *fdt, int nodeoffset)
 {
 	const fdt32_t *prop;
 	int ret, i;
@@ -126,6 +164,7 @@  static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat,
 		ret = sdhci_cdns_write_phy_reg(plat,
 					       sdhci_cdns_phy_cfgs[i].addr,
 					       fdt32_to_cpu(*prop));
+
 		if (ret)
 			return ret;
 	}
@@ -162,7 +201,14 @@  static void sdhci_cdns_set_control_reg(struct sdhci_host *host)
 	tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS06);
 	tmp &= ~SDHCI_CDNS_HRS06_MODE;
 	tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
+
 	writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06);
+	debug("%s: register = 0x%x\n", __func__,
+	      readl(plat->hrs_addr + SDHCI_CDNS_HRS06));
+
+	/* program phy based on generated settings, input through device tree */
+	if (plat->phy_enabled)
+		generic_phy_configure(&plat->phy_dev, NULL);
 }
 
 static const struct sdhci_ops sdhci_cdns_ops = {
@@ -192,6 +238,8 @@  static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
 		tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
 		writel(tmp, reg);
 
+		debug("%s: register = 0x%08x\n", __func__, readl(reg));
+
 		ret = readl_poll_timeout(reg, tmp,
 					 !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), 1);
 		if (ret)
@@ -252,15 +300,49 @@  static int sdhci_cdns_bind(struct udevice *dev)
 	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
 }
 
+static int socfpga_sdhci_get_clk_rate(struct udevice *dev)
+{
+	struct sdhci_socfpga_priv_data *priv = dev_get_priv(dev);
+	struct sdhci_host *host = &priv->host;
+
+#if (IS_ENABLED(CONFIG_CLK))
+	struct clk clk;
+	int ret;
+
+	ret = clk_get_by_index(dev, 1, &clk);
+	if (ret)
+		return ret;
+
+	host->max_clk = clk_get_rate(&clk);
+
+	clk_free(&clk);
+#else
+	/* Fixed clock divide by 4 which due to the SDMMC wrapper */
+	host->max_clk = cm_get_mmc_controller_clk_hz();
+#endif
+
+	if (!host->max_clk) {
+		debug("SDHCI: MMC clock is zero!");
+		return -EINVAL;
+	}
+	debug("max_clk: %d\n", host->max_clk);
+
+	return 0;
+}
+
 static int sdhci_cdns_probe(struct udevice *dev)
 {
 	DECLARE_GLOBAL_DATA_PTR;
 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 	struct sdhci_cdns_plat *plat = dev_get_plat(dev);
-	struct sdhci_host *host = dev_get_priv(dev);
+	struct sdhci_socfpga_priv_data *priv = dev_get_priv(dev);
+	struct sdhci_host *host = &priv->host;
+	const char *phy_name = dev_read_string(dev, "phy-names");
 	fdt_addr_t base;
 	int ret;
 
+	plat->phy_enabled = false;
+
 	base = dev_read_addr(dev);
 	if (base == FDT_ADDR_T_NONE)
 		return -EINVAL;
@@ -269,6 +351,43 @@  static int sdhci_cdns_probe(struct udevice *dev)
 	if (!plat->hrs_addr)
 		return -ENOMEM;
 
+	if (!phy_name)
+		return -EINVAL;
+
+	/* get SDMMC softreset */
+	ret = reset_get_by_name(dev, "reset", &plat->softreset_ctl);
+	if (ret)
+		pr_err("can't get soft reset for %s (%d)", dev->name, ret);
+
+	/* assert & deassert softreset */
+	ret = reset_assert(&plat->softreset_ctl);
+	if (ret < 0) {
+		pr_err("SDMMC soft reset deassert failed: %d", ret);
+		return ret;
+	}
+
+	ret = reset_deassert(&plat->softreset_ctl);
+	if (ret < 0) {
+		pr_err("SDMMC soft reset deassert failed: %d", ret);
+		return ret;
+	}
+
+	/* probe ComboPHY */
+	ret = generic_phy_get_by_name(dev, "combo-phy", &plat->phy_dev);
+	if (ret) {
+		printf("ComboPHY probe failed: %d\n", ret);
+		return ret;
+	}
+	debug("ComboPHY probe success\n");
+
+	ret = generic_phy_init(&plat->phy_dev);
+	if (ret) {
+		printf("ComboPHY init failed: %d\n", ret);
+		return ret;
+	}
+	debug("ComboPHY init success\n");
+
+	plat->phy_enabled = true;
 	host->name = dev->name;
 	host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE;
 	host->ops = &sdhci_cdns_ops;
@@ -282,18 +401,29 @@  static int sdhci_cdns_probe(struct udevice *dev)
 	if (ret)
 		return ret;
 
-	ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
+	/* get max clk */
+	ret = socfpga_sdhci_get_clk_rate(dev);
 	if (ret)
 		return ret;
 
-	host->mmc = &plat->mmc;
-	host->mmc->dev = dev;
-	ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
+	ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
 	if (ret)
 		return ret;
 
+	host->mmc = &plat->mmc;
 	upriv->mmc = &plat->mmc;
 	host->mmc->priv = host;
+	host->mmc->dev = dev;
+
+#if (IS_ENABLED(CONFIG_BLK))
+	ret = sdhci_setup_cfg(&plat->cfg, host, host->max_clk, SD_MIN_CLK);
+	if (ret)
+		return ret;
+#else
+	ret = add_sdhci(host, host->max_clk, SD_MIN_CLK);
+	if (ret)
+		return ret;
+#endif
 
 	return sdhci_probe(dev);
 }
@@ -310,7 +440,7 @@  U_BOOT_DRIVER(sdhci_cdns) = {
 	.of_match = sdhci_cdns_match,
 	.bind = sdhci_cdns_bind,
 	.probe = sdhci_cdns_probe,
-	.priv_auto	= sizeof(struct sdhci_host),
+	.priv_auto	= sizeof(struct sdhci_socfpga_priv_data),
 	.plat_auto	= sizeof(struct sdhci_cdns_plat),
 	.ops = &sdhci_cdns_mmc_ops,
 };