
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 --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, };