/* Register base for eMMC PHY 5.0 Version */ #define XENON_EMMC_5_0_PHY_REG_BASE 0x0160 /* Register base for eMMC PHY 5.1 Version */ #define XENON_EMMC_PHY_REG_BASE 0x0170
#define XENON_EMMC_PHY_PAD_CONTROL2 (XENON_EMMC_PHY_REG_BASE + 0x10) #define XENON_EMMC_5_0_PHY_PAD_CONTROL2 \
(XENON_EMMC_5_0_PHY_REG_BASE + 0xC) #define XENON_ZNR_MASK 0x1F #define XENON_ZNR_SHIFT 8 #define XENON_ZPR_MASK 0x1F /* Preferred ZNR and ZPR value vary between different boards. * The specific ZNR and ZPR value should be defined here * according to board actual timing.
*/ #define XENON_ZNR_DEF_VALUE 0xF #define XENON_ZPR_DEF_VALUE 0xF
/* * List offset of PHY registers and some special register values * in eMMC PHY 5.0 or eMMC PHY 5.1
*/ struct xenon_emmc_phy_regs { /* Offset of Timing Adjust register */
u16 timing_adj; /* Offset of Func Control register */
u16 func_ctrl; /* Offset of Pad Control register */
u16 pad_ctrl; /* Offset of Pad Control register 2 */
u16 pad_ctrl2; /* Offset of DLL Control register */
u16 dll_ctrl; /* Offset of Logic Timing Adjust register */
u16 logic_timing_adj; /* DLL Update Enable bit */
u32 dll_update; /* value in Logic Timing Adjustment register */
u32 logic_timing_val;
};
/* Add duration of FC_SYNC_RST */
wait = ((reg >> XENON_FC_SYNC_RST_DURATION_SHIFT) &
XENON_FC_SYNC_RST_DURATION_MASK); /* Add interval between FC_SYNC_EN and FC_SYNC_RST */
wait += ((reg >> XENON_FC_SYNC_RST_EN_DURATION_SHIFT) &
XENON_FC_SYNC_RST_EN_DURATION_MASK); /* Add duration of asserting FC_SYNC_EN */
wait += ((reg >> XENON_FC_SYNC_EN_DURATION_SHIFT) &
XENON_FC_SYNC_EN_DURATION_MASK); /* Add duration of waiting for PHY */
wait += ((reg >> XENON_WAIT_CYCLE_BEFORE_USING_SHIFT) &
XENON_WAIT_CYCLE_BEFORE_USING_MASK); /* 4 additional bus clock and 4 AXI bus clock are required */
wait += 8;
wait <<= 20;
clock = host->clock; if (!clock) /* Use the possibly slowest bus frequency value */
clock = XENON_LOWEST_SDCLK_FREQ; /* get the wait time */
wait /= clock;
wait++;
/* * AC5X spec says bit must be polled until zero. * We see cases in which timeout can take longer * than the standard calculation on AC5X, which is * expected following the spec comment above. * According to the spec, we must wait as long as * it takes for that bit to toggle on AC5X. * Cap that with 100 delay loops so we won't get * stuck here forever:
*/
ret = read_poll_timeout(sdhci_readl, reg,
!(reg & XENON_PHY_INITIALIZAION),
wait, XENON_MAX_PHY_TIMEOUT_LOOPS * wait, false, host, phy_regs->timing_adj); if (ret)
dev_err(mmc_dev(host->mmc), "eMMC PHY init cannot complete after %d us\n",
wait * XENON_MAX_PHY_TIMEOUT_LOOPS);
/* * Set SoC PHY voltage PAD control register, * according to the operation voltage on PAD. * The detailed operation depends on SoC implementation.
*/ staticvoid xenon_emmc_phy_set_soc_pad(struct sdhci_host *host, unsignedchar signal_voltage)
{ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); struct xenon_emmc_phy_params *params = priv->phy_params;
if (!params->pad_ctrl.reg) return;
if (params->pad_ctrl.set_soc_pad)
params->pad_ctrl.set_soc_pad(host, signal_voltage);
}
/* * Enable eMMC PHY HW DLL * DLL should be enabled and stable before HS200/SDR104 tuning, * and before HS400 data strobe setting.
*/ staticint xenon_emmc_phy_enable_dll(struct sdhci_host *host)
{
u32 reg; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
ktime_t timeout;
if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR)) return -EINVAL;
reg = sdhci_readl(host, phy_regs->dll_ctrl); if (reg & XENON_DLL_ENABLE) return 0;
/* * Set Phase as 90 degree, which is most common value. * Might set another value if necessary. * The granularity is 1 degree.
*/
reg &= ~((XENON_DLL_PHASE_MASK << XENON_DLL_PHSEL0_SHIFT) |
(XENON_DLL_PHASE_MASK << XENON_DLL_PHSEL1_SHIFT));
reg |= ((XENON_DLL_PHASE_90_DEGREE << XENON_DLL_PHSEL0_SHIFT) |
(XENON_DLL_PHASE_90_DEGREE << XENON_DLL_PHSEL1_SHIFT));
/* Enable SDHC Data Strobe */
reg = sdhci_readl(host, XENON_SLOT_EMMC_CTRL);
reg |= XENON_ENABLE_DATA_STROBE; /* * Enable SDHC Enhanced Strobe if supported * Xenon Enhanced Strobe should be enabled only when * 1. card is in HS400 mode and * 2. SDCLK is higher than 52MHz * 3. DLL is enabled
*/ if (host->mmc->ios.enhanced_strobe)
reg |= XENON_ENABLE_RESP_STROBE;
sdhci_writel(host, reg, XENON_SLOT_EMMC_CTRL);
/* * If eMMC PHY Slow Mode is required in lower speed mode (SDCLK < 55MHz) * in SDR mode, enable Slow Mode to bypass eMMC PHY. * SDIO slower SDR mode also requires Slow Mode. * * If Slow Mode is enabled, return true. * Otherwise, return false.
*/ staticbool xenon_emmc_phy_slow_mode(struct sdhci_host *host, unsignedchar timing)
{ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); struct xenon_emmc_phy_params *params = priv->phy_params; struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
u32 reg; int ret;
if (host->clock > MMC_HIGH_52_MAX_DTR) returnfalse;
reg = sdhci_readl(host, phy_regs->timing_adj); /* When in slower SDR mode, enable Slow Mode for SDIO * or when Slow Mode flag is set
*/ switch (timing) { case MMC_TIMING_LEGACY: /* * If Slow Mode is required, enable Slow Mode by default * in early init phase to avoid any potential issue.
*/ if (params->slow_mode) {
reg |= XENON_TIMING_ADJUST_SLOW_MODE;
ret = true;
} else {
reg &= ~XENON_TIMING_ADJUST_SLOW_MODE;
ret = false;
} break; case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_SDR12: case MMC_TIMING_SD_HS: case MMC_TIMING_MMC_HS: if ((priv->init_card_type == MMC_TYPE_SDIO) ||
params->slow_mode) {
reg |= XENON_TIMING_ADJUST_SLOW_MODE;
ret = true; break;
}
fallthrough; default:
reg &= ~XENON_TIMING_ADJUST_SLOW_MODE;
ret = false;
}
/* Setup pad, set bit[28] and bits[26:24] */
reg = sdhci_readl(host, phy_regs->pad_ctrl);
reg |= (XENON_FC_DQ_RECEN | XENON_FC_CMD_RECEN |
XENON_FC_QSP_RECEN | XENON_OEN_QSN); /* All FC_XX_RECEIVCE should be set as CMOS Type */
reg |= XENON_FC_ALL_CMOS_RECEIVER;
sdhci_writel(host, reg, phy_regs->pad_ctrl);
if (xenon_emmc_phy_slow_mode(host, timing)) goto phy_init;
/* * Set preferred ZNR and ZPR value * The ZNR and ZPR value vary between different boards. * Define them both in sdhci-xenon-emmc-phy.h.
*/
reg = sdhci_readl(host, phy_regs->pad_ctrl2);
reg &= ~((XENON_ZNR_MASK << XENON_ZNR_SHIFT) | XENON_ZPR_MASK);
reg |= ((params->znr << XENON_ZNR_SHIFT) | params->zpr);
sdhci_writel(host, reg, phy_regs->pad_ctrl2);
/* * When setting EMMC_PHY_FUNC_CONTROL register, * SD clock should be disabled
*/
reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
reg &= ~SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
if (timing == MMC_TIMING_MMC_HS400) /* Hardware team recommend a value for HS400 */
sdhci_writel(host, phy_regs->logic_timing_val,
phy_regs->logic_timing_adj); else
xenon_emmc_phy_disable_strobe(host);
if (priv->hw_version == XENON_A3700)
params->pad_ctrl.set_soc_pad = armada_3700_soc_pad_voltage_set; else return 0;
if (of_address_to_resource(np, 1, &iomem)) {
dev_err(mmc_dev(host->mmc), "Unable to find SoC PAD ctrl register address for %pOFn\n",
np); return -EINVAL;
}
params->pad_ctrl.reg = devm_ioremap_resource(mmc_dev(host->mmc),
&iomem); if (IS_ERR(params->pad_ctrl.reg)) return PTR_ERR(params->pad_ctrl.reg);
ret = of_property_read_string(np, "marvell,pad-type", &name); if (ret) {
dev_err(mmc_dev(host->mmc), "Unable to determine SoC PHY PAD ctrl type\n"); return ret;
} if (!strcmp(name, "sd")) {
params->pad_ctrl.pad_type = SOC_PAD_SD;
} elseif (!strcmp(name, "fixed-1-8v")) {
params->pad_ctrl.pad_type = SOC_PAD_FIXED_1_8V;
} else {
dev_err(mmc_dev(host->mmc), "Unsupported SoC PHY PAD ctrl type %s\n",
name); return -EINVAL;
}
params->slow_mode = false; if (device_property_read_bool(dev, "marvell,xenon-phy-slow-mode"))
params->slow_mode = true;
params->znr = XENON_ZNR_DEF_VALUE; if (!device_property_read_u32(dev, "marvell,xenon-phy-znr", &value))
params->znr = value & XENON_ZNR_MASK;
params->zpr = XENON_ZPR_DEF_VALUE; if (!device_property_read_u32(dev, "marvell,xenon-phy-zpr", &value))
params->zpr = value & XENON_ZPR_MASK;
params->nr_tun_times = XENON_TUN_CONSECUTIVE_TIMES; if (!device_property_read_u32(dev, "marvell,xenon-phy-nr-success-tun",
&value))
params->nr_tun_times = value & XENON_TUN_CONSECUTIVE_TIMES_MASK;
params->tun_step_divider = XENON_TUNING_STEP_DIVIDER; if (!device_property_read_u32(dev, "marvell,xenon-phy-tun-step-divider",
&value))
params->tun_step_divider = value & 0xFF;
if (dev->of_node) return get_dt_pad_ctrl_data(host, dev->of_node, params); return 0;
}
/* Set SoC PHY Voltage PAD */ void xenon_soc_pad_ctrl(struct sdhci_host *host, unsignedchar signal_voltage)
{
xenon_emmc_phy_set_soc_pad(host, signal_voltage);
}
/* * Setting PHY when card is working in High Speed Mode. * HS400 set Data Strobe and Enhanced Strobe if it is supported. * HS200/SDR104 set tuning config to prepare for tuning.
*/ staticint xenon_hs_delay_adj(struct sdhci_host *host)
{ int ret = 0;
if (WARN_ON(host->clock <= XENON_DEFAULT_SDCLK_FREQ)) return -EINVAL;
switch (host->timing) { case MMC_TIMING_MMC_HS400:
xenon_emmc_phy_strobe_delay_adj(host); return 0; case MMC_TIMING_MMC_HS200: case MMC_TIMING_UHS_SDR104: return xenon_emmc_phy_config_tuning(host); case MMC_TIMING_MMC_DDR52: case MMC_TIMING_UHS_DDR50: /* * DDR Mode requires driver to scan Sampling Fixed Delay Line, * to find out a perfect operation sampling point. * It is hard to implement such a scan in host driver * since initiating commands by host driver is not safe. * Thus so far just keep PHY Sampling Fixed Delay in * default value of DDR mode. * * If any timing issue occurs in DDR mode on Marvell products, * please contact maintainer for internal support in Marvell.
*/
dev_warn_once(mmc_dev(host->mmc), "Timing issue might occur in DDR mode\n"); return 0;
}
return ret;
}
/* * Adjust PHY setting. * PHY setting should be adjusted when SDCLK frequency, Bus Width * or Speed Mode is changed. * Additional config are required when card is working in High Speed mode, * after leaving Legacy Mode.
*/ int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios)
{ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); int ret = 0;
if (!host->clock) {
priv->clock = 0; return 0;
}
/* * The timing, frequency or bus width is changed, * better to set eMMC PHY based on current setting * and adjust Xenon SDHC delay.
*/ if ((host->clock == priv->clock) &&
(ios->bus_width == priv->bus_width) &&
(ios->timing == priv->timing)) return 0;
xenon_emmc_phy_set(host, ios->timing);
/* Update the record */
priv->bus_width = ios->bus_width;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.